【问题标题】:How to add a pandas column based on partial string match?如何根据部分字符串匹配添加熊猫列?
【发布时间】:2020-07-12 09:43:02
【问题描述】:

我有一个熊猫数据框,其中包含各种尚未定义的类别(汽油、杂货、快餐等)的信用卡费用。

df1: 

Category   Date         Description                 Cost 
nan        7.1.20       Chipotle Downtown West      $8.23
nan        7.1.20       Break Time - Springfield    $23.57
nan        7.3.20       State Farm - Agent          $94.23
nan        7.3.20       T-Mobile                    $132.42
nan        7.4.20       Venmo -xj8382dzavvd         $8.00
nan        7.6.20       Broadway McDonald's         $11.73
nan        7.8.20       Break Time - Townsville     $44.23

我想维护第二个数据框,它在描述中搜索关键字并填充“类别”列。如下:

df2:

item           category
mcdonald       fast food
state farm     insurance
break time     gas
chipotle       fast food
mobile         cell phone 

这里的想法是,我将编写代码行来搜索 df1['Description'] 中的部分字符串,并使用 df2[category] 中的值填充 df1['Category']

我确信有一种干净且 Pythonic 的方式来处理这段代码,但下面是我能得到的最接近的方式。下面代码的错误结果是,df1['Category'] 中包含匹配项的所有行都设置为 df2 中的最后一个循环(例如,在这种情况下,所有行都将设置为“手机”)。

    for x in df2['item']:
        for y in df2['category']:
            df1['Category'] = np.where(
                        df1['Description'].str.lower().str.contains(x),
                        y,
                        df1['Category'])

感谢您的帮助!

【问题讨论】:

  • 如果我的解决方案对您有用,如果您将其标记为已接受的答案,我将不胜感激。如果它对您不起作用,请加入 cmets,我会帮助您到达您需要去的地方。

标签: python pandas


【解决方案1】:

您可以使用 map、Python 的内置 difflib 获取密切匹配函数和 lambda 表达式来做到这一点。 difflib 调用返回字符串匹配列表,您可以根据需要调整截止参数以获得或多或少的灵敏度。

import difflib

# you'll need to change both cutoff values here for the lambda to work correctly

df1['Category'] = df1['Description'].map(lambda x: difflib.get_close_matches(x, df2['item'], cutoff=0.3)[0] if len(difflib.get_close_matches(x, df2['item'], cutoff=0.3)) > 1 else 'no match')

print(df1)


    Category    Date    Description                 Cost
0   chipotle    7.1.20  Chipotle Downtown West      $8.23
1   break time  7.1.20  Break Time - Springfield    $23.57
2   state farm  7.3.20  State Farm - Agent          $94.23
3   mobile      7.3.20  T-Mobile                    $132.42
4   no match    7.4.20  Venmo -xj8382dzavvd         $8.00
5   mcdonald    7.6.20  Broadway McDonald's         $11.73
6   break time  7.8.20  Break Time - Townsville     $44.23

【讨论】:

  • 谢谢马修,感谢您的帮助——我整天都被困在这上面。我无法导入新库,因为我的工作计算机对下载有严格要求。所以我试图按照 for 循环的方式工作。我很困惑为什么我的循环不起作用。当我尝试删除嵌套的 for 循环(使用 y)并简单地创建一个掩码列(即 for x in...: df1['mask'] = np.where(...contains) 时,我什至失败了(x), x, np.nan)。但同样,np.where 似乎刷新了所有其他迭代,并且好像循环中的唯一项是最后一个实例。感谢任何帮助!
  • 嘿 Dylan,difflib 内置于 python 标准库中,因此您无需安装任何额外的库/软件。您的 for 循环不起作用主要是因为它在每一列上全部运行,因为 pandas 无法使用您的方法跟踪行/索引,这就是为什么 map/lambda 在这里是更好的方法。 FWIW,您通常希望避免使用 pandas 的 for 循环,但如果您必须使用一个,则需要使用 iterrows 或 iterrupltes。 (即对于 idx,df.iterrows():) 中的行,它允许您跟踪索引。
  • 我尝试了您的解决方案并收到以下错误:IndexError: list index out of range
  • 啊,这是因为你得到的行没有匹配,这意味着 difflib 返回一个空列表。我为 lambda 添加了一个条件,因此您将在该案例的类别列中获得“不匹配”。
猜你喜欢
  • 1970-01-01
  • 2016-10-18
  • 2022-09-23
  • 1970-01-01
  • 2017-11-17
  • 1970-01-01
  • 2019-05-17
  • 2020-09-21
  • 2017-09-22
相关资源
最近更新 更多