首页 > 解决方案 > 如何使用python中的迭代编写函数以通过2个数据帧获取值

问题描述

我有两个数据帧,长度为 2173 的 A 和长度为 6135 的 B。A 称为“file_name”,B 称为“crsp_name”

A.head()
                    file_name
0                       3M CO
1         ABBOTT LABORATORIES
2                     ABC INC
3            ALTRIA GROUP INC
4  AMERICAN ELECTRIC POWER CO
B.head()
0      A & E PLASTIK PAK INC
1    A & M FOOD SERVICES INC
2                 A A I CORP
3          A A IMPORTING INC
4                 A A R CORP
Name: comnam, dtype: object

我试图计算 A 中每个元素与 B 的 fuzz.token_set_ratio 并将它们组合为一个数据帧。例如,计算“3M CO”与 B 中每个元素的比率,并将“3M”和最大比率组合作为数据框中的一行。这样,我期望得到一个shape(2173,2)的数据框

这是我的函数,我尝试使用 'apply' 和我的 'fuzzratio' 函数来计算所需的比率。但是有一个错误,希望有人能给我一个正确的解决方案。

def fuzzratio(x):
    global crsp_name
    ratio_list = list()
    name_list = list()
    for i in crsp_name:
        ratio = fuzz.token_set_ratio(x,i)
        if ratio > 80:
            name_list.append(x)
            ratio_list.append(ratio)
    name_list = pd.DataFrame(name_list)
    result = pd.merge(name_list,ratio_list).rename(columns = {'comnam','ratio'})
    return result

file_name.apply(fuzzratio)

错误:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/frame.py", line 6004, in apply
    return op.get_result()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/apply.py", line 318, in get_result
    return super(FrameRowApply, self).get_result()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/apply.py", line 142, in get_result
    return self.apply_standard()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/apply.py", line 248, in apply_standard
    self.apply_series_generator()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/apply.py", line 277, in apply_series_generator
    results[i] = self.f(v)
  File "<stdin>", line 11, in fuzzratio
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/reshape/merge.py", line 60, in merge
    validate=validate)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pandas/core/reshape/merge.py", line 526, in __init__
    'type {right}'.format(right=type(right)))
ValueError: ("can not merge DataFrame with instance of type <class 'list'>", 'occurred at index file_name')

标签: pythonpandasdataframe

解决方案


我通过以下方式创建了测试数据:

dat_A="""file_name
3M CO
ABBOTT LABORATORIES
ABC INC
ALTRIA GROUP INC
AMERICAN ELECTRIC POWER CO"""

dat_B="""crsp_name
A & E PLASTIK PAK INC
A & M FOOD SERVICES INC
A A I CORP
A A IMPORTING INC
A A R CORP
ABBOTT
ABBOTT LABS
ALTRIA
ALTRIA GROUP"""

df_A = pd.read_csv(pd.compat.StringIO(dat_A))
df_B = pd.read_csv(pd.compat.StringIO(dat_B))

因为从df_B只使用了crsp_name列,出于效率原因,我将其提取到crsp变量中:

crsp = df_B.crsp_name

为了便于使用比率阈值进行操作,我将其保存为另一个变量:

ratio_threshold = 50

出于测试目的,我采用了较低的值以获得任何“非空”结果,但对于您的数据,将其设置回80(您定义的值)。

然后我们定义即将应用的函数:

def fn(t1):
    ratios = crsp.apply(lambda t2: fuzz.token_set_ratio(t1, t2))
    iMax = ratios.idxmax()
    rMax = ratios[iMax]
    return crsp.loc[iMax] if rMax > ratio_threshold else f'{iMax}_{rMax}'

此函数将应用于df_A.file_name中的每个字符串,因此t1是当前值 ( file_name )。

该函数从计算当前 file_name和每个crsp_name之间的令牌集比率开始(保存在ratios中)。

然后iMax被计算为最大比率和rMax的索引- 最大比率本身。

如果最大比率高于我们的阈值,该函数将返回相应的crsp_name

否则,该函数将返回“诊断消息” - x_y(2 个数字),其中x是找到最大比率的索引, y - 比率本身。在程序的最终版本中,将其更改为np.nan或空字符串。

唯一要做的就是应用此函数并将结果作为新列添加到df_A

df_A['crsp_name'] = df_A.file_name.apply(fn)

对于上述测试数据(和阈值 == 50),我得到:

                    file_name              crsp_name
0                       3M CO                   2_46
1         ABBOTT LABORATORIES                 ABBOTT
2                     ABC INC  A & E PLASTIK PAK INC
3            ALTRIA GROUP INC                 ALTRIA
4  AMERICAN ELECTRIC POWER CO                   1_43

如您所见,仍然有 2 种情况,crsp_name中没有位置给出的比率超过 50,但原因是源数据非常有限。

第二种解决方案

当任务是根据候选列表查找匹配项时,更好(更快)的解决方案是使用process.extractOne

由于它需要一个列表作为第二个参数,我将crsp提取到另一个变量中:

crspVals = crsp.values

要匹配的函数(仅返回最佳匹配)是:

def fn3(t1):
    res = process.extractOne(t1, crspVals, scorer=fuzz.token_set_ratio,
        score_cutoff=ratio_threshold)
    return res[0] if res else ''

process.extractOne返回一个元组(最佳匹配,比率),但是如果没有找到高于阈值的匹配,则结果为None,所以我决定放弃低于阈值的最佳比率,只返回最佳匹配或空字符串。

要应用此功能,请运行:

df_A['crsp_name'] = df_A.file_name.apply(fn3)

与第一个解决方案(修改为仅返回crsp_name)相比,它的运行速度快了近 2 倍。


推荐阅读