检查 pandas 列是否包含列表中的所有元素

Kau*_*ber 23 python pandas

我有一个这样的 df:

frame = pd.DataFrame({'a' : ['a,b,c', 'a,c,f', 'b,d,f','a,z,c']})
Run Code Online (Sandbox Code Playgroud)

以及项目清单:

letters = ['a','c']
Run Code Online (Sandbox Code Playgroud)

我的目标是从中获取frame至少包含 2 个元素的所有行letters

我想出了这个解决方案:

for i in letters:
    subframe = frame[frame['a'].str.contains(i)]
Run Code Online (Sandbox Code Playgroud)

这给了我想要的东西,但它可能不是可扩展性方面的最佳解决方案。有没有“矢量化”的解决方案?谢谢

Ser*_*sta 19

我会构建一个系列列表,然后应用矢量化np.all

contains = [frame['a'].str.contains(i) for i in letters]
resul = frame[np.all(contains, axis=0)]
Run Code Online (Sandbox Code Playgroud)

它按预期提供:

       a
0  a,b,c
1  a,c,f
3  a,z,c
Run Code Online (Sandbox Code Playgroud)


yat*_*atu 18

一种方法是使用 将列值拆分为列表str.split,并检查是否set(letters)subset获得的列表中的一个:

letters_s = set(letters)
frame[frame.a.str.split(',').map(letters_s.issubset)]

     a
0  a,b,c
1  a,c,f
3  a,z,c
?
Run Code Online (Sandbox Code Playgroud)

基准:

def serge(frame):
    contains = [frame['a'].str.contains(i) for i in letters]
    return frame[np.all(contains, axis=0)]

def yatu(frame):
    letters_s = set(letters)
    return frame[frame.a.str.split(',').map(letters_s.issubset)]

def austin(frame):
    mask =  frame.a.apply(lambda x: np.intersect1d(x.split(','), letters).size > 0)
    return frame[mask]

def datanovice(frame):
    s = frame['a'].str.split(',').explode().isin(letters).groupby(level=0).cumsum()
    return frame.loc[s[s.ge(2)].index.unique()]

perfplot.show(
    setup=lambda n: pd.concat([frame]*n, axis=0).reset_index(drop=True), 

    kernels=[
        lambda df: serge(df),
        lambda df: yatu(df),
        lambda df: df[df['a'].apply(lambda x: np.all([*map(lambda l: l in x, letters)]))],
        lambda df: austin(df),
        lambda df: datanovice(df),
    ],

    labels=['serge', 'yatu', 'bruno','austin', 'datanovice'],
    n_range=[2**k for k in range(0, 18)],
    equality_check=lambda x, y: x.equals(y),
    xlabel='N'
)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

  • @Datanovice 我认为你需要 python 3.8 为此:) (3认同)
  • 谢谢,我得到了与@Datanovice相同的错误,不幸的是无法跳转到python 3.8 (2认同)

Bru*_*llo 9

这也解决了:

frame[frame['a'].apply(lambda x: np.all([*map(lambda l: l in x, letters)]))]
Run Code Online (Sandbox Code Playgroud)


Aus*_*tin 8

您可以使用np.intersect1d

import pandas as pd
import numpy as np

frame = pd.DataFrame({'a' : ['a,b,c', 'a,c,f', 'b,d,f','a,z,c']})
letters = ['a','c']

mask =  frame.a.apply(lambda x: np.intersect1d(x.split(','), letters).size > 0)
print(frame[mask])

    a
0  a,b,c
1  a,c,f
3  a,z,c
Run Code Online (Sandbox Code Playgroud)


Man*_*ojK 7

使用set.issubset

frame = pd.DataFrame({'a' : ['a,b,c', 'a,c,f', 'b,d,f','a,z,c','x,y']})
letters = ['a','c']

frame[frame['a'].apply(lambda x: set(letters).issubset(x))]

Out:

       a
0  a,b,c
1  a,c,f
3  a,z,c
Run Code Online (Sandbox Code Playgroud)


Dat*_*ice 5

IIUCexplode和布尔过滤器

这个想法是创建一个单一的系列,然后我们可以通过索引进行分组,使用累积总和来计算列表的真实出现次数

s = frame['a'].str.split(',').explode().isin(letters).groupby(level=0).cumsum()

print(s)

0    1.0
0    1.0
0    2.0
1    1.0
1    2.0
1    2.0
2    0.0
2    0.0
2    0.0
3    1.0
3    1.0
3    2.0
Run Code Online (Sandbox Code Playgroud)
frame.loc[s[s.ge(2)].index.unique()]

out:

       a
0  a,b,c
1  a,c,f
3  a,z,c
Run Code Online (Sandbox Code Playgroud)