【问题标题】:Sequences’ Similarity Matching in PandasPandas 中序列的相似性匹配
【发布时间】:2020-02-03 15:22:42
【问题描述】:

我尝试在 SO 中搜索答案,但没有找到任何帮助。

这是我想要做的:
我有一个数据框(这里是一个小例子):

 df = pd.DataFrame([[1, 5, 'AADDEEEEIILMNORRTU'], [2, 5, 'AACEEEEGMMNNTT'], [3, 5, 'AAACCCCEFHIILMNNOPRRRSSTTUUY'], [4, 5, 'DEEEGINOOPRRSTY'], [5, 5, 'AACCDEEHHIIKMNNNNTTW'], [6, 5, 'ACEEHHIKMMNSSTUV'], [7, 5, 'ACELMNOOPPRRTU'], [8, 5, 'BIT'], [9, 5, 'APR'], [10, 5, 'CDEEEGHILLLNOOST'], [11, 5, 'ACCMNO'], [12, 5, 'AIK'], [13, 5, 'CCHHLLOORSSSTTUZ'], [14, 5, 'ANNOSXY'], [15, 5, 'AABBCEEEEHIILMNNOPRRRSSTUUVY']],columns=['PartnerId','CountryId','Name'])

我的目标是找到Name 至少与某个ratio 相似的PartnerIds。
另外,我只想比较具有相同CountryIdPartnerIds。匹配的PartnerIds 应附加到列表中,最后写入数据帧的新列中。

这是我的尝试:

itemDict = {item[0]: {'CountryId': item[1], 'Name': item[2]} for item in df.values}

from difflib import SequenceMatcher
def similar(a, b):
    return SequenceMatcher(None, a, b).ratio()

def calculate_similarity(x,itemDict):
    own_name = x['Name']
    country_id = x['CountryId']
    matching_ids = []
    for k, v in itemDict.items():

        if k != x['PartnerId']:
            if v['CountryId'] == country_id:

                ratio = similar(own_name,v['Name'])


                if ratio > 0.7:

                    matching_ids.append(k)
    return matching_ids

df['Similar_IDs'] = df.apply(lambda x: calculate_similarity(x,itemDict),axis=1)
print(df)

输出是:

    PartnerId  CountryId                          Name Similar_IDs
0           1          5            AADDEEEEIILMNORRTU          []
1           2          5                AACEEEEGMMNNTT          []
2           3          5  AAACCCCEFHIILMNNOPRRRSSTTUUY        [15]
3           4          5               DEEEGINOOPRRSTY        [10]
4           5          5          AACCDEEHHIIKMNNNNTTW          []
5           6          5              ACEEHHIKMMNSSTUV          []
6           7          5                ACELMNOOPPRRTU          []
7           8          5                           BIT          []
8           9          5                           APR          []
9          10          5              CDEEEGHILLLNOOST         [4]
10         11          5                        ACCMNO          []
11         12          5                           AIK          []
12         13          5              CCHHLLOORSSSTTUZ          []
13         14          5                       ANNOSXY          []
14         15          5  AABBCEEEEHIILMNNOPRRRSSTUUVY         [3]

我现在的问题是:
1.) 有没有更有效的计算方法?我现在有大约 20.000 行,在不久的将来还会更多。
2.) 是否有可能“摆脱” itemDict 并直接从数据框中进行操作?
3.) 使用另一种距离度量可能更好吗?

非常感谢您的帮助!

【问题讨论】:

    标签: python pandas similarity


    【解决方案1】:

    您可以使用模块difflib。首先,您需要通过使用外连接将表连接到自身来生成所有字符串的笛卡尔积:

    cols = ['Name', 'CountryId', 'PartnerId']
    df = df[cols].merge(df[cols], on='CountryId', how='outer')    
    df = df.query('PartnerId_x != PartnerId_y')
    

    在下一步中,您可以应用此 answer 中的函数并过滤掉所有匹配项:

    def match(x):
        return SequenceMatcher(None, x[0], x[1]).ratio()
    
    match = df.apply(match, axis=1) > 0.7
    df.loc[match, ['PartnerId_x', 'Name_x', 'PartnerId_y']]
    

    输出:

         PartnerId_x                        Name_x  PartnerId_y
    44             3  AAACCCCEFHIILMNNOPRRRSSTTUUY           15
    54             4               DEEEGINOOPRRSTY           10
    138           10              CDEEEGHILLLNOOST            4
    212           15  AABBCEEEEHIILMNNOPRRRSSTUUVY            3
    

    如果您没有足够的内存,您可以尝试遍历数据框的行:

    lst = []
    for idx, row in df.iterrows():
        if SequenceMatcher(None, row['Name_x'], row['Name_y']).ratio() > 0.7:
            lst.append(row[['PartnerId_x', 'Name_x', 'PartnerId_y']])
    
    pd.concat(lst, axis=1).T
    

    【讨论】:

    • 目前看起来不错!在您的描述中,您正在编写“内部联接”,但之后使用“外部”联接。反正我觉得没关系!
    • @mgruber 我的意思是自我加入。谢谢指正。
    • 我尝试将代码用于我的 19.053 行,结果得到了 MemoryError
    • @mgruber 看起来你没有足够的内存。
    • @MykolaZotko 合并大约需要 1 分钟。在匹配过程中抛出错误。合并数据帧的长度是 131.044.638,这对于我的“可怜”机器来说显然是很重要的。我考虑过是否有一种方法可以先根据 CountryId 和 Name 对 df 进行排序,然后仅将其与上下 5 个条目合并。其他 >19k 行无论如何都不会匹配。
    猜你喜欢
    • 2019-03-09
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多