@@ -1019,14 +1019,16 @@ def putmask(self, mask, new) -> list[Block]:
1019
1019
res_blocks .extend (rbs )
1020
1020
return res_blocks
1021
1021
1022
- def where (self , other , cond ) -> list [Block ]:
1022
+ def where (self , other , cond , _downcast = "infer" ) -> list [Block ]:
1023
1023
"""
1024
1024
evaluate the block; return result block(s) from the result
1025
1025
1026
1026
Parameters
1027
1027
----------
1028
1028
other : a ndarray/object
1029
1029
cond : np.ndarray[bool], SparseArray[bool], or BooleanArray
1030
+ _downcast : str or None, default "infer"
1031
+ Private because we only specify it when calling from fillna.
1030
1032
1031
1033
Returns
1032
1034
-------
@@ -1066,7 +1068,7 @@ def where(self, other, cond) -> list[Block]:
1066
1068
1067
1069
block = self .coerce_to_target_dtype (other )
1068
1070
blocks = block .where (orig_other , cond )
1069
- return self ._maybe_downcast (blocks , "infer" )
1071
+ return self ._maybe_downcast (blocks , downcast = _downcast )
1070
1072
1071
1073
else :
1072
1074
# since _maybe_downcast would split blocks anyway, we
@@ -1083,7 +1085,7 @@ def where(self, other, cond) -> list[Block]:
1083
1085
oth = other [:, i : i + 1 ]
1084
1086
1085
1087
submask = cond [:, i : i + 1 ]
1086
- rbs = nb .where (oth , submask )
1088
+ rbs = nb .where (oth , submask , _downcast = _downcast )
1087
1089
res_blocks .extend (rbs )
1088
1090
return res_blocks
1089
1091
@@ -1158,21 +1160,19 @@ def fillna(
1158
1160
if limit is not None :
1159
1161
mask [mask .cumsum (self .ndim - 1 ) > limit ] = False
1160
1162
1161
- if self ._can_hold_element (value ):
1162
- nb = self if inplace else self .copy ()
1163
- putmask_inplace (nb .values , mask , value )
1164
- return nb ._maybe_downcast ([nb ], downcast )
1165
-
1166
- elif self .ndim == 1 or self .shape [0 ] == 1 :
1167
- blk = self .coerce_to_target_dtype (value )
1168
- # bc we have already cast, inplace=True may avoid an extra copy
1169
- return blk .fillna (value , limit = limit , inplace = True , downcast = None )
1170
-
1163
+ if inplace :
1164
+ nbs = self .putmask (mask .T , value )
1171
1165
else :
1172
- # operate column-by-column
1173
- return self .split_and_operate (
1174
- type (self ).fillna , value , limit = limit , inplace = inplace , downcast = None
1175
- )
1166
+ # without _downcast, we would break
1167
+ # test_fillna_dtype_conversion_equiv_replace
1168
+ nbs = self .where (value , ~ mask .T , _downcast = False )
1169
+
1170
+ # Note: blk._maybe_downcast vs self._maybe_downcast(nbs)
1171
+ # makes a difference bc blk may have object dtype, which has
1172
+ # different behavior in _maybe_downcast.
1173
+ return extend_blocks (
1174
+ [blk ._maybe_downcast ([blk ], downcast = downcast ) for blk in nbs ]
1175
+ )
1176
1176
1177
1177
def interpolate (
1178
1178
self ,
@@ -1401,7 +1401,8 @@ def setitem(self, indexer, value):
1401
1401
else :
1402
1402
return self
1403
1403
1404
- def where (self , other , cond ) -> list [Block ]:
1404
+ def where (self , other , cond , _downcast = "infer" ) -> list [Block ]:
1405
+ # _downcast private bc we only specify it when calling from fillna
1405
1406
arr = self .values .T
1406
1407
1407
1408
cond = extract_bool_array (cond )
@@ -1429,7 +1430,7 @@ def where(self, other, cond) -> list[Block]:
1429
1430
# TestSetitemFloatIntervalWithIntIntervalValues
1430
1431
blk = self .coerce_to_target_dtype (orig_other )
1431
1432
nbs = blk .where (orig_other , orig_cond )
1432
- return self ._maybe_downcast (nbs , "infer" )
1433
+ return self ._maybe_downcast (nbs , downcast = _downcast )
1433
1434
1434
1435
elif isinstance (self , NDArrayBackedExtensionBlock ):
1435
1436
# NB: not (yet) the same as
@@ -1485,39 +1486,29 @@ def fillna(
1485
1486
) -> list [Block ]:
1486
1487
# Caller is responsible for validating limit; if int it is strictly positive
1487
1488
1488
- try :
1489
- new_values = self .values .fillna (value = value , limit = limit )
1490
- except (TypeError , ValueError ) as err :
1491
- _catch_deprecated_value_error (err )
1492
-
1493
- if is_interval_dtype (self .dtype ):
1494
- # Discussion about what we want to support in the general
1495
- # case GH#39584
1496
- blk = self .coerce_to_target_dtype (value )
1497
- return blk .fillna (value , limit , inplace , downcast )
1498
-
1499
- elif isinstance (self , NDArrayBackedExtensionBlock ):
1500
- # We support filling a DatetimeTZ with a `value` whose timezone
1501
- # is different by coercing to object.
1502
- if self .dtype .kind == "m" :
1503
- # GH#45746
1504
- warnings .warn (
1505
- "The behavior of fillna with timedelta64[ns] dtype and "
1506
- f"an incompatible value ({ type (value )} ) is deprecated. "
1507
- "In a future version, this will cast to a common dtype "
1508
- "(usually object) instead of raising, matching the "
1509
- "behavior of other dtypes." ,
1510
- FutureWarning ,
1511
- stacklevel = find_stack_level (),
1512
- )
1513
- raise
1514
- blk = self .coerce_to_target_dtype (value )
1515
- return blk .fillna (value , limit , inplace , downcast )
1516
-
1517
- else :
1489
+ if self .dtype .kind == "m" :
1490
+ try :
1491
+ res_values = self .values .fillna (value , limit = limit )
1492
+ except (ValueError , TypeError ):
1493
+ # GH#45746
1494
+ warnings .warn (
1495
+ "The behavior of fillna with timedelta64[ns] dtype and "
1496
+ f"an incompatible value ({ type (value )} ) is deprecated. "
1497
+ "In a future version, this will cast to a common dtype "
1498
+ "(usually object) instead of raising, matching the "
1499
+ "behavior of other dtypes." ,
1500
+ FutureWarning ,
1501
+ stacklevel = find_stack_level (),
1502
+ )
1518
1503
raise
1504
+ else :
1505
+ res_blk = self .make_block (res_values )
1506
+ return [res_blk ]
1519
1507
1520
- return [self .make_block_same_class (values = new_values )]
1508
+ # TODO: since this now dispatches to super, which in turn dispatches
1509
+ # to putmask, it may *actually* respect 'inplace=True'. If so, add
1510
+ # tests for this.
1511
+ return super ().fillna (value , limit = limit , inplace = inplace , downcast = downcast )
1521
1512
1522
1513
def delete (self , loc ) -> Block :
1523
1514
# This will be unnecessary if/when __array_function__ is implemented
0 commit comments