@@ -678,7 +678,6 @@ def copy(self, deep: bool = True):
678
678
# ---------------------------------------------------------------------
679
679
# Replace
680
680
681
- @final
682
681
def replace (
683
682
self ,
684
683
to_replace ,
@@ -693,30 +692,15 @@ def replace(
693
692
"""
694
693
inplace = validate_bool_kwarg (inplace , "inplace" )
695
694
696
- # Note: the checks we do in NDFrame.replace ensure we never get
697
- # here with listlike to_replace or value, as those cases
698
- # go through _replace_list
699
-
700
- values = self .values
701
-
702
- if isinstance (values , Categorical ):
703
- # TODO: avoid special-casing
704
- blk = self if inplace else self .copy ()
705
- blk .values .replace (to_replace , value , inplace = True )
706
- return [blk ]
707
-
708
- regex = should_use_regex (regex , to_replace )
709
-
710
- if regex :
711
- return self ._replace_regex (to_replace , value , inplace = inplace )
712
-
713
695
if not self ._can_hold_element (to_replace ):
714
696
# We cannot hold `to_replace`, so we know immediately that
715
697
# replacing it is a no-op.
716
698
# Note: If to_replace were a list, NDFrame.replace would call
717
699
# replace_list instead of replace.
718
700
return [self ] if inplace else [self .copy ()]
719
701
702
+ values = self .values
703
+
720
704
mask = missing .mask_missing (values , to_replace )
721
705
if not mask .any ():
722
706
# Note: we get here with test_replace_extension_other incorrectly
@@ -741,7 +725,7 @@ def replace(
741
725
else :
742
726
# split so that we only upcast where necessary
743
727
return self .split_and_operate (
744
- type (self ).replace , to_replace , value , inplace = True , regex = regex
728
+ type (self ).replace , to_replace , value , inplace = inplace , regex = regex
745
729
)
746
730
747
731
@final
@@ -1244,7 +1228,7 @@ def take_nd(
1244
1228
Take values according to indexer and return them as a block.bb
1245
1229
1246
1230
"""
1247
- # algos.take_nd dispatches for DatetimeTZBlock
1231
+ # algos.take_nd dispatches for DatetimeTZBlock, CategoricalBlock
1248
1232
# so need to preserve types
1249
1233
# sparse is treated like an ndarray, but needs .get_values() shaping
1250
1234
@@ -1443,7 +1427,7 @@ class ExtensionBlock(Block):
1443
1427
Notes
1444
1428
-----
1445
1429
This holds all 3rd-party extension array types. It's also the immediate
1446
- parent class for our internal extension types' blocks.
1430
+ parent class for our internal extension types' blocks, CategoricalBlock .
1447
1431
1448
1432
ExtensionArrays are limited to 1-D.
1449
1433
"""
@@ -1595,6 +1579,7 @@ def take_nd(
1595
1579
1596
1580
def _can_hold_element (self , element : Any ) -> bool :
1597
1581
# TODO: We may need to think about pushing this onto the array.
1582
+ # We're doing the same as CategoricalBlock here.
1598
1583
return True
1599
1584
1600
1585
def _slice (self , slicer ):
@@ -2016,6 +2001,41 @@ def _maybe_downcast(self, blocks: List[Block], downcast=None) -> List[Block]:
2016
2001
def _can_hold_element (self , element : Any ) -> bool :
2017
2002
return True
2018
2003
2004
+ def replace (
2005
+ self ,
2006
+ to_replace ,
2007
+ value ,
2008
+ inplace : bool = False ,
2009
+ regex : bool = False ,
2010
+ ) -> List [Block ]:
2011
+ # Note: the checks we do in NDFrame.replace ensure we never get
2012
+ # here with listlike to_replace or value, as those cases
2013
+ # go through _replace_list
2014
+
2015
+ regex = should_use_regex (regex , to_replace )
2016
+
2017
+ if regex :
2018
+ return self ._replace_regex (to_replace , value , inplace = inplace )
2019
+ else :
2020
+ return super ().replace (to_replace , value , inplace = inplace , regex = False )
2021
+
2022
+
2023
+ class CategoricalBlock (ExtensionBlock ):
2024
+ __slots__ = ()
2025
+
2026
+ def replace (
2027
+ self ,
2028
+ to_replace ,
2029
+ value ,
2030
+ inplace : bool = False ,
2031
+ regex : bool = False ,
2032
+ ) -> List [Block ]:
2033
+ inplace = validate_bool_kwarg (inplace , "inplace" )
2034
+ result = self if inplace else self .copy ()
2035
+
2036
+ result .values .replace (to_replace , value , inplace = True )
2037
+ return [result ]
2038
+
2019
2039
2020
2040
# -----------------------------------------------------------------
2021
2041
# Constructor Helpers
@@ -2078,7 +2098,7 @@ def get_block_type(values, dtype: Optional[Dtype] = None):
2078
2098
# Need this first(ish) so that Sparse[datetime] is sparse
2079
2099
cls = ExtensionBlock
2080
2100
elif isinstance (dtype , CategoricalDtype ):
2081
- cls = ExtensionBlock
2101
+ cls = CategoricalBlock
2082
2102
elif vtype is Timestamp :
2083
2103
cls = DatetimeTZBlock
2084
2104
elif vtype is Interval or vtype is Period :
0 commit comments