Skip to content

Commit bea8c89

Browse files
BUG: case argument ignored when regex is False in Series.str.replace (#41606)
1 parent 7cdd833 commit bea8c89

File tree

4 files changed

+26
-5
lines changed

4 files changed

+26
-5
lines changed

doc/source/whatsnew/v1.3.0.rst

+1
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,7 @@ Strings
832832
- Bug in the conversion from ``pyarrow.ChunkedArray`` to :class:`~arrays.StringArray` when the original had zero chunks (:issue:`41040`)
833833
- Bug in :meth:`Series.replace` and :meth:`DataFrame.replace` ignoring replacements with ``regex=True`` for ``StringDType`` data (:issue:`41333`, :issue:`35977`)
834834
- Bug in :meth:`Series.str.extract` with :class:`~arrays.StringArray` returning object dtype for empty :class:`DataFrame` (:issue:`41441`)
835+
- Bug in :meth:`Series.str.replace` where the ``case`` argument was ignored when ``regex=False`` (:issue:`41602`)
835836

836837
Interval
837838
^^^^^^^^

pandas/core/strings/accessor.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -1358,14 +1358,13 @@ def replace(
13581358
"*not* be treated as literal strings when regex=True."
13591359
)
13601360
warnings.warn(msg, FutureWarning, stacklevel=3)
1361-
regex = True
13621361

13631362
# Check whether repl is valid (GH 13438, GH 15055)
13641363
if not (isinstance(repl, str) or callable(repl)):
13651364
raise TypeError("repl must be a string or callable")
13661365

13671366
is_compiled_re = is_re(pat)
1368-
if regex:
1367+
if regex or regex is None:
13691368
if is_compiled_re and (case is not None or flags != 0):
13701369
raise ValueError(
13711370
"case and flags cannot be set when pat is a compiled regex"
@@ -1378,6 +1377,14 @@ def replace(
13781377
elif callable(repl):
13791378
raise ValueError("Cannot use a callable replacement when regex=False")
13801379

1380+
# The current behavior is to treat single character patterns as literal strings,
1381+
# even when ``regex`` is set to ``True``.
1382+
if isinstance(pat, str) and len(pat) == 1:
1383+
regex = False
1384+
1385+
if regex is None:
1386+
regex = True
1387+
13811388
if case is None:
13821389
case = True
13831390

pandas/core/strings/object_array.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,10 @@ def _str_replace(
145145
# add case flag, if provided
146146
flags |= re.IGNORECASE
147147

148-
if regex and (
149-
isinstance(pat, re.Pattern) or len(pat) > 1 or flags or callable(repl)
150-
):
148+
if regex or flags or callable(repl):
151149
if not isinstance(pat, re.Pattern):
150+
if regex is False:
151+
pat = re.escape(pat)
152152
pat = re.compile(pat, flags=flags)
153153

154154
n = n if n >= 0 else 0

pandas/tests/strings/test_find_replace.py

+13
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,19 @@ def test_replace_moar(any_string_dtype):
555555
tm.assert_series_equal(result, expected)
556556

557557

558+
def test_replace_not_case_sensitive_not_regex(any_string_dtype):
559+
# https://github.com/pandas-dev/pandas/issues/41602
560+
ser = Series(["A.", "a.", "Ab", "ab", np.nan], dtype=any_string_dtype)
561+
562+
result = ser.str.replace("a", "c", case=False, regex=False)
563+
expected = Series(["c.", "c.", "cb", "cb", np.nan], dtype=any_string_dtype)
564+
tm.assert_series_equal(result, expected)
565+
566+
result = ser.str.replace("a.", "c.", case=False, regex=False)
567+
expected = Series(["c.", "c.", "Ab", "ab", np.nan], dtype=any_string_dtype)
568+
tm.assert_series_equal(result, expected)
569+
570+
558571
def test_replace_regex_default_warning(any_string_dtype):
559572
# https://github.com/pandas-dev/pandas/pull/24809
560573
s = Series(["a", "b", "ac", np.nan, ""], dtype=any_string_dtype)

0 commit comments

Comments
 (0)