55
55
from pandas .core .dtypes .generic import (
56
56
ABCDataFrame ,
57
57
ABCExtensionArray ,
58
+ ABCIndexClass ,
58
59
ABCPandasArray ,
59
60
ABCSeries ,
60
61
)
@@ -913,7 +914,7 @@ def putmask(
913
914
914
915
Parameters
915
916
----------
916
- mask : the condition to respect
917
+ mask : np.ndarray[bool], SparseArray[bool], or BooleanArray
917
918
new : a ndarray/object
918
919
inplace : bool, default False
919
920
Perform inplace modification.
@@ -925,10 +926,10 @@ def putmask(
925
926
-------
926
927
List[Block]
927
928
"""
928
- new_values = self .values if inplace else self .values .copy ()
929
+ mask = _extract_bool_array (mask )
930
+ assert not isinstance (new , (ABCIndexClass , ABCSeries , ABCDataFrame ))
929
931
930
- new = getattr (new , "values" , new )
931
- mask = getattr (mask , "values" , mask )
932
+ new_values = self .values if inplace else self .values .copy ()
932
933
933
934
# if we are passed a scalar None, convert it here
934
935
if not is_list_like (new ) and isna (new ) and not self .is_object :
@@ -1308,18 +1309,21 @@ def where(
1308
1309
Parameters
1309
1310
----------
1310
1311
other : a ndarray/object
1311
- cond : the condition to respect
1312
+ cond : np.ndarray[bool], SparseArray[bool], or BooleanArray
1312
1313
errors : str, {'raise', 'ignore'}, default 'raise'
1313
1314
- ``raise`` : allow exceptions to be raised
1314
1315
- ``ignore`` : suppress exceptions. On error return original object
1315
1316
axis : int, default 0
1316
1317
1317
1318
Returns
1318
1319
-------
1319
- a new block(s), the result of the func
1320
+ List[Block]
1320
1321
"""
1321
1322
import pandas .core .computation .expressions as expressions
1322
1323
1324
+ cond = _extract_bool_array (cond )
1325
+ assert not isinstance (other , (ABCIndexClass , ABCSeries , ABCDataFrame ))
1326
+
1323
1327
assert errors in ["raise" , "ignore" ]
1324
1328
transpose = self .ndim == 2
1325
1329
@@ -1328,9 +1332,6 @@ def where(
1328
1332
if transpose :
1329
1333
values = values .T
1330
1334
1331
- other = getattr (other , "_values" , getattr (other , "values" , other ))
1332
- cond = getattr (cond , "values" , cond )
1333
-
1334
1335
# If the default broadcasting would go in the wrong direction, then
1335
1336
# explicitly reshape other instead
1336
1337
if getattr (other , "ndim" , 0 ) >= 1 :
@@ -1628,9 +1629,9 @@ def putmask(
1628
1629
"""
1629
1630
inplace = validate_bool_kwarg (inplace , "inplace" )
1630
1631
1631
- # use block's copy logic.
1632
- # .values may be an Index which does shallow copy by default
1633
- new_values = self .values if inplace else self .copy (). values
1632
+ mask = _extract_bool_array ( mask )
1633
+
1634
+ new_values = self .values if inplace else self .values . copy ()
1634
1635
1635
1636
if isinstance (new , np .ndarray ) and len (new ) == len (mask ):
1636
1637
new = new [mask ]
@@ -1859,19 +1860,19 @@ def shift(
1859
1860
def where (
1860
1861
self , other , cond , errors = "raise" , try_cast : bool = False , axis : int = 0 ,
1861
1862
) -> List ["Block" ]:
1862
- if isinstance (other , ABCDataFrame ):
1863
- # ExtensionArrays are 1-D, so if we get here then
1864
- # `other` should be a DataFrame with a single column.
1865
- assert other .shape [1 ] == 1
1866
- other = other .iloc [:, 0 ]
1867
1863
1868
- other = extract_array (other , extract_numpy = True )
1864
+ cond = _extract_bool_array (cond )
1865
+ assert not isinstance (other , (ABCIndexClass , ABCSeries , ABCDataFrame ))
1869
1866
1870
- if isinstance (cond , ABCDataFrame ):
1871
- assert cond .shape [1 ] == 1
1872
- cond = cond .iloc [:, 0 ]
1867
+ if isinstance (other , np .ndarray ) and other .ndim == 2 :
1868
+ # TODO(EA2D): unnecessary with 2D EAs
1869
+ assert other .shape [1 ] == 1
1870
+ other = other [:, 0 ]
1873
1871
1874
- cond = extract_array (cond , extract_numpy = True )
1872
+ if isinstance (cond , np .ndarray ) and cond .ndim == 2 :
1873
+ # TODO(EA2D): unnecessary with 2D EAs
1874
+ assert cond .shape [1 ] == 1
1875
+ cond = cond [:, 0 ]
1875
1876
1876
1877
if lib .is_scalar (other ) and isna (other ):
1877
1878
# The default `other` for Series / Frame is np.nan
@@ -3113,3 +3114,16 @@ def _putmask_preserve(nv, n):
3113
3114
v = v .astype (dtype )
3114
3115
3115
3116
return _putmask_preserve (v , n )
3117
+
3118
+
3119
+ def _extract_bool_array (mask : ArrayLike ) -> np .ndarray :
3120
+ """
3121
+ If we have a SparseArray or BooleanArray, convert it to ndarray[bool].
3122
+ """
3123
+ if isinstance (mask , ExtensionArray ):
3124
+ # We could have BooleanArray, Sparse[bool], ...
3125
+ mask = np .asarray (mask , dtype = np .bool_ )
3126
+
3127
+ assert isinstance (mask , np .ndarray ), type (mask )
3128
+ assert mask .dtype == bool , mask .dtype
3129
+ return mask
0 commit comments