Как проверить, содержит ли строка одну из подстрок в списке в пандах?

119

Есть ли какая-нибудь функция, которая была бы эквивалентом комбинации df.isin()и df[col].str.contains()?

Например, скажем, у меня есть серия s = pd.Series(['cat','hat','dog','fog','pet']), и я хочу найти все места, где sесть что-то из ['og', 'at'], я бы хотел получить все, кроме «pet».

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

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 07

Ответы:

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/… тоже. Начиная с pandas 0.15, строковые операции стали еще проще
goofd
6
одна вещь, о которой вы должны позаботиться, - это если строка в searchfor имеет специальные символы регулярного выражения (вы можете сопоставить с re.escape ).
Энди Хайден
@AndyHayden Спасибо, я улучшил свой ответ, чтобы учесть это осложнение.
Alex Riley
Я не знаю, почему ваш метод не работает с "str.startswith ('|' .join (searchfor))"
Ду Хён Шин
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 
l'L'л
источник
как это сделать для И?
JacoSolari,
1
@JacoSolari, посмотрите этот ответ stackoverflow.com/questions/37011734/…
Джеймс,
1
@ Джеймс, да, спасибо. Для завершения вот один из самых популярных ответов в этом ответе. 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
Грант Шеннон
источник