У меня есть DataFrame pandas, который выглядит так:
molecule species
0 a [dog]
1 b [horse, pig]
2 c [cat, dog]
3 d [cat, horse, pig]
4 e [chicken, pig]
и мне нравится извлекать DataFrame, содержащий только те строки, которые содержат любую из selection = ['cat', 'dog']. Итак, результат должен выглядеть так:
molecule species
0 a [dog]
1 c [cat, dog]
2 d [cat, horse, pig]
Как это сделать проще всего?
Для тестирования:
selection = ['cat', 'dog']
df = pd.DataFrame({'molecule': ['a','b','c','d','e'], 'species' : [['dog'], ['horse','pig'],['cat', 'dog'], ['cat','horse','pig'], ['chicken','pig']]})






Вы можете использовать mask с apply здесь.
selection = ['cat', 'dog']
mask = df.species.apply(lambda x: any(item for item in selection if item in x))
df1 = df[mask]
Для DataFrame, который вы предоставили в качестве примера выше, df1 будет:
molecule species
0 a [dog]
2 c [cat, dog]
3 d [cat, horse, pig]
Учитывая, что @NicoH ищет «кошку» или «собаку», я бы порекомендовал сменить маску на эту mask = df.species.apply(lambda x: any(item for item in selection if item in x)).
@ rs311 согласился - обновил лямбду с примером выбора
Спасибо, сэр, вы спасли мне день :)
В этом случае использование Numpy будет намного быстрее, чем использование Pandas,
Вариант 1. Используя numpy-пересечение,
mask = df.species.apply(lambda x: np.intersect1d(x, selection).size > 0)
df[mask]
450 µs ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
molecule species
0 a [dog]
2 c [cat, dog]
3 d [cat, horse, pig]
Вариант 2: решение, аналогичное приведенному выше, с использованием numpy in1d,
df[df.species.apply(lambda x: np.any(np.in1d(x, selection)))]
420 µs ± 17.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Вариант 3: Интересно, что использование чистого набора Python здесь довольно быстро
df[df.species.apply(lambda x: bool(set(x) & set(selection)))]
305 µs ± 5.22 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Это простой и простой подход.
Вы можете создать функцию, которая проверяет, присутствуют ли элементы в Selection list в столбце pandas list.
def check(speciesList):
flag = False
for animal in selection:
if animal in speciesList:
flag = True
return flag
Затем вы можете использовать этот list для создания столбца, содержащего True или False, в зависимости от того, содержит ли запись хотя бы один элемент в списке выбора, и создать на его основе новый фрейм данных.
df['containsCatDog'] = df.species.apply(lambda animals: check(animals))
newDf = df[df.containsCatDog == True]
Я надеюсь, что это помогает.
IIUC Восстановите свой df, тогда использование isin с any должно быть быстрее, чем apply
df[pd.DataFrame(df.species.tolist()).isin(selection).any(1).values]
Out[64]:
molecule species
0 a [dog]
2 c [cat, dog]
3 d [cat, horse, pig]
Это хорошо сделано - единственное предостережение заключается в том, что если ваш индекс не является последовательным целым числом (моя конкретная проблема, а не OP), вы должны добавить аргумент index = df.index в свой конструктор фрейма данных. например df [pd.DataFrame (df.species.tolist (), index = df.index) .isin (selection) .any (1)]
@James_SO добавляет значения в конце ~
Использование pandas str.contains (использует регулярное выражение):
df[~df["species"].str.contains('(cat|dog)', regex=True)]
Вывод:
molecule species
1 b [horse, pig]
4 e [chicken, pig]
import pandas as pd
import numpy as np
selection = ['cat', 'dog']
df = pd.DataFrame({'molecule': ['a','b','c','d','e'], 'species' : [['dog'], ['horse','pig'],['cat', 'dog'], ['cat','horse','pig'], ['chicken','pig']]})
df1 = df[df['species'].apply((lambda x: 'dog' in x) )]
df2=df[df['species'].apply((lambda x: 'cat' in x) )]
frames = [df1, df2]
result = pd.concat(frames,join='inner',ignore_index=False)
print("result",result)
result = result[~result.index.duplicated(keep='first')]
print(result)
Используйте
df = df.loc[df.species.str.contains('cat|dog'),:]