I am trying to iterate through a Pandas dataframe's column values one by one to detect a substring with a RegEx pattern and replace it wherever it shows up. The string values in the dataframe's target column have the following variations:
I am attempting to target the values with the 2nd variation and replace the comma-separated substring by a value from another column in the same row.
My sample code looks like:
import re
for i, x in enumerate(df['col1']):
df.loc[i,'col2'] = re.sub(r'^(func\().+,.+(\).*)', r'\1%s\2'%(x), df.loc[i,'col2'])
I have had a successful output on a terminal when I replace the *
in the pattern with +
:
>>> val = 'func(y,z) and func2(a,b)'
>>> val
'func(y,z) and func2(a,b)'
>>> pattern1 = r'^(func\().+,.+(\).*)'
>>> replacement = r'\1%s\2'%('x')
>>> val2 = re.sub(pattern1, replacement, val)
>>> val2
'func(x)'
>>> pattern2 = r'^(func\().+,.+(\).+)'
>>> val2 = re.sub(pattern2, replacement, val)
>>> val2
'func(x) and func2(a,b)'
>>> val3 = 'func(y) and func2(a,b)'
>>> val4 = re.sub(pattern2, replacement, val3)
>>> val4
'func(y) and func2(a, b)'
I thought the .*
in pattern1
should have accommodated for 0 or more instances of any character after the final captured parenthesis \)
, but at least using .+
in pattern2
works.
Problem is, when I try to replicate it on VSCode with Python on the dataframe's column values, I am not getting the desired output. I think my regex pattern is catching other parts of the strings I did not intend, so I am getting mixed results.
Am I missing anything with pattern2
?
Update: Here is an example code I used in VSCode in which the result is promising, but when the string has more conditions added the pattern doesn't hold up.
df = pd.DataFrame({'col1':['func(a,b) and func(c,d)','func(a) and func(c)','func(b) and func(c,d)','func(a,b,c) and func(d,e,f)'],
'col2':['e','b','a','g']})
df
>>>
col1 col2
0 func(a,b) and func(c,d) e
1 func(a) and func(c) b
2 func(b) and func(c,d) a
3 func(a,b,c) and func(d,e,f) g
import re
df['col3'] = ''
for i,x in enumerate(df['col2']):
pattern = r'^(func\().+,.+(\).+)'
replacement = fr'\1{x}\2'
df.loc[i,'col3'] = re.sub(pattern,replacement,df.loc[i,'col1'])
if df.loc[i,'col3'] != df.loc[i,'col1']:
print(df.loc[i,'col1'],'--->',df.loc[i,'col3'])
>>>
func(a,b) and func(c,d) ---> func(e) and func(c,d)
func(a,b,c) and func(d,e,f) ---> func(g) and func(d,e,f)
The above is what I expected to see, but in the attempt below, you can see additional func()
conditions get dropped:
df = pd.DataFrame({'col1':['func(a,b) and func(c,d) and func2(z)','func(a) and func(c) and func2(z)','func(b) and func(c,d) and func2(z)','func(a,b,c) and func(d,e,f) and func2(z)'],
'col2':['e','b','a','g']})
df
>>>
col1 col2
0 func(a,b) and func(c,d) and func2(z) e
1 func(a) and func(c) and func2(z) b
2 func(b) and func(c,d) and func2(z) a
3 func(a,b,c) and func(d,e,f) and func2(z) g
import re
df['col3'] = ''
for i,x in enumerate(df['col2']):
pattern = r'^(func\().+(?:,.+)+(\).+)'
replacement = fr'\1{x}\2'
df.loc[i,'col3'] = re.sub(pattern,replacement,df.loc[i,'col1'])
if df.loc[i,'col3'] != df.loc[i,'col1']:
print(df.loc[i,'col1'],'--->',df.loc[i,'col3'])
>>>
func(a,b) and func(c,d) and func2(z) ---> func(e) and func2(z)
func(b) and func(c,d) and func2(z) ---> func(a) and func2(z)
func(a,b,c) and func(d,e,f) and func2(z) ---> func(g) and func2(z)
The pattern seems to leave out the middle func()
expressions.
Prepare the replacement string for each iteration before inserting it in the re.sub(pattern, replacement, text_string)
function.
Updated based on new information on test string and desired output.
REGEX PATTERN (Python re module):
^func\([^),]+(,[^,)]+)+\)
Regex demo: https://regex101.com/r/aKyQSs/4
PYTHON
import re
df = {
'col1':['func(a,b) and func(c,d)','func(a) and func(c)','func(b) and func(c,d)','func(a,b,c) and func(d,e,f)'],
'col2':['e','b','a','g']
}
df['col3'] = []
pattern = r'^func\([^),]+(,[^,)]+)+\)'
for i, x in enumerate(df['col2']):
replacement = r'func(%s)' % x
result = re.sub(pattern, replacement, df['col1'][i])
df['col3'].append((result))
print(df['col3'])
OUTPUT:
['func(e) and func(c,d)', 'func(a) and func(c)', 'func(b) and func(c,d)', 'func(g) and func(d,e,f)']
REGEX NOTES:
^
Match beginning of string ^
.func\(
Match literal func(
[^),]+
Negated character class [^...]
. Match any character that is not a literal )
or ,
, 1 or more times (+
).(
,
Match literal ,
.[^),]+
Negated character class [^...]
. Match any character that is not a literal )
or ,
, 1 or more times (+
).)+
Match 1 or more times (+
).\)
Match literal )
.