【问题标题】:String contains between two df in python字符串包含在python中的两个df之间
【发布时间】:2019-04-29 01:17:03
【问题描述】:

我有两个带有两个字符串列的df,如下所示:

Df1:原始 df 有 2000 行 Names

Id    Name
1     Paper
2     Paper
3     Scissors
4     Mat
5     Cat
6     Cat

第二个 Df:原始 df 有 1000+ Item_Name

Item_ID   Item_Name
1         Paper Bag
2         wallpaper
3         paper
4         cat cage

我需要列在 Item_Name

中的 Name 列中的字符串

第一种方法:使用str.contains

如果它们是一列和几个要匹配的字符串,我知道如何匹配字符串,如下所示:

df[df['Name'].str.contains("paper|cat", na=False)]

但是当有两列字符串(name & Item_name)要匹配时怎么办

第二种方法:Fuzzywuzzy

matched = []
for row in df1.index:
    name = df1.get_value(row,"Name")
    for columns in df2.index:
        item_name=df2.get_value(columns,"Item_Name")
        matched_token=fuzz.token_sort_ratio(name,item_name)
        if matched_token> 80:
            matched.append([name,item_name,matched_token])

问题是,它会很慢,而且我想要的输出比从fuzzywuzzy 得到的要少得多。输出如下:

Id Name     Item_ID
1  Paper     1,2,3
2  Paper     1,2,3
3  Scissors  NA 
4  Mat       NA 
5  Cat       4
6  Cat       4 

总结

  1. 如果 str.contains 是两个列名不同的 df,如何处理
  2. 如何转换 df 以获得上述预期输出

【问题讨论】:

    标签: python string pandas performance string-matching


    【解决方案1】:

    您可以将pd.Series.apply 与自定义函数一起使用:

    def matcher(x):
        res = df2.loc[df2['Item_Name'].str.contains(x, regex=False, case=False), 'Item_ID']
        return ','.join(res.astype(str))
    
    df1['Item_ID'] = df1['Name'].apply(matcher)
    
    print(df1)
    
       Id      Name Item_ID
    0   1     Paper   1,2,3
    1   2     Paper   1,2,3
    2   3  Scissors        
    3   4       Mat        
    4   5       Cat       4
    5   6       Cat       4
    

    有一些方法可以提高效率:

    • 仅处理 df1['Name'] 中的唯一项目:apply 是逐行循环。
    • 使用列表推导代替pd.Series.apply。两者都是 Python 级别的循环,但列表推导通常优于 Pandas str 方法。

    但以上并没有提高算法的复杂性。要获得数量级更好的改进,您应该考虑使用基于 trie 的算法,例如使用 Aho–Corasick algorithmthis answer

    【讨论】:

    • 您的 item_ID 列是字符串
    • @min2bro,是的,Item_ID 在 OP 的期望输出中也是如此。
    • @jpp:我会试试这个并回复你
    • Since you mentioned that it works only on unique。不,这不是我的帖子所说的。它说如果您想提高性能,请先删除重复值。我无法用matching with different Item_ID 复制您的问题。
    • @RahulAgarwal,不幸的是,我认为这超出了调整范围,最好提出一个新问题(用一个完整的例子让人们理解你的意思!)。
    【解决方案2】:

    您可以在此处使用df.apply

    def get_vals(df):
        return ','.join(map(str, df2.loc[df2['Item_Name'].str.contains(df['Name'], case=False),]['Item_ID'].tolist()))
    
    df1['Item_ID'] = df1.apply(get_vals, axis=1)
    

    输出:

    Id     Name  Item_ID
    1     Paper   1,2,3
    2     Paper   1,2,3
    3  Scissors        
    4       Mat        
    5       Cat       4
    6       Cat       4
    

    相信这会给你想要的结果

    【讨论】:

    • 得到一个错误:TypeError: ("'Series' 对象是可变的,因此它们不能被散列",你'发生在索引 0')
    【解决方案3】:
    df=pd.DataFrame({'ID':[1,2,3,4,5,6],'Name':['paper','paper','scissors','mat','cat','cat']})
    df1=pd.DataFrame({'ID':[1,2,3,4],'Name':['paper bag','wallpaper','paper','cat cage']})
    
    
    import numpy as np
    def lookup_prod(ip):
        lst=[]
        for idx,row in df1.iterrows():
            if ip in row['Name']:
                lst.append(row['ID'])    
        if not lst:
            return np.NaN
        return lst
    
    df['Item_ID'] = df['Name'].apply(lookup_prod)
    

    输出:

     ID  Name         Item_ID
    0   1   paper     [1, 2, 3]
    1   2   paper     [1, 2, 3]
    2   3   scissors    NaN
    3   4   mat         NaN
    4   5   cat         [4]
    5   6   cat         [4]
    

    【讨论】:

    • 它给了我作为 Nan 的所有行。在您的示例中,您将两个 df 的列名视为相同。你能改一下吗,这样我就可以理解你在代码中指的是哪个 ID 和哪个名称
    猜你喜欢
    • 2021-12-15
    • 1970-01-01
    • 2016-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-20
    • 2016-11-20
    相关资源
    最近更新 更多