Tengo un DataFrame
con 4 columnas de las cuales 2 contienen valores de cadena. Me preguntaba si habría una manera de seleccionar filas basadas en una cadena de concordancia parcial con una columna en particular.
En otras palabras, una función o función lambda que haría algo como
re.search(pattern, cell_in_question)
devolviendo un booleano. Estoy familiarizado con la syntax de df[df['A'] == "hello world"]
pero parece que no puedo encontrar la manera de hacer lo mismo con una cadena de caracteres parcial que diga 'hello'
.
¿Alguien podría apuntarme en la dirección correcta?
Según el número github # 620 , parece que pronto podrás hacer lo siguiente:
df[df['A'].str.contains("hello")]
Actualización: los métodos de cadenas vectorizadas (es decir, Series.str) están disponibles en pandas 0.8.1 y superiores.
Estoy usando pandas 0.14.1 en macos en el portátil ipython. Probé la línea propuesta arriba:
df[df['A'].str.contains("Hello|Britain")]
y consiguió un error:
"cannot index with vector containing NA / NaN values"
pero funcionó perfectamente cuando se agregó una condición “== True”, como esta:
df[df['A'].str.contains("Hello|Britain")==True]
Si alguien se pregunta cómo realizar un problema relacionado: “Seleccionar columna por cadena parcial”
Utilizar:
df.filter(like='hello') # select columns which contain the word hello
Y para seleccionar filas por coincidencia de cadena parcial, pase el axis=0
para filtrar:
# selects rows which contain the word hello in their index label df.filter(like='hello', axis=0)
Nota rápida: si desea realizar una selección basada en una cadena parcial contenida en el índice, intente lo siguiente:
df['stridx']=df.index df[df['stridx'].str.contains("Hello|Britain")]
Digamos que tienes el siguiente DataFrame
:
>>> df = pd.DataFrame([['hello', 'hello world'], ['abcd', 'defg']], columns=['a','b']) >>> df ab 0 hello hello world 1 abcd defg
Siempre puede usar el operador in en una expresión lambda para crear su filtro.
>>> df.apply(lambda x: x['a'] in x['b'], axis=1) 0 True 1 False dtype: bool
El truco aquí es usar la opción axis=1
en la apply
para pasar elementos a la función lambda fila por fila, en lugar de columna por columna.
Esto es lo que terminé haciendo para coincidencias de cadenas parciales. Si alguien tiene una forma más eficiente de hacerlo, hágamelo saber.
def stringSearchColumn_DataFrame(df, colName, regex): newdf = DataFrame() for idx, record in df[colName].iteritems(): if re.search(regex, record): newdf = concat([df[df[colName] == record], newdf], ignore_index=True) return newdf
Como he visto muchas preguntas sobre temas similares, pensé que sería bueno dejar esto aquí.
df1 = pd.DataFrame({'col': ['foo', 'foobar', 'bar', 'baz']}) df1 col 0 foo 1 foobar 2 bar 3 baz
Para seleccionar todas las filas que contienen “foo”, use str.contains
:
df1[df1['col'].str.contains('foo')] col 0 foo 1 foobar
Tenga en cuenta que esta es una búsqueda de subcadena pura, por lo que puede deshabilitar de forma segura la coincidencia basada en expresiones regulares.
df1[df1['col'].str.contains('foo', regex=False)] col 0 foo 1 foobar
En cuanto al rendimiento, esto hace una diferencia.
df2 = pd.concat([df1] * 1000, ignore_index=True) %timeit df2[df2['col'].str.contains('foo')] %timeit df2[df2['col'].str.contains('foo', regex=False)] 6.31 ms ± 126 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 2.8 ms ± 241 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Evite utilizar la búsqueda basada en expresiones regulares si no la necesita.
Nota
Las búsquedas de subcadenas parciales que están ancladas al comienzo o al final de las cadenas se pueden hacer usandostr.startswith
ostr.endswith
respectivamente.Además, para las búsquedas basadas en
str.match
regulares ancladas al inicio, usestr.match
.
Búsqueda basada en Regex
La mayoría de los métodos str
admiten expresiones regulares. Por ejemplo, para encontrar filas en df1
que contengan “foo” seguidas de otra cosa, podemos usar
df1[df1['col'].str.contains(r'foo(?!$)')] col 1 foobar
De forma predeterminada, la búsqueda de subcadenas busca la subcadena / patrón especificado, independientemente de si es palabra completa o no. Para unir solo palabras completas, necesitaremos usar expresiones regulares aquí, en particular, nuestro patrón deberá especificar límites de palabras ( \b
).
Por ejemplo,
df3 = pd.DataFrame({'col': ['the sky is blue', 'bluejay by the window']}) df3 col 0 the sky is blue 1 bluejay by the window
Ahora considera,
df3[df3['col'].str.contains('blue')] col 0 the sky is blue 1 bluejay by the window
v / s
df3[df3['col'].str.contains(r'\bblue\b')] col 0 the sky is blue
Esto se logra más fácilmente a través de una búsqueda de expresiones regulares utilizando la expresión regular o la tubería.
# Slightly modified example. df4 = pd.DataFrame({'col': ['foo abc', 'foobar xyz', 'bar32', 'baz 45']}) df4 col 0 foo abc 1 foobar xyz 2 bar32 3 baz 45 df4[df4['col'].str.contains(r'foo|baz')] col 0 foo abc 1 foobar xyz 3 baz 45
También puedes crear una lista de términos, luego unirlos:
terms = ['foo', 'baz'] df4[df4['col'].str.contains('|'.join(terms))] col 0 foo abc 1 foobar xyz 3 baz 45
A veces, es aconsejable escapar de sus términos en caso de que tengan caracteres que puedan interpretarse como metacaracteres de expresiones regulares . Si tus términos contienen alguno de los siguientes caracteres …
. ^ $ * + ? { } [ ] \ | ( )
Entonces, necesitarás usar re.escape
para escapar de ellos:
import re df4[df4['col'].str.contains('|'.join(map(re.escape, terms)))] col 0 foo abc 1 foobar xyz 3 baz 45
re.escape
tiene el efecto de escapar de los caracteres especiales para que sean tratados literalmente.
re.escape(r'.foo^') # '\\.foo\\^'
Similar al anterior, excepto que agregamos un límite de palabra ( \b
) al patrón unido.
p = r'\b(?:{})\b'.format('|'.join(map(re.escape, terms))) df4[df4['col'].str.contains(p)] col 0 foo abc 3 baz 45
Donde p
ve así,
p # '\\b(?:foo|baz)\\b'
¡Porque tú puedes! ¡Y tú deberías! Por lo general, son un poco más rápidos que los métodos de cadena, ya que los métodos de cadena son difíciles de vectorizar y generalmente tienen implementaciones descabelladas.
En lugar de,
df1[df1['col'].str.contains('foo', regex=False)]
Utilice el operador in
dentro de una lista comp,
df1[['foo' in x for x in df1['col']]] col 0 foo abc 1 foobar
En lugar de,
regex_pattern = r'foo(?!$)' df1[df1['col'].str.contains(regex_pattern)]
Use re.compile
(para almacenar en caché su expresión regular) + Pattern.search
dentro de una lista comp,
p = re.compile(regex_pattern, flags=re.IGNORECASE) df1[[bool(p.search(x)) for x in df1['col']]] col 1 foobar
Si “col” tiene NaNs, entonces en lugar de
df1[df1['col'].str.contains(regex_pattern, na=False)]
Utilizar,
def try_search(p, x): try: return bool(p.search(x)) except TypeError: return False p = re.compile(regex_pattern) df1[[try_search(p, x) for x in df1['col']]] col 1 foobar
np.char.find
, np.vectorize
, DataFrame.query
. Además de str.contains
y listas de comprensión, también puede utilizar las siguientes alternativas.
np.char.find
Admite búsquedas de subcadenas (leer: no regex) solamente.
df4[np.char.find(df4['col'].values.astype(str), 'foo') > -1] col 0 foo abc 1 foobar xyz
np.vectorize
Se trata de una envoltura alrededor de un bucle, pero con una sobrecarga menor que la de la mayoría de los métodos de pandas str
.
f = np.vectorize(lambda haystack, needle: needle in haystack) f(df1['col'], 'foo') # array([ True, True, False, False]) df1[f(df1['col'], 'foo')] col 0 foo abc 1 foobar
Posibles soluciones Regex:
regex_pattern = r'foo(?!$)' p = re.compile(regex_pattern) f = np.vectorize(lambda x: pd.notna(x) and bool(p.search(x))) df1[f(df1['col'])] col 1 foobar
DataFrame.query
Es compatible con los métodos de cadena a través del motor Python. Esto no ofrece beneficios de rendimiento visibles, pero aún así es útil saber si necesita generar dinámicamente sus consultas.
df1.query('col.str.contains("foo")', engine='python') col 0 foo 1 foobar
Se puede encontrar más información sobre la familia de métodos de query
y eval
en Dynamic Expression Evaluation en pandas usando pd.eval () .
str.contains
, por su simplicidad. np.vectorize
df.query
¿Por qué no lo intentas con df[df["COLUMN_ID"].str.contains("SUBSTRING")]
.
Simplemente reemplace el COLUMN_ID
con la cadena que se refiere a la columna donde desea buscar su SUBSTRING
y reemplace la SUBSTRING
con el texto que desea buscar (“Hola” en su caso).