팬더 열에 목록의 모든 요소가 포함되어 있는지 확인


20

다음과 같은 df가 있습니다.

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

그리고 아이템 목록 :

letters = ['a','c']

내 목표는 frame적어도 두 요소를 포함하는 모든 행을 얻는 것 입니다.letters

이 솔루션을 생각해 냈습니다.

for i in letters:
    subframe = frame[frame['a'].str.contains(i)]

이것은 내가 원하는 것을 제공하지만 확장 성 측면에서 가장 좋은 해결책은 아닙니다. '벡터화 된'솔루션이 있습니까? 감사


4
반복에서 서브 프레임을 대체하기 때문에 마지막 문자를 포함하는 행만 제공합니다.
Tom Ron

@TomRon 당신이 맞아요, 얼마나 실수 :)
카우 버

답변:


12

Series 목록을 작성한 다음 벡터화를 적용합니다 np.all.

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

예상대로 제공됩니다.

       a
0  a,b,c
1  a,c,f
3  a,z,c

3
100k 축하합니다!
Peter Haddad

14

한 가지 방법은을 사용하여 열 값을 목록으로 나누고 얻은 목록 중 하나 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

기준:

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'
)

여기에 이미지 설명을 입력하십시오


TypeError: unhashable type: 'set'코드를 실행할 때 얻을 수 있습니까? 제공된 프레임 aboe에서 실행
Datanovice

어떤 버전? @Datanovice 두 번 확인하고 모두 괜찮아 보인다
yatu

내 팬더는 1.0.3파이썬은 3.7아마도
나일

3
@ Datanovice 나는 이것을 위해 파이썬 3.8이 필요하다고 생각한다 :)
anky

2
감사합니다, 나는 @Datanovice과 동일한 오류가 불행하게도 파이썬 3.8로 이동할 수 없습니다
Kauber

7

당신은 사용할 수 있습니다 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

7

이것은 또한 그것을 해결합니다 :

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

6

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

5

IIUC explode및 부울 필터

아이디어는 단일 시리즈를 만드는 것입니다. 그러면 누적 합계를 사용하여 목록의 실제 발생 횟수를 색인별로 그룹화 할 수 있습니다

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

frame.loc[s[s.ge(2)].index.unique()]

out:

       a
0  a,b,c
1  a,c,f
3  a,z,c

1
frame.iloc[[x for x in range(len(frame)) if set(letters).issubset(frame.iloc[x,0])]]

산출:

        a
 0  a,b,c
 1  a,c,f
 3  a,z,c

timeit

%%timeit
#hermes
frame.iloc[[x for x in range(len(frame)) if set(letters).issubset(frame.iloc[x,0])]]

산출

300 µs ± 32.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.