在不同大小的框B中检查框A的子串

1 投票
3 回答
58 浏览
提问于 2025-04-12 17:23

我有一个问题,希望你能帮我解决。我有两个数据框,每个数据框都有很多列,但为了简单起见,我们只关注每个数据框的一列。重要的是:这两个数据框的大小不同,数据框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

撰写回答