24
24
astype_dt64_to_dt64tz ,
25
25
astype_nansafe ,
26
26
can_hold_element ,
27
- convert_scalar_for_putitemlike ,
28
27
find_common_type ,
29
28
infer_dtype_from ,
30
29
maybe_downcast_numeric ,
52
51
53
52
import pandas .core .algorithms as algos
54
53
from pandas .core .array_algos .putmask import (
54
+ extract_bool_array ,
55
55
putmask_inplace ,
56
56
putmask_smart ,
57
57
putmask_without_repeat ,
58
+ setitem_datetimelike_compat ,
59
+ validate_putmask ,
58
60
)
59
61
from pandas .core .array_algos .quantile import quantile_with_mask
60
62
from pandas .core .array_algos .replace import (
@@ -425,7 +427,8 @@ def fillna(
425
427
inplace = validate_bool_kwarg (inplace , "inplace" )
426
428
427
429
mask = isna (self .values )
428
- mask = _extract_bool_array (mask )
430
+ mask , noop = validate_putmask (self .values , mask )
431
+
429
432
if limit is not None :
430
433
limit = libalgos .validate_limit (None , limit = limit )
431
434
mask [mask .cumsum (self .ndim - 1 ) > limit ] = False
@@ -442,8 +445,8 @@ def fillna(
442
445
# TODO: should be nb._maybe_downcast?
443
446
return self ._maybe_downcast ([nb ], downcast )
444
447
445
- # we can't process the value, but nothing to do
446
- if not mask . any ():
448
+ if noop :
449
+ # we can't process the value, but nothing to do
447
450
return [self ] if inplace else [self .copy ()]
448
451
449
452
# operate column-by-column
@@ -846,7 +849,7 @@ def _replace_list(
846
849
# GH#38086 faster if we know we dont need to check for regex
847
850
masks = [missing .mask_missing (self .values , s [0 ]) for s in pairs ]
848
851
849
- masks = [_extract_bool_array (x ) for x in masks ]
852
+ masks = [extract_bool_array (x ) for x in masks ]
850
853
851
854
rb = [self if inplace else self .copy ()]
852
855
for i , (src , dest ) in enumerate (pairs ):
@@ -968,18 +971,8 @@ def setitem(self, indexer, value):
968
971
# TODO(EA2D): special case not needed with 2D EA
969
972
values [indexer ] = value .to_numpy (values .dtype ).reshape (- 1 , 1 )
970
973
971
- elif self .is_object and not is_ea_value and arr_value .dtype .kind in ["m" , "M" ]:
972
- # https://github.com/numpy/numpy/issues/12550
973
- # numpy will incorrect cast to int if we're not careful
974
- if is_list_like (value ):
975
- value = list (value )
976
- else :
977
- value = [value ] * len (values [indexer ])
978
-
979
- values [indexer ] = value
980
-
981
974
else :
982
-
975
+ value = setitem_datetimelike_compat ( values , len ( values [ indexer ]), value )
983
976
values [indexer ] = value
984
977
985
978
if transpose :
@@ -1004,7 +997,7 @@ def putmask(self, mask, new) -> List[Block]:
1004
997
List[Block]
1005
998
"""
1006
999
transpose = self .ndim == 2
1007
- mask = _extract_bool_array ( mask )
1000
+ mask , noop = validate_putmask ( self . values . T , mask )
1008
1001
assert not isinstance (new , (ABCIndex , ABCSeries , ABCDataFrame ))
1009
1002
1010
1003
new_values = self .values # delay copy if possible.
@@ -1020,7 +1013,7 @@ def putmask(self, mask, new) -> List[Block]:
1020
1013
putmask_without_repeat (new_values , mask , new )
1021
1014
return [self ]
1022
1015
1023
- elif not mask . any () :
1016
+ elif noop :
1024
1017
return [self ]
1025
1018
1026
1019
dtype , _ = infer_dtype_from (new )
@@ -1296,12 +1289,13 @@ def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]:
1296
1289
if transpose :
1297
1290
values = values .T
1298
1291
1299
- cond = _extract_bool_array ( cond )
1292
+ icond , noop = validate_putmask ( values , ~ cond )
1300
1293
1301
1294
if is_valid_na_for_dtype (other , self .dtype ) and not self .is_object :
1302
1295
other = self .fill_value
1303
1296
1304
- if cond .ravel ("K" ).all ():
1297
+ if noop :
1298
+ # TODO: avoid the downcasting at the end in this case?
1305
1299
result = values
1306
1300
else :
1307
1301
# see if we can operate on the entire block, or need item-by-item
@@ -1313,23 +1307,14 @@ def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]:
1313
1307
blocks = block .where (orig_other , cond , errors = errors , axis = axis )
1314
1308
return self ._maybe_downcast (blocks , "infer" )
1315
1309
1316
- dtype , _ = infer_dtype_from (other , pandas_dtype = True )
1317
- if dtype .kind in ["m" , "M" ] and dtype .kind != values .dtype .kind :
1318
- # expressions.where would cast np.timedelta64 to int
1319
- if not is_list_like (other ):
1320
- other = [other ] * (~ cond ).sum ()
1321
- else :
1322
- other = list (other )
1310
+ alt = setitem_datetimelike_compat (values , icond .sum (), other )
1311
+ if alt is not other :
1323
1312
result = values .copy ()
1324
- np .putmask (result , ~ cond , other )
1325
-
1313
+ np .putmask (result , icond , alt )
1326
1314
else :
1327
- # convert datetime to datetime64, timedelta to timedelta64
1328
- other = convert_scalar_for_putitemlike (other , values .dtype )
1329
-
1330
1315
# By the time we get here, we should have all Series/Index
1331
1316
# args extracted to ndarray
1332
- result = expressions .where (cond , values , other )
1317
+ result = expressions .where (~ icond , values , other )
1333
1318
1334
1319
if self ._can_hold_na or self .ndim == 1 :
1335
1320
@@ -1339,6 +1324,7 @@ def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]:
1339
1324
return [self .make_block (result )]
1340
1325
1341
1326
# might need to separate out blocks
1327
+ cond = ~ icond
1342
1328
axis = cond .ndim - 1
1343
1329
cond = cond .swapaxes (axis , 0 )
1344
1330
mask = np .array ([cond [i ].all () for i in range (cond .shape [0 ])], dtype = bool )
@@ -1545,7 +1531,7 @@ def putmask(self, mask, new) -> List[Block]:
1545
1531
"""
1546
1532
See Block.putmask.__doc__
1547
1533
"""
1548
- mask = _extract_bool_array (mask )
1534
+ mask = extract_bool_array (mask )
1549
1535
1550
1536
new_values = self .values
1551
1537
@@ -1775,7 +1761,7 @@ def shift(self, periods: int, axis: int = 0, fill_value: Any = None) -> List[Blo
1775
1761
1776
1762
def where (self , other , cond , errors = "raise" , axis : int = 0 ) -> List [Block ]:
1777
1763
1778
- cond = _extract_bool_array (cond )
1764
+ cond = extract_bool_array (cond )
1779
1765
assert not isinstance (other , (ABCIndex , ABCSeries , ABCDataFrame ))
1780
1766
1781
1767
if isinstance (other , np .ndarray ) and other .ndim == 2 :
@@ -2019,7 +2005,7 @@ def to_native_types(self, na_rep="NaT", **kwargs):
2019
2005
return self .make_block (result )
2020
2006
2021
2007
def putmask (self , mask , new ) -> List [Block ]:
2022
- mask = _extract_bool_array (mask )
2008
+ mask = extract_bool_array (mask )
2023
2009
2024
2010
if not self ._can_hold_element (new ):
2025
2011
return self .astype (object ).putmask (mask , new )
@@ -2034,7 +2020,7 @@ def where(self, other, cond, errors="raise", axis: int = 0) -> List[Block]:
2034
2020
# TODO(EA2D): reshape unnecessary with 2D EAs
2035
2021
arr = self .array_values ().reshape (self .shape )
2036
2022
2037
- cond = _extract_bool_array (cond )
2023
+ cond = extract_bool_array (cond )
2038
2024
2039
2025
try :
2040
2026
res_values = arr .T .where (cond , other ).T
@@ -2513,18 +2499,3 @@ def safe_reshape(arr: ArrayLike, new_shape: Shape) -> ArrayLike:
2513
2499
# TODO(EA2D): special case will be unnecessary with 2D EAs
2514
2500
arr = np .asarray (arr ).reshape (new_shape )
2515
2501
return arr
2516
-
2517
-
2518
- def _extract_bool_array (mask : ArrayLike ) -> np .ndarray :
2519
- """
2520
- If we have a SparseArray or BooleanArray, convert it to ndarray[bool].
2521
- """
2522
- if isinstance (mask , ExtensionArray ):
2523
- # We could have BooleanArray, Sparse[bool], ...
2524
- # Except for BooleanArray, this is equivalent to just
2525
- # np.asarray(mask, dtype=bool)
2526
- mask = mask .to_numpy (dtype = bool , na_value = False )
2527
-
2528
- assert isinstance (mask , np .ndarray ), type (mask )
2529
- assert mask .dtype == bool , mask .dtype
2530
- return mask
0 commit comments