@@ -813,17 +813,50 @@ def replace(
813813 )
814814 return blocks
815815
816- def _replace_single (
816+ def _replace_regex (
817817 self ,
818818 to_replace ,
819819 value ,
820820 inplace : bool = False ,
821- regex : bool = False ,
822821 convert : bool = True ,
823822 mask = None ,
824823 ) -> List ["Block" ]:
825- """ no-op on a non-ObjectBlock """
826- return [self ] if inplace else [self .copy ()]
824+ """
825+ Replace elements by the given value.
826+
827+ Parameters
828+ ----------
829+ to_replace : object or pattern
830+ Scalar to replace or regular expression to match.
831+ value : object
832+ Replacement object.
833+ inplace : bool, default False
834+ Perform inplace modification.
835+ convert : bool, default True
836+ If true, try to coerce any object types to better types.
837+ mask : array-like of bool, optional
838+ True indicate corresponding element is ignored.
839+
840+ Returns
841+ -------
842+ List[Block]
843+ """
844+ if not self ._can_hold_element (to_replace ):
845+ # i.e. only ObjectBlock, but could in principle include a
846+ # String ExtensionBlock
847+ return [self ] if inplace else [self .copy ()]
848+
849+ rx = re .compile (to_replace )
850+
851+ new_values = self .values if inplace else self .values .copy ()
852+ replace_regex (new_values , rx , value , mask )
853+
854+ block = self .make_block (new_values )
855+ if convert :
856+ nbs = block .convert (numeric = False )
857+ else :
858+ nbs = [block ]
859+ return nbs
827860
828861 def _replace_list (
829862 self ,
@@ -1598,14 +1631,16 @@ def _replace_coerce(
15981631 self = self .coerce_to_target_dtype (value )
15991632 return self .putmask (mask , value , inplace = inplace )
16001633 else :
1601- return self ._replace_single (
1602- to_replace ,
1603- value ,
1604- inplace = inplace ,
1605- regex = regex ,
1606- convert = False ,
1607- mask = mask ,
1608- )
1634+ regex = _should_use_regex (regex , to_replace )
1635+ if regex :
1636+ return self ._replace_regex (
1637+ to_replace ,
1638+ value ,
1639+ inplace = inplace ,
1640+ convert = False ,
1641+ mask = mask ,
1642+ )
1643+ return self .replace (to_replace , value , inplace = inplace , regex = False )
16091644 return [self ]
16101645
16111646
@@ -2506,72 +2541,26 @@ def replace(
25062541 # here with listlike to_replace or value, as those cases
25072542 # go through _replace_list
25082543
2509- if is_re (to_replace ) or regex :
2510- return self ._replace_single (to_replace , value , inplace = inplace , regex = True )
2511- else :
2512- return super ().replace (to_replace , value , inplace = inplace , regex = regex )
2513-
2514- def _replace_single (
2515- self ,
2516- to_replace ,
2517- value ,
2518- inplace : bool = False ,
2519- regex : bool = False ,
2520- convert : bool = True ,
2521- mask = None ,
2522- ) -> List ["Block" ]:
2523- """
2524- Replace elements by the given value.
2525-
2526- Parameters
2527- ----------
2528- to_replace : object or pattern
2529- Scalar to replace or regular expression to match.
2530- value : object
2531- Replacement object.
2532- inplace : bool, default False
2533- Perform inplace modification.
2534- regex : bool, default False
2535- If true, perform regular expression substitution.
2536- convert : bool, default True
2537- If true, try to coerce any object types to better types.
2538- mask : array-like of bool, optional
2539- True indicate corresponding element is ignored.
2540-
2541- Returns
2542- -------
2543- List[Block]
2544- """
2545- inplace = validate_bool_kwarg (inplace , "inplace" )
2546-
2547- # to_replace is regex compilable
2548- regex = regex and is_re_compilable (to_replace )
2544+ regex = _should_use_regex (regex , to_replace )
25492545
2550- # try to get the pattern attribute (compiled re) or it's a string
2551- if is_re (to_replace ):
2552- pattern = to_replace .pattern
2546+ if regex :
2547+ return self ._replace_regex (to_replace , value , inplace = inplace )
25532548 else :
2554- pattern = to_replace
2549+ return super (). replace ( to_replace , value , inplace = inplace , regex = False )
25552550
2556- # if the pattern is not empty and to_replace is either a string or a
2557- # regex
2558- if regex and pattern :
2559- rx = re .compile (to_replace )
2560- else :
2561- # if the thing to replace is not a string or compiled regex call
2562- # the superclass method -> to_replace is some kind of object
2563- return super ().replace (to_replace , value , inplace = inplace , regex = regex )
25642551
2565- new_values = self .values if inplace else self .values .copy ()
2566- replace_regex (new_values , rx , value , mask )
2552+ def _should_use_regex (regex : bool , to_replace : Any ) -> bool :
2553+ """
2554+ Decide whether to treat `to_replace` as a regular expression.
2555+ """
2556+ if is_re (to_replace ):
2557+ regex = True
25672558
2568- # convert
2569- block = self .make_block (new_values )
2570- if convert :
2571- nbs = block .convert (numeric = False )
2572- else :
2573- nbs = [block ]
2574- return nbs
2559+ regex = regex and is_re_compilable (to_replace )
2560+
2561+ # Don't use regex if the pattern is empty.
2562+ regex = regex and re .compile (to_replace ).pattern != ""
2563+ return regex
25752564
25762565
25772566class CategoricalBlock (ExtensionBlock ):
0 commit comments