根据另一列匹配部分文本
Posted
技术标签:
【中文标题】根据另一列匹配部分文本【英文标题】:match part of text based on another column 【发布时间】:2020-10-28 02:52:42 【问题描述】:我有这个数据集;
text num
test one 3.5 and 60 test tow 3.5/60
test one 3/4 test tow 3/4
test one 5.0 test 10 tow 5.0
如果数字匹配,我需要从文本列中删除数字 所以我这样做了:
df['text']=[re.sub(''.format(number), '', the_text) for the_text, number in zip(df['text'], df['num'])]
结果变成这样
text num
test one 3.5 and 60 test tow 3.5/60
test one test tow 3/4
test one test 10 tow 5.0
如您所见,除了第一行之外,匹配的数字已被删除,因为它不是完全匹配的。 所以我想使用包含函数匹配或任何类似于匹配部分文本的东西。
我希望第一行会变成
测试一个并测试两个
我已经这样做了,但出现错误:
[re.sub(r"\b\b".format(word), "", the_text) for the_text, word in zip(df['text'], word='/'.join([r''.format(words) for words in df['num']]) )]
TypeError: zip() takes no keyword arguments
有什么帮助吗?
【问题讨论】:
如果您说您遇到了错误,请始终发布错误消息。 代替''.format(number)
,试试r'(?<!\d)(?<!\d\.)(?:)(?!\.?\d)'.format('|'.join([re.escape(x) for x in number.split('/')]))
@WiktorStribiżew 不起作用
@JoelFan 谢谢你
你的示例数据框是df = pd.DataFrame('text': ['test one 3.5 and 60 test tow','test one 3/4 test tow', 'test one 5.0 test tow'], 'num': ['3.5/60', '3/4', '5.0'])
吗?
【参考方案1】:
这行得通:
import re
txt='''\
text num
test one 3.5 and 60 test tow 3.5/60
test one 3/4 test tow 3/4
test one 5.0 test tow 5.0'''
for line in txt.splitlines():
m=re.search(r'^(.*?[ \t]2,(?=\d))([0-9.\/]+)$', line)
if m:
a,_,b=m.group(2).partition('/')
if re.search(fr'\bm.group(2)\b', m.group(1)):
l=len(m.group(1))
s=re.sub(fr'[ ]?\bm.group(2)\b', '', m.group(1))
line=s+' '*(l-len(s))+m.group(2)
elif re.search(fr'a[^/]+b', m.group(1)):
l=len(m.group(1))
s=re.sub(fr'[ ]?\ba\b','',m.group(1))
s=re.sub(fr'[ ]?\bb\b','',s)
line=s+' '*(l-len(s))+m.group(2)
print(line)
打印:
text num
test one and test tow 3.5/60
test one test tow 3/4
test one test tow 5.0
【讨论】:
【参考方案2】:你可以使用
df['text'] = df.apply(lambda x: re.sub(r'(?<!\d)(?<!\d\.)(?:|)(?!\.?\d)'.format(re.escape(x['num']), '|'.join([re.escape(l) for l in x['num'].split('/')])), '', x['text']), axis=1)
感谢df.apply
和axis=1
,我们遍历所有行。
正则表达式是根据num
列中的值动态生成的,并应用于text
列。
r'(?<!\d)(?<!\d\.)(?:|)(?!\.?\d)'.format(re.escape(x['num']), '|'.join([re.escape(l) for l in x['num'].split('/')]))
创建一个类似的正则表达式
(?<!\d)(?<!\d\.)(?:3/4|3|4)(?!\.?\d)
分别匹配num
列中的完整值和/
之间的数字。
(?<!\d)(?<!\d\.)
是一个后向序列,如果在当前位置的左边有一个数字或一个数字 + 点,则匹配失败,如果有一个数字或一个点 +,(?!\.?\d)
则匹配失败当前位置右侧的数字有效地禁止数字匹配较长的数字。
【讨论】:
【参考方案3】:创建一个数字列表并添加/
nums = '|'.join(df['num'].tolist()).replace('/', '|') + '|/'
nums
'3.5|60|3|4|5.0|/'
然后str替换
df['text'].str.replace(nums, '')
0 test one and test tow
1 test one test tow
2 test one test tow
【讨论】:
请注意,.
是一个特殊的正则表达式元字符,5.0
将匹配 5 0
、5+0
等等。你需要逃避它。此外,没有任何界限,您将冒着将3
替换为43
的风险。
我想传递 num 列,以便每一行都匹配 text 列上的同一行
@WiktorStribiżew 所以如果文本上有任何其他数字,如果不匹配则不会删除以上是关于根据另一列匹配部分文本的主要内容,如果未能解决你的问题,请参考以下文章
将语料库中的名称部分匹配到 Pandas 数据框中另一列中的名称
MariaDB/mysql SQL查询问题:我想根据另一列的组值对一列进行部分总和