Los pandas encuentran cadenas en común entre las series.

Tengo una serie de palabras clave extraídas de un DataFrame más grande y un DataFrame con, entre otras, una columna de cadenas. Me gustaría enmascarar el DataFrame encontrando qué cadenas contiene al menos una palabra clave. La serie de “Palabras clave” es la siguiente (perdón por las palabras extrañas):

Skilful Wilful Somewhere Thing Strange 

El DataFrame se ve como sigue:

 User_ID;Tweet 01;hi all 02;see you somewhere 03;So weird 04;hi all :-) 05;next big thing 06;how can i say no? 07;so strange 08;not at all 

Hasta ahora he usado una función str.contains () de pandas como:

 mask = df['Tweet'].str.contains(str(Keywords['Keyword'][4]), case=False) 

que funciona bien encontrando la cadena “Extraña” en el DataFrame y devuelve:

 0 False 1 False 2 False 3 False 4 False 5 False 6 True 7 False Name: Tweet, dtype: bool 

Lo que me gustaría hacer es enmascarar todo el DataFrame con la matriz de todas las palabras clave, por lo que puedo tener algo como esto:

 0 False 1 True 2 False 3 False 4 True 5 False 6 True 7 False Name: Tweet, dtype: bool 

¿Es posible sin hacer un bucle a través de la matriz? En mi caso real, tengo que buscar a través de millones de cadenas, así que estoy buscando un método rápido.

Gracias por su amable ayuda.

Otra forma de lograr esto es usar pd.Series.isin () con map y apply , con su muestra será como:

 df # DataFrame User_ID Tweet 0 1 hi all 1 2 see you somewhere 2 3 So weird 3 4 hi all :-) 4 5 next big thing 5 6 how can i say no? 6 7 so strange 7 8 not at all 

 w # Series 0 Skilful 1 Wilful 2 Somewhere 3 Thing 4 Strange dtype: object 

 # list masked = map(lambda x: any(w.apply(str.lower).isin(x)), \ df['Tweet'].apply(str.lower).apply(str.split)) df['Tweet_masked'] = masked 

Resultados:

 df Out[13]: User_ID Tweet Tweet_masked 0 1 hi all False 1 2 see you somewhere True 2 3 So weird False 3 4 hi all :-) False 4 5 next big thing True 5 6 how can i say no? False 6 7 so strange True 7 8 not at all False 

Como nota al margen, isin solo funciona si toda la cadena coincide con los valores, en caso de que solo esté interesado en str.contains , aquí está la variante:

 masked = map(lambda x: any(_ in x for _ in w.apply(str.lower)), \ df['Tweet'].apply(str.lower)) 

Actualizado: como señaló @Alex, podría ser aún más eficiente combinar mapa y regexp, de hecho, no me gusta mucho map + lambda , aquí vamos:

 import re r = re.compile(r'.*({}).*'.format('|'.join(w.values)), re.IGNORECASE) masked = map(bool, map(r.match, df['Tweet'])) 
 import re df['Tweet'].str.match('.*({0}).*'.format('|'.join(phrases))) 

Donde las phrases es un iterable de frases cuya existencia estás condicionando.

Una simple apply puede resolver esto. Si puede resistir unos segundos de procesamiento, creo que este es el método más simple disponible sin aventurarse fuera de los pandas .

 import pandas as pd df = pd.read_csv("dict.csv", delimiter=";") ref = pd.read_csv("ref.csv") kw = set([k.lower() for k in ref["Keywords"]]) print kw boom = lambda x:True if any(w in kw for w in x.split()) else False df["Tweet"] = df["Tweet"].apply(boom) print df 

Lo probé contra exactamente 10,165,760 filas de datos inventados y se completó en 18.9s. Si eso no es lo suficientemente rápido, entonces se necesita un método mejor.

 set(['somewhere', 'thing', 'strange', 'skilful', 'wilful']) User_ID Tweet 0 1 False 1 2 True 2 3 False 3 4 False 4 5 True 5 6 False 6 7 True 7 8 False 8 1 False 9 2 True 10 3 False 11 4 False 12 5 True 13 6 False 14 7 True 15 8 False 16 1 False 17 2 True 18 3 False 19 4 False 20 5 True 21 6 False 22 7 True 23 8 False 24 1 False 25 2 True 26 3 False 27 4 False 28 5 True 29 6 False ... ... ... 10165730 3 False 10165731 4 False 10165732 5 True 10165733 6 False 10165734 7 True 10165735 8 False 10165736 1 False 10165737 2 True 10165738 3 False 10165739 4 False 10165740 5 True 10165741 6 False 10165742 7 True 10165743 8 False 10165744 1 False 10165745 2 True 10165746 3 False 10165747 4 False 10165748 5 True 10165749 6 False 10165750 7 True 10165751 8 False 10165752 1 False 10165753 2 True 10165754 3 False 10165755 4 False 10165756 5 True 10165757 6 False 10165758 7 True 10165759 8 False [10165760 rows x 2 columns] [Finished in 18.9s] 

Espero que esto ayude.