Как проверить, присутствуют ли все элементы в списке в столбце pandas

#python #python-3.x #pandas

#python #python-3.x #pandas

Вопрос:

У меня есть фрейм данных и список:

 df = pd.DataFrame({'id':[1,2,3,4,5,6,7,8], 
    'char':[['a','b'],['a','b','c'],['a','c'],['b','c'],[],['c','a','d'],['c','d'],['a']]})

names = ['a','c']
 

Я хочу получать строки, только если оба a и c оба присутствуют в char столбце. (порядок здесь не имеет значения)

Ожидаемый результат:

        char  id                                                                                                                      
1  [a, b, c]   2                                                                                                                      
2     [a, c]   3                                                                                                                      
5  [c, a, d]   6   
 

Мои усилия

 true_indices = []
for idx, row in df.iterrows():
    if all(name in row['char'] for name in names):
        true_indices.append(idx)


ids = df[df.index.isin(true_indices)]
 

Что дает мне правильный вывод, но он слишком медленный для большого набора данных, поэтому я ищу более эффективное решение.

Ответ №1:

Используйте pd.DataFrame.apply :

 df[df['char'].apply(lambda x: set(names).issubset(x))]
 

Вывод:

    id       char
1   2  [a, b, c]
2   3     [a, c]
5   6  [c, a, d]
 

Ответ №2:

Вы можете создать набор из списка имен для более быстрого поиска и использовать set.issubset для проверки, содержатся ли все элементы в наборе в списках столбцов:

 names = set(['a','c'])
df[df['char'].map(names.issubset)]

   id       char
1   2  [a, b, c]
2   3     [a, c]
5   6  [c, a, d]
 

Комментарии:

1. Это быстрее, чем rest. Спасибо 🙂

Ответ №3:

Используйте понимание списка с issubset :

 mask = [set(names).issubset(x) for x in df['char']]
df = df[mask]
print (df)
   id       char
1   2  [a, b, c]
2   3     [a, c]
5   6  [c, a, d]
 

Другое решение с Series.map :

 df = df[df['char'].map(set(names).issubset)]
print (df)
   id       char
1   2  [a, b, c]
2   3     [a, c]
5   6  [c, a, d]
 

Производительность зависит от количества строк и количества совпадающих значений:

 df = pd.concat([df] * 10000, ignore_index=True)

In [270]: %timeit df[df['char'].apply(lambda x: set(names).issubset(x))]
45.9 ms ± 2.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [271]: %%timeit
     ...: names = set(['a','c'])
     ...: [names.issubset(set(row)) for _,row in df.char.iteritems()]
     ...: 
46.7 ms ± 5.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


In [272]: %%timeit
     ...: df[[set(names).issubset(x) for x in df['char']]]
     ...: 
45.6 ms ± 1.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [273]: %%timeit
     ...: df[df['char'].map(set(names).issubset)]
     ...: 
18.3 ms ± 2.96 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [274]: %%timeit
     ...: n = set(names)
     ...: df[df['char'].map(n.issubset)]
     ...: 
16.6 ms ± 278 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [279]: %%timeit
     ...: names = set(['a','c'])
     ...: m = [name.issubset(i) for i in df.char.values.tolist()]
     ...: 
19.2 ms ± 317 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
 

Комментарии:

1. @yatu — хм, для меня нет, но реальные данные кажутся другими %%timeit names = set(['a','c']) m = [name.issubset(i) for i in df.char.values.tolist()] 19.2 ms ± 317 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Ответ №4:

Попробуйте это.

 df['char']=df['char'].apply(lambda x: x if ("a"in x and "c" in x) else np.nan)
print(df.dropna())
 

вывод:

    id       char
1   2  [a, b, c]
2   3     [a, c]
5   6  [c, a, d]