Comment tester si une chaîne contient l'une des sous-chaînes d'une liste, dans les pandas?


119

Y a-t-il une fonction qui équivaudrait à une combinaison de df.isin()et df[col].str.contains()?

Par exemple, disons que j'ai la série s = pd.Series(['cat','hat','dog','fog','pet'])et que je veux trouver tous les endroits où scontient l'un des ['og', 'at'], je voudrais tout obtenir sauf «animal de compagnie».

J'ai une solution, mais c'est plutôt inélégant:

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

Y a-t-il une meilleure manière de faire cela?


Remarque : il existe une solution décrite par @unutbu qui est plus efficace que l'utilisation pd.Series.str.contains. Si les performances sont un problème, cela peut valoir la peine d'être étudié.
— jpp

Je recommande vivement de vérifier cette réponse pour une recherche de chaîne partielle à l'aide de plusieurs mots-clés / expressions régulières (faites défiler jusqu'à la sous-rubrique « Recherche de sous- chaînes multiples »).
— cs95

Réponses:


219

Une option consiste simplement à utiliser le |caractère regex pour essayer de faire correspondre chacune des sous-chaînes dans les mots de votre série s(toujours en utilisant str.contains).

Vous pouvez construire l'expression régulière en joignant les mots searchforavec |:

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

Comme @AndyHayden l'a noté dans les commentaires ci-dessous, faites attention si vos sous-chaînes ont des caractères spéciaux tels que $et ^que vous voulez faire correspondre littéralement. Ces caractères ont des significations spécifiques dans le contexte des expressions régulières et affecteront la correspondance.

Vous pouvez rendre votre liste de sous-chaînes plus sûre en échappant les caractères non alphanumériques avec re.escape:

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

Les chaînes avec dans cette nouvelle liste correspondront littéralement à chaque caractère lorsqu'elles sont utilisées avec str.contains.


4
peut-être bon d'ajouter ce lien pandas.pydata.org/pandas-docs/stable/… aussi. À partir de pandas 0.15, les opérations sur les chaînes sont encore plus faciles
— goofd

6
une chose dont vous devez faire attention est si une chaîne dans searchfor a des caractères spéciaux de regex (vous pouvez mapper avec re.escape ).
— Andy Hayden

@AndyHayden Merci, j'ai amélioré ma réponse pour prendre en compte cette complication.
— Alex Riley

Je ne sais pas pourquoi votre méthode ne fonctionne pas avec "str.startswith ('|' .join (searchfor))"
— Doo Hyun Shin

48

Vous pouvez utiliser str.containsseul avec un modèle regex en utilisant OR (|):

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

Ou vous pouvez ajouter la série à un dataframepuis utiliser str.contains:

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

Production:

0 cat
1 hat
2 dog
3 fog 

comment le faire pour ET?
— JacoSolari

1
@JacoSolari consultez cette réponse stackoverflow.com/questions/37011734/…
— James

1
@James oui, merci. Pour terminer, voici le oneliner le plus voté dans cette réponse. df.col.str.contains(r'(?=.*apple)(?=.*banana)',regex=True)
— JacoSolari

1

Voici un lambda d'une ligne qui fonctionne également:

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

Contribution:

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

Appliquer Lambda:

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

Production:

    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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.