【问题标题】:pandas find strings in common among Seriespandas 在 Series 中找到共同的字符串
【发布时间】:2023-03-16 19:34:01
【问题描述】:

我从一个更大的 DataFrame 和一个 DataFrame 中提取了一系列关键字,其中包含一列字符串。我想掩盖 DataFrame 查找哪些字符串包含至少一个关键字。 “关键词”系列如下(怪词见谅):

Skilful
Wilful
Somewhere
Thing
Strange

DataFrame 如下所示:

User_ID;Tweet
01;hi all
02;see you somewhere
03;So weird
04;hi all :-)
05;next big thing
06;how can i say no?
07;so strange
08;not at all

到目前为止,我使用了来自 pandas 的 str.contains() 函数,例如:

mask = df['Tweet'].str.contains(str(Keywords['Keyword'][4]), case=False)

在DataFrame中找到“Strange”字符串并返回效果很好:

0    False
1    False
2    False
3    False
4    False
5    False
6     True
7    False
Name: Tweet, dtype: bool

我想做的是用所有关键字数组屏蔽整个 DataFrame,所以我可以有这样的东西:

0    False
1     True
2    False
3    False
4     True
5    False
6     True
7    False
Name: Tweet, dtype: bool

是否可以不循环遍历数组?在我的真实案例中,我必须搜索数百万个字符串,所以我正在寻找一种快速的方法。

感谢您的热心帮助。

【问题讨论】:

    标签: python string pandas


    【解决方案1】:
    import re
    df['Tweet'].str.match('.*({0}).*'.format('|'.join(phrases)))
    

    其中phrases 是一个可迭代的短语,您正在以它们的存在为条件。

    【讨论】:

    • 感谢 Alex,但我得到一个 AttributeError:'Series' 对象没有属性 'findall'。实际上 df['Tweet'] 是一系列字符串。
    • 您需要访问矢量化的str 方法,以便:df['Tweet'].str.findall(r'|'.join(phrases), flags=re.IGNORECASE).astype(bool) 应该可以工作
    • 哎呀,没有从我的 IDE 正确复制。很好的收获。
    • 是的,现在可以正常使用了,谢谢!即使在字符串中进行子字符串搜索!
    • @Alex,作为旁注,虽然当前的方法非常快,但如果您应用 compiled 正则表达式,它可能会受益更多。
    【解决方案2】:

    实现此目的的另一种方法是将 pd.Series.isin()ma​​papply 一起使用,您的示例将是喜欢:

    df    # DataFrame
    
       User_ID              Tweet
    0        1             hi all
    1        2  see you somewhere
    2        3           So weird
    3        4         hi all :-)
    4        5     next big thing
    5        6  how can i say no?
    6        7         so strange
    7        8         not at all
    

    w    # Series
    
    0      Skilful
    1       Wilful
    2    Somewhere
    3        Thing
    4      Strange
    dtype: object
    

    # list
    masked = map(lambda x: any(w.apply(str.lower).isin(x)), \                 
                 df['Tweet'].apply(str.lower).apply(str.split))
    
    df['Tweet_masked'] = masked
    

    结果:

    df
    Out[13]: 
       User_ID              Tweet Tweet_masked
    0        1             hi all        False
    1        2  see you somewhere         True
    2        3           So weird        False
    3        4         hi all :-)        False
    4        5     next big thing         True
    5        6  how can i say no?        False
    6        7         so strange         True
    7        8         not at all        False
    

    附带说明,isin 仅在整个字符串与值匹配时才有效,以防您只对 str.contains 感兴趣,这里是变体:

    masked = map(lambda x: any(_ in x for _ in w.apply(str.lower)), \
                 df['Tweet'].apply(str.lower))
    

    更新:正如@Alex 指出的那样,结合 map 和 regexp 可能会更有效,事实上我不太喜欢 ma​​p + lambda ,我们开始:

    import re
    
    r = re.compile(r'.*({}).*'.format('|'.join(w.values)), re.IGNORECASE)
    
    masked = map(bool, map(r.match, df['Tweet']))
    

    【讨论】:

    • 非常感谢大家!我真的很感谢你的帮助!我现在正在测试通过数百万个字符串快速搜索的方法。
    • @fblamanna,我认为速度方面,使用正则表达式会更快,因为我的方法需要 3 x apply 来转换字符串内容,效率方面,使用 map 将产生每个 lambdafindall 最终可能会最大化内存
    • 这行得通,但要非常小心,因为它们会随着数据大小的增长而减慢速度……正则表达式之所以如此之快,是因为它们基本上只是词法解析。如果您真的很担心内存使用情况(这里不应该是这种情况,因为您已经在内存中保存了一个大于或等于结果 df 的 DF)并且想要使用 apply,那么至少使用正则表达式在你的 lambda 函数中。
    • @Alex,好点子,让我更新并提供我们的组合
    【解决方案3】:

    一个简单的apply 可以解决这个问题。如果您可以忍受几秒钟的处理,我认为这是您无需冒险到外面pandas 即可使用的最简单方法。

    import pandas as pd
    
    df = pd.read_csv("dict.csv", delimiter=";")
    ref = pd.read_csv("ref.csv")
    
    kw = set([k.lower() for k in ref["Keywords"]])
    print kw
    
    boom = lambda x:True if any(w in kw for w in x.split()) else False
    
    df["Tweet"] = df["Tweet"].apply(boom)
    print df
    

    我针对 10,165,760 行虚构数据对其进行了测试,并在 18.9 秒内完成。如果这还不够快,则需要更好的方法。

    set(['somewhere', 'thing', 'strange', 'skilful', 'wilful'])
              User_ID  Tweet
    0               1  False
    1               2   True
    2               3  False
    3               4  False
    4               5   True
    5               6  False
    6               7   True
    7               8  False
    8               1  False
    9               2   True
    10              3  False
    11              4  False
    12              5   True
    13              6  False
    14              7   True
    15              8  False
    16              1  False
    17              2   True
    18              3  False
    19              4  False
    20              5   True
    21              6  False
    22              7   True
    23              8  False
    24              1  False
    25              2   True
    26              3  False
    27              4  False
    28              5   True
    29              6  False
    ...           ...    ...
    10165730        3  False
    10165731        4  False
    10165732        5   True
    10165733        6  False
    10165734        7   True
    10165735        8  False
    10165736        1  False
    10165737        2   True
    10165738        3  False
    10165739        4  False
    10165740        5   True
    10165741        6  False
    10165742        7   True
    10165743        8  False
    10165744        1  False
    10165745        2   True
    10165746        3  False
    10165747        4  False
    10165748        5   True
    10165749        6  False
    10165750        7   True
    10165751        8  False
    10165752        1  False
    10165753        2   True
    10165754        3  False
    10165755        4  False
    10165756        5   True
    10165757        6  False
    10165758        7   True
    10165759        8  False
    
    [10165760 rows x 2 columns]
    [Finished in 18.9s]
    

    希望这会有所帮助。

    【讨论】:

    • 谢谢!有用!但是我一直在使用 str.contains() 方法,因为有时我必须在 DataFrame 中搜索子字符串或单词片段。我现在正在尝试将这个方法放在繁荣功能中......
    猜你喜欢
    • 1970-01-01
    • 2022-01-23
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-24
    相关资源
    最近更新 更多