在不同大小的框B中检查框A的子串
我有一个问题,希望你能帮我解决。我有两个数据框,每个数据框都有很多列,但为了简单起见,我们只关注每个数据框的一列。重要的是:这两个数据框的大小不同,数据框A比较短。
import pandas as pd
FrameA=pd.DataFrame({"A":["00281378554", "10862520000","82540193700","76015394900","00134355050","21864009"]})
FrameB=pd.DataFrame({"A":["AT511634000134355050","AT411513000281378554", "AT711509100151013992",
"AT511509000121340020","AT424480010862520000","AT742011182540193700","AT531200076015394900","HU02142201082186400900000000"
]})
我的目标是:我想检查数据框A中列A的每个元素,看看它是否在数据框B的列A中存在。如果存在,我想在数据框A(较短的那个)中创建一个新列,结果如下:
Frame A
Col A Col B
00281378554 AT411513000281378554
10862520000 AT424480010862520000
82540193700 AT742011182540193700
76015394900 AT531200076015394900
00134355050 AT511634000134355050
21864009 HU02142201082186400900000000
所以数据框A的新列B应该包含数据框B中包含数据框A字符串的元素。我尝试使用np.where
,但由于数据框A的维度比数据框B短,所以这个方法不奏效。因此,我只能使用for循环。但这非常耗时,我觉得应该有更优雅的解决方案。
有什么建议吗?
3 个回答
0
使用这个,你就不需要担心长度匹配的问题了:
FrameA['B'] = FrameA['A'].apply(lambda x: FrameB.loc[FrameB['A'].str.contains(x), 'A'].iloc[0] if not FrameB.loc[FrameB['A'].str.contains(x), 'A'].empty else None)
1
另一种可能的解决方案是使用 regex
(正则表达式)来查找匹配的内容,并且结合 list comprehension
(列表推导式)来实现:
import re
pd.DataFrame(
[[x, y] for x in FrameA.A for y in FrameB.A if re.match(f'.*{x}.*',y)],
columns=list('AB'))
输出结果:
A B
0 00281378554 AT411513000281378554
1 10862520000 AT424480010862520000
2 82540193700 AT742011182540193700
3 76015394900 AT531200076015394900
4 00134355050 AT511634000134355050
5 21864009 HU02142201082186400900000000
1
如果FrameA中的子字符串总是和FrameB的最后11个字符匹配,那么你可以使用切片和map
来处理:
FrameA['B'] = FrameA['A'].map(FrameB.set_index(FrameB['A'].str[-11:])['A'])
或者可以使用merge
:
out = FrameA.merge(FrameB.rename(columns={'A': 'B'}),
left_on='A', right_on=FrameB['A'].str[-11:], how='left')
输出结果:
A B
0 00281378554 AT411513000281378554
1 10862520000 AT424480010862520000
2 82540193700 AT742011182540193700
3 76015394900 AT531200076015394900
4 00134355050 NaN
如果子字符串不是固定的,你可以将相同长度的字符串分组,然后用同样的方法处理。你也可以确保FrameB中没有重复的值。
def make_mapper(length):
idx = FrameB['A'].str[-length:]
return (FrameB.set_index(idx)['A']
.groupby(level=0).first()
)
FrameA['B'] = (FrameA.groupby(FrameA['A'].str.len(), group_keys=False)['A']
.apply(lambda g: g.map(make_mapper(g.name)))
)
输出结果:
A B
0 00281378554 AT411513000281378554
1 10862520000 AT424480010862520000
2 82540193700 AT742011182540193700
3 76015394900 AT531200076015394900
4 00134355050 NaN
在完成这个第一步(高效)的映射后,你可以使用第二步,虽然效率较低,但可以根据任意位置的子字符串来填充其他值:
def find_anywhere(substr):
return next((s for s in FrameB['A'] if substr in s), None)
m = FrameA['B'].isna()
FrameA.loc[m, 'B'] = FrameA.loc[m, 'A'].map(find_anywhere)
最终输出:
A B
0 00281378554 AT411513000281378554
1 10862520000 AT424480010862520000
2 82540193700 AT742011182540193700
3 76015394900 AT531200076015394900
4 00134355050 AT511634000134355050
5 21864009 HU02142201082186400900000000