【问题标题】:How to merge on approximate strings?如何合并近似字符串?
【发布时间】:2019-02-02 14:51:33
【问题描述】:

我想将大约国家名称上的 2 个数据框与合并为的合并,但我收到以下错误:

TypeError:'NoneType' 对象不可调用

请看下面的说明性代码:

cl =  {'Country' : ["Brazil", "US", "Russia"], 'BL?':['No', 'No','Yes']}
clist = pd.DataFrame.from_dict(cl)

cd = {'Country' : ["Braizl", "us", "Rusia"]}
cdata  = pd.DataFrame.from_dict(cd)

clist = clist.sort_values('Country')
cdata = cdata.sort_values('Country')


cdata = pd.merge_asof(cdata,clist,on='Country')  

预期结果将合并两个 dfs,而 cdata df 将具有“BL?”带有 YES/NO 值的列。

提前谢谢你!

【问题讨论】:

  • inplace=True 使 sort_values 返回 None,因为您指定了就地执行此操作的方法
  • 是的,我得到一个非类型对象在删除就地后现在不可调用
  • 我已经编辑了代码
  • 我认为merge_asof 仅适用于整数。如果您不想找到最接近的字符串,则可以使用 levenshtein 距离,但我不知道 Pandas 库中有任何实现。
  • 如何将它应用到 2 个字典?

标签: python pandas


【解决方案1】:

这应该会让你接近,但它不会 100% 准确。您可以使用fuzzywuzzyfuzzywuzzy 使用 Levenshtein 距离计算两个字符串之间的差异:

from fuzzywuzzy import process

# create a choice list
choices = clist['Country'].values.tolist()

# apply fuzzywuzzy to each row using lambda expression
cdata['Close Country'] = cdata['Country'].apply(lambda x: process.extractOne(x, choices)[0])

# merge
cdata.merge(clist, left_on='Close Country', right_on='Country')


  Country_x Close Country Country_y  BL?
0    Braizl        Brazil    Brazil   No
1     Rusia        Russia    Russia  Yes
2        us            US        US   No

你甚至可以返回匹配百分比并且只保留值 > n 如果你只想保持匹配说大于 85%

添加百分比匹配

from fuzzywuzzy import process

# create a choice list
choices = clist['Country'].values.tolist()

# apply fuzzywuzzy to each row using lambda expression
cdata['Close Country'] = cdata['Country'].apply(lambda x: process.extractOne(x, choices))

# add percent match wiht apply
cdata[['Close Country', 'Percent Match']] = cdata['Close Country'].apply(pd.Series)

# merge
cdata.merge(clist, left_on='Close Country', right_on='Country')

  Country_x Close Country  Percent Match Country_y  BL?
0    Braizl        Brazil             83    Brazil   No
1     Rusia        Russia             91    Russia  Yes
2        us            US            100        US   No

您可以在合并之前进行布尔索引以删除错误匹配然后合并:

cdata[['Close Country', 'Percent Match']] = cdata['Close Country'].apply(pd.Series)
cdata = cdata[cdata['Percent Match']>85]

或者你可以在合并后进行:

merge = cdata.merge(clist, left_on='Close Country', right_on='Country')
merge[merge['Percent Match'] > 85]

fuzzywuzzy 返回匹配百分比作为process 函数的一部分。在第一个示例中,我通过调用元组的第一个元素来删除它:process.extractOne(x, choices)[0]

【讨论】:

  • 太棒了!非常感谢!
  • 如何返回匹配百分比?
  • @TPguru 你想要它作为一个新列吗?
  • 是的,那太好了
  • 尝试将from fuzzywuzzy import process 替换为from rapidfuzz import process。您会看到执行时间大幅缩短,结果相同
【解决方案2】:

根据您的示例,我找到了解决方案。这不是很pythonic,但它有效! (假设每个cdata 拼错的国家/地区都有一个匹配的国家/地区名称)

def get_closest(x, column):
    tmp = 1000
    for i2, r2 in clist.iterrows():
        levenshtein = editdistance.eval(x,r2['Country'])
        if levenshtein <= tmp:
            tmp = levenshtein
            res = r2

    return res['BL?']

cdata['BL'] = cdata['Country'].apply(lambda x: get_closest(x, clist))

输出:

   Country   BL
0  Braizl   No
1      us   No
2   Rusia  Yes

我正在使用editdistance 库来计算 levenshtein 距离。 您可以使用 pip 安装它:

pip install editdistance

【讨论】:

  • 应该有两个。你能发布你的代码是什么样的吗?
猜你喜欢
  • 1970-01-01
  • 2011-05-11
  • 2019-02-21
  • 2013-07-10
  • 1970-01-01
  • 2010-09-08
  • 2016-03-28
  • 2015-02-07
  • 2017-04-26
相关资源
最近更新 更多