【问题标题】:Vectorized/Matrix calculation between 2 Pandas dataframe columns2个熊猫数据框列之间的矢量化/矩阵计算
【发布时间】:2014-02-16 00:52:44
【问题描述】:

我正在使用 difflib 比率来计算 2 个字符串之间的相似度:

ratio = difflib.SequenceMatcher(None, 'string1', 'string2').ratio()

输出是一个从 0 到 1 的浮点值,可以解释为匹配分数。

我要做的是创建一个列,其中包含基于max(ratio) 的值与其他值列表之间的最佳匹配。

如果:

df.col1 = 'maria','fred','john'

和:

df2.col1 = 'mary','orange','maria'

df.bestmatch 将包含基于df2.col1 值的'maria', 'fred' and 'john' 的最佳匹配。

我觉得使用.apply 方法可以做到这一点,但我只是不知道如何计算df.col1 中的每个值与df2.col1 的比较。

更新:difflib.get_close_matches 方法能够更好地处理大型数组,并且给了我想要的一切,除了比率分数(没什么大不了的)。下面汤姆的回答适用于较小的数据集,但当每列约为 19,000 个值时会出现 MemoryError。

【问题讨论】:

    标签: python string pandas difflib


    【解决方案1】:

    根据您的评论编辑:

    In [164]: df = pd.DataFrame({'col1': ['maria','fred','john'], 'col2': ['mary','orange','maria']})
    

    制作所有组合(玛丽亚,玛丽),(玛丽亚,橙色),(玛丽亚,玛丽亚),(弗雷德......)

    In [165]: combos = itertools.product(df.col1, df.col2)
    

    combos 将是一个像('maria', 'mary') ..., 9 这样的元组的平面列表。由于我们需要每个名称的最佳匹配,我们需要按来自col1 的名称对元组进行分组。

    In [166]: groups = [list(g) for k, g in itertools.groupby(combos, lambda x: x[0])]
    

    现在我们有一个包含三个列表的列表:[[('maria', 'mary'), ('maria', 'orange'), ('maria', 'maria')], [...]]groupby 的第二个参数是分解组的键。查看itertools docs

    In [167]: groups
    Out[167]: 
    [[('maria', 'mary'), ('maria', 'orange'), ('maria', 'maria')],
     [('fred', 'mary'), ('fred', 'orange'), ('fred', 'maria')],
     [('john', 'mary'), ('john', 'orange'), ('john', 'maria')]]
    

    定义一个辅助函数:

    def get_best(group):
        k = group[0][0]
        ratios = {x[1]: difflib.SequenceMatcher(None, *x).ratio() for x in group}
        winner = max(ratios.iteritems(), key=lambda x: x[1])
        return winner[1] # mess with this to return original name, mathcihng name, ratio
    

    这是您将应用于groups 中每个列表的函数。就像我们之前将货币对交给SequenceMatcher 以获得比率一样。只是现在我们需要保留这个名字。所以在那个函数中x 是一个像('maria', 'mary') 这样的元组。我们需要知道最佳匹配中的名称和最佳匹配的比例,所以我将它们放入带有{name: ratio} 的字典中。这里的另一件事是max 接受第二个参数。这次只是说最大化的东西是x[1],比率。

    并获得最佳匹配:

    In [173]: best = [get_best(group) for group in groups]
    
    In [175]: df['best_match'] = best
    
    In [176]: df
    Out[176]: 
        col1    col2 best_match
    0  maria    mary      maria
    1   fred  orange     orange
    2   john   maria     orange
    
    [3 rows x 3 columns]
    

    这应该是相当有效的。

    【讨论】:

    • 嘿汤姆,这让我更接近 - 但我追求的是具有最高比率的 col2 值。例如,maria 将针对 mary、orange、maria 和 sam 进行评估,然后根据比率分数,将返回 maria。下一个 fred 将根据相同的值进行评估,并返回得分最高的值,依此类推。那有意义吗?这可能可以通过一些丑陋的嵌套 for 循环来完成,但我希望有一个更优雅/高效的解决方案。
    • 哇,这很复杂。没有更简单的方法吗?使用我不理解 90% 正在发生的事情的代码时,我感到不舒服......
    • 刚刚添加了一些 cmets。希望他们有所帮助!可能有一种更简单的方法,但是当您分解问题时,我认为这里的一切都是必需的。首先创建所有匹配项的列表 (combos)。然后按名称从df.col1 (groups) 分解该列表。最后,获取与每个名称最匹配的名称和比例 (get_best)。 [173] 行可以用 df.col1.apply(get_best) 完成。
    • 嘿汤姆,我开始尝试您的解决方案,但在创建组对象时遇到了 MemoryError。作为参考,每个 df 列包含大约 15,000 个值...
    • 我刚刚发现 difflib.get_close_matches 方法几乎可以满足我的需求。
    猜你喜欢
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    • 2015-08-09
    • 1970-01-01
    • 2020-05-18
    • 2018-02-05
    • 1970-01-01
    • 2015-01-28
    相关资源
    最近更新 更多