Skip to content

Commit 2d785de

Browse files
committed
Fix some tests
1 parent 21fcbd2 commit 2d785de

File tree

3 files changed

+109
-17
lines changed

3 files changed

+109
-17
lines changed

asv_bench/benchmarks/replace.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@ def setup(self, inplace):
1414
data = np.random.randn(N)
1515
data[::2] = np.nan
1616
self.ts = pd.Series(data, index=rng)
17+
self.df = pd.DataFrame(np.random.randn(10 ** 3, 10 ** 3))
1718

1819
def time_fillna(self, inplace):
1920
self.ts.fillna(0.0, inplace=inplace)
2021

22+
def time_fillna_limit(self, inplace):
23+
self.ts.fillna(0.0, inplace=inplace, limit=10 ** 5)
24+
25+
def time_fillna_df(self, inplace):
26+
self.df.fillna(0.0, inplace=inplace)
27+
2128
def time_replace(self, inplace):
2229
self.ts.replace(np.nan, 0.0, inplace=inplace)
2330

pandas/_libs/algos.pyx

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ ctypedef fused fillna_t:
856856
@cython.wraparound(False)
857857
def fillna1d(fillna_t[:] arr,
858858
fillna_t value,
859-
limit=None,
859+
Py_ssize_t limit,
860860
bint inf_as_na=False
861861
) -> ndarray:
862862
"""
@@ -889,11 +889,6 @@ def fillna1d(fillna_t[:] arr,
889889
assert arr.ndim == 1, "'arr' must be 1-D."
890890

891891
N = len(arr)
892-
lim = validate_limit(N, limit)
893-
# if inf_as_na:
894-
# check_func = checknull_old
895-
# else:
896-
# check_func = checknull
897892
for i in range(N):
898893
val = arr[i]
899894
if fillna_t is object:
@@ -907,19 +902,82 @@ def fillna1d(fillna_t[:] arr,
907902
result = result and (val == INF or val == NEGINF)
908903
else:
909904
result = val == NPY_NAT
910-
if result and count < lim:
905+
if result and count < limit:
911906
arr[i] = value
912907
count+=1
913-
# if check_func(val) and count < lim:
914-
# arr[i] = value
915-
# count+=1
908+
909+
910+
@cython.boundscheck(False)
911+
@cython.wraparound(False)
912+
def fillna1d_multi_values(fillna_t[:] arr,
913+
algos_t[:] value,
914+
Py_ssize_t limit,
915+
bint inf_as_na=False
916+
) -> ndarray:
917+
"""
918+
Fills na-like elements inplace for a 1D array
919+
according to the criteria defined in `checknull`:
920+
- None
921+
- nan
922+
- NaT
923+
- np.datetime64 representation of NaT
924+
- np.timedelta64 representation of NaT
925+
- NA
926+
- Decimal("NaN")
927+
928+
Parameters
929+
----------
930+
arr : ndarray
931+
value : ndarray/ExtensionArray
932+
A ndarray/ExtensionArray with same length as arr
933+
describing which fill value to use at each position,
934+
with a value of np.nan indicating that a position should
935+
not be filled
936+
limit : int, default None
937+
The number of elements to fill. If None, fills all NaN values
938+
inf_as_na:
939+
Whether to consider INF and NEGINF as NA
940+
"""
941+
cdef:
942+
Py_ssize_t i, N
943+
Py_ssize_t count=0
944+
fillna_t val
945+
algos_t fill_value
946+
bint result
947+
948+
assert arr.ndim == 1, "'arr' must be 1-D."
949+
950+
N = len(arr)
951+
for i in range(N):
952+
fill_value = value[i]
953+
if fill_value != fill_value:
954+
# np.nan don't fill
955+
continue
956+
val = arr[i]
957+
if fillna_t is object:
958+
if inf_as_na:
959+
result = checknull_old(val)
960+
else:
961+
result = checknull(val)
962+
elif fillna_t is float32_t or fillna_t is float64_t:
963+
result = val != val
964+
if inf_as_na:
965+
result = result and (val == INF or val == NEGINF)
966+
else:
967+
result = val == NPY_NAT
968+
if result and count < limit:
969+
# Ugh... We have to cast here since technically could have a int64->float32
970+
# There shouldn't be any risk here since BlockManager should check
971+
# that the element can be held
972+
arr[i] = <fillna_t>fill_value
973+
count+=1
916974

917975

918976
@cython.boundscheck(False)
919977
@cython.wraparound(False)
920978
def fillna2d(fillna_t[:, :] arr,
921-
object value,
922-
limit=None,
979+
fillna_t value,
980+
Py_ssize_t limit,
923981
bint inf_as_na=False
924982
) -> ndarray:
925983
"""
@@ -944,14 +1002,14 @@ def fillna2d(fillna_t[:, :] arr,
9441002
Whether to consider INF and NEGINF as NA
9451003
"""
9461004
cdef:
947-
Py_ssize_t i, j, n, m, lim
1005+
Py_ssize_t i, j, n, m
9481006
Py_ssize_t count=0
9491007
fillna_t val
1008+
bint result
9501009

9511010
assert arr.ndim == 2, "'arr' must be 2-D."
9521011

9531012
n, m = (<object>arr).shape
954-
lim = validate_limit(m, limit)
9551013
if inf_as_na:
9561014
check_func = checknull_old
9571015
else:
@@ -960,7 +1018,18 @@ def fillna2d(fillna_t[:, :] arr,
9601018
count = 0 # Limit is per axis
9611019
for j in range(m):
9621020
val = arr[i, j]
963-
if check_func(val) and count < lim:
1021+
if fillna_t is object:
1022+
if inf_as_na:
1023+
result = checknull_old(val)
1024+
else:
1025+
result = checknull(val)
1026+
elif fillna_t is float32_t or fillna_t is float64_t:
1027+
result = val != val
1028+
if inf_as_na:
1029+
result = result and (val == INF or val == NEGINF)
1030+
else:
1031+
result = val == NPY_NAT
1032+
if result and count < limit:
9641033
arr[i, j] = value
9651034
count+=1
9661035

pandas/core/internals/blocks.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -434,17 +434,33 @@ def fillna(
434434
fillna on the block with the value. If we fail, then convert to
435435
ObjectBlock and try again
436436
"""
437+
# TODO: Handle inf_as_na, we need to get option and pass to cython funcs
437438
inplace = validate_bool_kwarg(inplace, "inplace")
438439
arr = self if inplace else self.copy()
439-
limit = libalgos.validate_limit(None, limit=limit)
440+
limit = libalgos.validate_limit(
441+
len(self) if self.ndim == 1 else self.shape[1], limit=limit
442+
)
440443

441444
if not self._can_hold_na:
442445
return [arr]
443446

444447
if not self.is_extension:
445448
if self._can_hold_element(value):
446449
if self.ndim == 1:
447-
libalgos.fillna1d(arr.values, value=value, limit=limit)
450+
if is_list_like(value):
451+
# TODO: Verify EA case
452+
if is_extension_array_dtype(value):
453+
mask = value.isna()
454+
value = np.asarray(value[mask], dtype=object)
455+
libalgos.fillna1d_multi_values(
456+
arr.values[mask], value=value, limit=limit
457+
)
458+
else:
459+
libalgos.fillna1d_multi_values(
460+
arr.values, value=value, limit=limit
461+
)
462+
else:
463+
libalgos.fillna1d(arr.values, value=value, limit=limit)
448464
else:
449465
libalgos.fillna2d(arr.values, value=value, limit=limit)
450466
return arr._maybe_downcast([arr], downcast)

0 commit comments

Comments
 (0)