Як перевірити, чи рядок містить одну з підрядів у списку, в пандах?


119

Чи є якась функція, яка була б еквівалентом комбінації df.isin()та df[col].str.contains()?

Наприклад, скажіть, що у мене є серія s = pd.Series(['cat','hat','dog','fog','pet']), і я хочу знайти всі місця, де sє будь-який із них ['og', 'at'], я б хотів отримати все, окрім "домашньої тварини".

У мене є рішення, але воно досить неелегантне:

searchfor = ['og', 'at']
found = [s.str.contains(x) for x in searchfor]
result = pd.DataFrame[found]
result.any()

Чи є кращий спосіб зробити це?


Примітка . Існує рішення, описане @unutbu, яке є більш ефективним, ніж використання pd.Series.str.contains. Якщо продуктивність є проблемою, то це, можливо, варто вивчити.
jpp

Настійно рекомендую перевірити цю відповідь для часткового пошуку в рядку за допомогою декількох ключових слів / регулярних виразів (прокрутіть униз до підзаголовка " Кілька пошукових підрядків ").
cs95

Відповіді:


219

Один із варіантів - просто використовувати |символ регулярного виразів, щоб спробувати відповідати кожній із підрядів у словах у вашій серії s(все ще використовується str.contains).

Ви можете побудувати регулярний вираз, з'єднавши слова в searchforс |:

>>> searchfor = ['og', 'at']
>>> s[s.str.contains('|'.join(searchfor))]
0    cat
1    hat
2    dog
3    fog
dtype: object

Як зазначає @AndyHayden у коментарях нижче, будьте уважні, чи у ваших підрядках є спеціальні символи, такі як $і ^які ви хочете дослівно відповідати. Ці символи мають конкретні значення в контексті регулярних виразів і впливатимуть на відповідність.

Ви можете зробити свій список підрядів більш безпечним, уникаючи не буквено-цифрових символів за допомогою re.escape:

>>> import re
>>> matches = ['$money', 'x^y']
>>> safe_matches = [re.escape(m) for m in matches]
>>> safe_matches
['\\$money', 'x\\^y']

Рядки з цього нового списку будуть буквально відповідати кожному символу при використанні str.contains.


4
можливо, добре також додати це посилання pandas.pydata.org/pandas-docs/stable/… . Починаючи з панд 0,15, струнні операції ще простіші
goofd

6
одна річ, про яку ви повинні подбати - це те, що в рядку searchfor є спеціальні символи регулярного виразів (ви можете зіставити карту за допомогою re.escape ).
Енді Хейден

@AndyHayden Дякую, я вдосконалив свою відповідь, щоб врахувати це ускладнення.
Алекс Райлі

Я не знаю, чому ваш метод не працює з "str.startswith ('|' .join (searchfor))"
Doo Hyun Shin

48

Можна використовувати str.containsокремо з малюнком регулярного вираження, використовуючи OR (|):

s[s.str.contains('og|at')]

Або ви можете додати серію до dataframeтодішнього використання str.contains:

df = pd.DataFrame(s)
df[s.str.contains('og|at')] 

Вихід:

0 cat
1 hat
2 dog
3 fog 

як це зробити для І?
JacoSolari

1
@JacoSolari перевірити цю відповідь stackoverflow.com/questions/37011734/…
Джеймс,

1
@James так, дякую. Для завершення ось найвисокодослідніший ліній у цій відповіді. df.col.str.contains(r'(?=.*apple)(?=.*banana)',regex=True)
JacoSolari

1

Ось одна лямбда-рядок, яка також працює:

df["TrueFalse"] = df['col1'].apply(lambda x: 1 if any(i in x for i in searchfor) else 0)

Вхід:

searchfor = ['og', 'at']

df = pd.DataFrame([('cat', 1000.0), ('hat', 2000000.0), ('dog', 1000.0), ('fog', 330000.0),('pet', 330000.0)], columns=['col1', 'col2'])

   col1  col2
0   cat 1000.0
1   hat 2000000.0
2   dog 1000.0
3   fog 330000.0
4   pet 330000.0

Застосувати лямбда:

df["TrueFalse"] = df['col1'].apply(lambda x: 1 if any(i in x for i in searchfor) else 0)

Вихід:

    col1    col2        TrueFalse
0   cat     1000.0      1
1   hat     2000000.0   1
2   dog     1000.0      1
3   fog     330000.0    1
4   pet     330000.0    0
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.