【问题标题】:Filter Dataframe by using ~isin([list_of_substrings])使用 ~isin([list_of_substrings]) 过滤数据框
【发布时间】:2020-05-28 13:52:43
【问题描述】:

给定一个充满电子邮件的数据框,我想过滤掉包含可能被阻止的域名或明显虚假电子邮件的行。下面的数据框代表我的数据示例。

>> print(df)

        email                number
1   fake@fake.com              2
2   real.email@gmail.com       1
3   no.email@email.com         5
4   real@yahoo.com             2  
5   rich@money.com             1            

我想按两个列表进行过滤。第一个列表是fake_lst = ['noemail', 'noaddress', 'fake', ... 'no.email']。 第二个列表只是将集合from disposable_email_domains import blocklist 转换为列表(或作为集合保留)。

当我使用df = df[~df['email'].str.contains('noemail')] 时,它可以正常工作并过滤掉该条目。然而,当我做df = df[~df['email'].str.contains(fake_lst)] 时,我得到TypeError: unhashable type: 'list'

显而易见的答案是在许多其他 stackoverflow 问题中使用 df = df[~df['email'].isin(fake_lst)],例如 Filter Pandas Dataframe based on List of substringspandas filtering using isin function,但最终没有任何效果。

我想我可以为每个可能的列表条目使用str.contains('string'),但这太麻烦了。

因此,我需要根据两个列表中包含的子字符串过滤此数据帧,以便删除包含我想要删除的特定子字符串的任何电子邮件,以及包含它的后续行。

在上面的例子中,过滤后的数据框是:

>> print(df)

        email                number
2   real.email@gmail.com       1
4   real@yahoo.com             2  
5   rich@money.com             1            

【问题讨论】:

标签: python pandas dataframe filter substring


【解决方案1】:

假设您关注dffake_lst,这是一个潜在的解决方案

df = pd.DataFrame({
    'email': ['fake@fake.com', 'real.email@gmail.com', 'no.email@email.com',
              'real@yahoo.com', 'rich@money.com'],
    'number': [2, 1, 5, 2, 1]
})

fake_lst = ['fake', 'money']

选项 1:

apply过滤掉电子邮件中包含任何fake_lst字样的行:

df.loc[
    ~df['email'].apply(lambda x: any([i in x for i in fake_lst]))
]
                  email  number
1  real.email@gmail.com       1
2    no.email@email.com       5
3        real@yahoo.com       2

选项 2:

过滤掉没有apply

df.loc[
    [not any(i) for i in zip(*[df['email'].str.contains(word) for word in fake_lst])]
]
                  email  number
1  real.email@gmail.com       1
2    no.email@email.com       5
3        real@yahoo.com       2

【讨论】:

  • 这很好用,但我尽量避免使用apply,因为在大 df 上它需要很长时间。
  • @DrakeMurdoch 我添加了另一个选项,这次没有使用apply
【解决方案2】:

使用DataFrame.isin 检查DataFrame 中的每个元素是否包含在值中。另一个问题是您的虚假列表包含没有域的名称,因此您需要 str.split 删除您不匹配的字符。

注意:str.contains 测试模式或正则表达式是否包含在 Series 的字符串中,因此您的代码 df['email'].str.contains('noemail') 工作正常,但不适用于 list

df[~df['email'].str.split('@').str[0].isin(fake_lst)]


    email                   number
0   fake@fake.com           2
1   real.email@gmail.com    1
3   real@yahoo.com          2
4   rich@money.com          1

【讨论】:

  • 它可能在实践中有效,但这不是问题所在。你假设假名总是出现在@之前,没有人说一定是这样。
  • pat = '|'.join(fake_lst); df[~df.email.str.contains(pat)]
猜你喜欢
  • 1970-01-01
  • 2017-06-06
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多