【问题标题】:How to compare a value in one dataframe to a column in another using fuzzywuzzy ratio如何使用fuzzywuzzy比率将一个数据框中的值与另一个数据框中的列进行比较
【发布时间】:2019-12-12 20:30:55
【问题描述】:

我有一个数据帧df_sample,其中包含 10 个已解析地址,我将它与另一个包含数十万已解析地址记录 df 的数据帧进行比较。 df_sampledf 共享完全相同的结构:

zip_code     city        state     street_number    street_name   unit_number   country
 12345    FAKEVILLE     FLORIDA          123           FAKE ST        NaN          US

我想要做的是将df_sample 中的一行与df 中的每一行匹配,从state 开始,并且只将fuzzy.ratio(df['state'], df_sample['state']) > 0.9 所在的行放入一个新的数据帧中。一旦从这些匹配中创建了这个新的、更小的数据框,我将继续为cityzip_code 等执行此操作。类似于:

df_match = df[fuzzy.ratio(df_sample['state'], df['state']) > 0.9]

除非那行不通。

我的目标是在每次使用更难的搜索条件时缩小匹配的数量,并最终在逐列缩小范围的基础上得到一个匹配尽可能少的数据框。但我不确定如何对任何单个记录执行此操作。

【问题讨论】:

  • 交叉连接。我们正在查看大约几百万行数据,这应该是可能的。
  • @QuangHoang 在这种情况下我该怎么做?

标签: python pandas dataframe match fuzzywuzzy


【解决方案1】:

创建您的数据框

import pandas as pd
from fuzzywuzzy import fuzz 

df = pd.DataFrame({'key': [1, 1, 1, 1, 1],
                   'zip': [1, 2, 3, 4, 5],
                   'state': ['Florida', 'Nevada', 'Texas', 'Florida', 'Texas']})

df_sample = pd.DataFrame({'key': [1, 1, 1, 1, 1],
                          'zip': [6, 7, 8, 9, 10],
                          'state': ['florida', 'Flor', 'NY', 'Florida', 'Tx']})

merged_df = df_sample.merge(df, on='key')
merged_df['fuzzy_ratio'] = merged_df.apply(lambda row: fuzz.ratio(row['state_x'], row['state_y']), axis=1)
merged_df

你得到每对的模糊比率

    key  zip_x  state_x  zip_y  state_y  fuzzy_ratio
0     1      6  florida      1  Florida           86
1     1      6  florida      2   Nevada           31
2     1      6  florida      3    Texas           17
3     1      6  florida      4  Florida           86
4     1      6  florida      5    Texas           17
5     1      7     Flor      1  Florida           73
6     1      7     Flor      2   Nevada            0
7     1      7     Flor      3    Texas            0
8     1      7     Flor      4  Florida           73
9     1      7     Flor      5    Texas            0
10    1      8       NY      1  Florida            0
11    1      8       NY      2   Nevada           25
12    1      8       NY      3    Texas            0
13    1      8       NY      4  Florida            0
14    1      8       NY      5    Texas            0
15    1      9  Florida      1  Florida          100
16    1      9  Florida      2   Nevada           31
17    1      9  Florida      3    Texas           17
18    1      9  Florida      4  Florida          100
19    1      9  Florida      5    Texas           17
20    1     10       Tx      1  Florida            0
21    1     10       Tx      2   Nevada            0
22    1     10       Tx      3    Texas           57
23    1     10       Tx      4  Florida            0
24    1     10       Tx      5    Texas           57

然后过滤掉你不想要的东西

mask = (merged_df['fuzzy_ratio']>80)
merged_df[mask]

结果:

    key  zip_x  state_x  zip_y  state_y  fuzzy_ratio
0     1      6  florida      1  Florida           86
3     1      6  florida      4  Florida           86
15    1      9  Florida      1  Florida          100
18    1      9  Florida      4  Florida          100

【讨论】:

  • 它们是您添加的完全任意且仅用于初始合并的键吗?
【解决方案2】:

我不熟悉fuzzy,所以这更像是一个评论而不是一个答案。也就是说,您可以执行以下操作:

# cross join
df_merge = pd.merge(*[d.assign(dummy=1) for d in (df, df_sample)],
                    on='dummy', how='left'
                   )

filters = pd.DataFrame()

# compute the fuzzy ratio for each pair of columns
for col in df.columns:
    filters[col] = (df_merge[[col+'_x', col+'_y']]
                       .apply(lambda x: fuzzy.ratio(x[col+'_x'], x[col+'_y']), axis=1) 
                   )

# filter only those with ratio > 0.9
df_match = df_merge[filter.gt(0.9).all(1)]

【讨论】:

  • 你知道为什么我会在filters[col] = fuzzy.ratio(df_merge[col+'_x'], df_merge[col+'_y']) 的行上得到错误ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). 吗? fuzzy.ratio(A,B) 只是将相似率输出为浮点数 0
【解决方案3】:

您写道,您的 df 行数非常多, 所以完全交叉加入然后消除可能会导致您的代码 内存不足。

看看另一种解决方案,需要更少的内存:

minRatio = 90
result = []
for idx1, t1 in df_sample.state.iteritems():
    for idx2, t2 in df.state.iteritems():
        ratio = fuzz.WRatio(t1, t2)
        if ratio > minRatio:
            result.append([ idx1, t1, idx2, t2, ratio ])
df2 = pd.DataFrame(result, columns=['idx1', 'state1', 'idx2', 'state2', 'ratio'])

它包含在两个 DataFrame 上运行的 2 个嵌套循环。 结果是一个包含行的 DataFrame:

    df_sample中的
  • 索引和状态
  • 索引和状态来自df
  • 比率。

这为您提供了两个 DataFrame 中的哪些行是“相关”的信息 彼此。

优点是您不会生成完整的交叉连接和(目前) 您只对 state 列进行操作,而不是整行。

你没有描述最终结果应该是什么,但我觉得 根据上面的代码,您将能够进一步进行。

【讨论】:

    猜你喜欢
    • 2018-03-14
    • 1970-01-01
    • 2020-08-13
    • 2020-03-20
    • 1970-01-01
    • 2018-01-03
    • 2021-12-19
    • 1970-01-01
    • 2021-10-27
    相关资源
    最近更新 更多