Kiểm tra xem cột gấu trúc có chứa tất cả các thành phần từ danh sách không


20

Tôi có một df như thế này:

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

Và một danh sách các mặt hàng:

letters = ['a','c']

Mục tiêu của tôi là có được tất cả các hàng từ frame đó chứa ít nhất 2 phần tử trongletters

Tôi đã đưa ra giải pháp này:

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

Điều này mang lại cho tôi những gì tôi muốn, nhưng nó có thể không phải là giải pháp tốt nhất về khả năng mở rộng. Có giải pháp "véc tơ" nào không? Cảm ơn


4
Nó sẽ chỉ cung cấp cho bạn các hàng có chứa chữ cái cuối cùng vì bạn ghi đè khung con trong bất kỳ lần lặp nào
Tom Ron

@TomRon Bạn nói đúng, thật là sai lầm :)
Kauber

Câu trả lời:


12

Tôi sẽ xây dựng một danh sách Sê-ri, và sau đó áp dụng một vectơ np.all:

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

Nó cho như mong đợi:

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

3
chúc mừng 100k!
Peter Haddad

14

Một cách là chia các giá trị cột thành các danh sách bằng cách sử dụng str.splitvà kiểm tra xem có phải set(letters)là một subsettrong các danh sách thu được không:

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

Điểm chuẩn:

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

nhập mô tả hình ảnh ở đây


Tôi nhận được TypeError: unhashable type: 'set'khi tôi chạy mã của bạn? chạy nó trên khung aboe được cung cấp
Datanovice

Phiên bản nào? @Datanovice Kiểm tra hai lần và tất cả có vẻ ổn
yatu

gấu trúc của tôi là 1.0.3và trăn 3.7có lẽ chỉ là tôi
Datanovice

3
@Datanovice tôi nghĩ bạn cần python 3.8 cho việc này :)
anky

2
Cảm ơn, tôi gặp lỗi tương tự như @Datanovice và không thể nhảy lên python 3.8
Kauber

7

Bạn có thể sử dụng 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

Điều này cũng giải quyết nó:

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

6

Sử dụng 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 và bộ lọc boolean

ý tưởng là tạo ra một chuỗi duy nhất sau đó chúng ta có thể nhóm chỉ số đếm số lần xuất hiện thực sự của danh sách của bạn bằng cách sử dụng tổng cộng

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

đầu ra:

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

thời gian

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

đầu ra

300 µs ± 32.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.