-
-
Notifications
You must be signed in to change notification settings - Fork 18.5k
BUG: Fix for .str.replace with repl function #15056
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
6ecc43d
91c883d
4baa0a7
30d4727
067a7a8
ae04a3e
27065a2
14beb21
f15ee2a
40c0d97
e15dcdf
c2cc13a
90779ce
826730c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -303,8 +303,9 @@ def str_replace(arr, pat, repl, n=-1, case=True, flags=0): | |
---------- | ||
pat : string | ||
Character sequence or regular expression | ||
repl : string | ||
Replacement sequence | ||
repl : string or callable | ||
Replacement string or a callable, it's passed the match object and | ||
must return a replacement string to be used. See :func:`re.sub`. | ||
n : int, default -1 (all) | ||
Number of replacements to make from start | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you add some examples to the doc-string? also would be nice to have an example in text.rst |
||
case : boolean, default True | ||
|
@@ -318,9 +319,9 @@ def str_replace(arr, pat, repl, n=-1, case=True, flags=0): | |
""" | ||
|
||
# Check whether repl is valid (GH 13438) | ||
if not is_string_like(repl): | ||
raise TypeError("repl must be a string") | ||
use_re = not case or len(pat) > 1 or flags | ||
if not (is_string_like(repl) or callable(repl)): | ||
raise TypeError("repl must be a string or function") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. function -> callable |
||
use_re = not case or len(pat) > 1 or flags or callable(repl) | ||
|
||
if use_re: | ||
if not case: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -435,6 +435,12 @@ def test_replace(self): | |
for data in (['a', 'b', None], ['a', 'b', 'c', 'ad']): | ||
values = klass(data) | ||
self.assertRaises(TypeError, values.str.replace, 'a', repl) | ||
|
||
# GH 15055, callable repl | ||
repl = lambda m: m.group(0).swapcase() | ||
result = values.str.replace('[a-z][A-Z]{2}', repl, n=2) | ||
exp = Series([u('foObaD__baRbaD'), NA]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. test where the callable has different than 1 arg (should be error) test using named field in re (and callable works correctly) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, @jreback . Named field/group is a good idea. If the callable raises a We could overcome this by either inspecting the callable signature: Sorry about the checks going wrong. It seems I am too careless with checking my commits. 😕 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With n_empty_args = sum(1 for p in inspect.signature(repl).parameters.values()
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD, p.KEYWORD_ONLY)
and p.default is p.empty)
# or better readable syntax
signature = inspect.signature(repl)
empty_args = [p for p in signature.parameters.values()
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD, p.KEYWORD_ONLY)
and p.default is p.empty]
n_empty_args = len(empty_args)
# followed by
if n_empty_args > 1:
raise TypeError("callable should have at most one positional without a default")
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is too complicated, instead maybe you can introspect the actual error message in |
||
tm.assert_series_equal(result, exp) | ||
|
||
def test_repeat(self): | ||
values = Series(['a', 'b', NA, 'c', NA, 'd']) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add the version (0.20.0) after callable