Come verificare se una stringa contiene una delle sottostringhe in un elenco, in panda?


119

C'è qualche funzione che sarebbe l'equivalente di una combinazione di df.isin()e df[col].str.contains()?

Ad esempio, diciamo che ho la serie s = pd.Series(['cat','hat','dog','fog','pet'])e voglio trovare tutti i posti in cui ne scontiene uno ['og', 'at'], vorrei ottenere tutto tranne "animale domestico".

Ho una soluzione, ma è piuttosto inelegante:

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

C'è un modo migliore per farlo?


Nota : esiste una soluzione descritta da @unutbu che è più efficiente dell'utilizzo di pd.Series.str.contains. Se le prestazioni sono un problema, potrebbe valere la pena indagare.
jpp

Consiglio vivamente di dare un'occhiata a questa risposta per la ricerca di stringhe parziale utilizzando più parole chiave / espressioni regolari (scorri verso il basso fino al sottotitolo " Ricerca di sottostringa multipla ").
cs95

Risposte:


219

Un'opzione è solo quella di utilizzare il |carattere regex per cercare di abbinare ciascuna delle sottostringhe nelle parole della tua serie s(ancora in uso str.contains).

Puoi costruire la regex unendo le parole searchforcon |:

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

Come ha notato @AndyHayden nei commenti qui sotto, fai attenzione se le tue sottostringhe hanno caratteri speciali come $e ^che vuoi abbinare letteralmente. Questi caratteri hanno significati specifici nel contesto delle espressioni regolari e influenzeranno la corrispondenza.

Puoi rendere più sicuro il tuo elenco di sottostringhe eseguendo l'escape dei caratteri non alfanumerici con re.escape:

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

Le stringhe con in questo nuovo elenco corrisponderanno letteralmente a ogni carattere se usate con str.contains.


4
forse è bene aggiungere anche questo link pandas.pydata.org/pandas-docs/stable/… . A partire da panda 0.15, le operazioni sulle stringhe sono ancora più semplici
goofd

6
una cosa a cui devi fare attenzione è se una stringa in searchfor ha caratteri speciali regex (puoi mappare con re.escape ).
Andy Hayden

@ AndyHayden Grazie, ho migliorato la mia risposta per tenere conto di questa complicazione.
Alex Riley

Non so perché il tuo metodo non funziona con "str.startswith ('|' .join (searchfor))"
Doo Hyun Shin

48

Puoi usare str.containsda solo con un pattern regex usando OR (|):

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

Oppure puoi aggiungere la serie a un dataframequindi utilizzare str.contains:

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

Produzione:

0 cat
1 hat
2 dog
3 fog 

come farlo per AND?
JacoSolari

1
@JacoSolari controlla questa risposta stackoverflow.com/questions/37011734/…
James

1
@ James si, grazie. Per il completamento ecco l'oneliner più votato in quella risposta. df.col.str.contains(r'(?=.*apple)(?=.*banana)',regex=True)
JacoSolari

1

Ecco un lambda di una riga che funziona anche:

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

Ingresso:

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

Applica Lambda:

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

Produzione:

    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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.