python - filtrar - funciones de pandas para leer datos a tablas
pandas: filtrar filas de DataFrame con el encadenamiento del operador (14)
La mayoría de las operaciones en pandas
se pueden realizar con el encadenamiento de operadores ( groupby
, aggregate
, apply
, etc.), pero la única forma que he encontrado para filtrar filas es a través de la indexación de corchetes normal
df_filtered = df[df[''column''] == value]
Esto no es atractivo ya que requiere que asigne df
a una variable antes de poder filtrar por sus valores. ¿Hay algo más parecido a lo siguiente?
df_filtered = df.mask(lambda x: x[''column''] == value)
Esto no es atractivo ya que requiere que asigne
df
a una variable antes de poder filtrar por sus valores.
df[df["column_name"] != 5].groupby("other_column_name")
Parece funcionar: también se puede encadenar el operador []
. Tal vez lo agregaron desde que hiciste la pregunta.
Desde la versión 0.18.1, el método .loc
acepta un llamable para la selección. Junto con las funciones lambda, puede crear filtros chainable muy flexibles:
import numpy as np
import pandas as pd
df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list(''ABCD''))
df.loc[lambda df: df.A == 80] # equivalent to df[df.A == 80] but chainable
df.sort_values(''A'').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A]
Si todo lo que está haciendo es filtrar, también puede omitir .loc
.
Esta solución es más complicada en términos de implementación, pero me parece mucho más limpia en términos de uso, y ciertamente es más general que las otras propuestas.
https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/where.py
No es necesario descargar el repositorio completo: guardar el archivo y hacerlo
from where import where as W
debería bastar Entonces lo usas así:
df = pd.DataFrame([[1, 2, True],
[3, 4, False],
[5, 7, True]],
index=range(3), columns=[''a'', ''b'', ''c''])
# On specific column:
print(df.loc[W[''a''] > 2])
print(df.loc[-W[''a''] == W[''b'']])
print(df.loc[~W[''c'']])
# On entire - or subset of a - DataFrame:
print(df.loc[W.sum(axis=1) > 3])
print(df.loc[W[[''a'', ''b'']].diff(axis=1)[''b''] > 1])
Un ejemplo de uso un poco menos estúpido:
data = pd.read_csv(''ugly_db.csv'').loc[~(W == ''$null$'').any(axis=1)]
Por cierto: incluso en el caso de que solo esté utilizando cols booleanos,
df.loc[W[''cond1'']].loc[W[''cond2'']]
puede ser mucho más eficiente que
df.loc[W[''cond1''] & W[''cond2'']]
porque evalúa cond2
solo donde cond1
es True
.
DESCARGO DE RESPONSABILIDAD: Primero di esta respuesta en elsewhere porque no había visto esto.
La respuesta de @lodagro es genial. Lo extendería generalizando la función de máscara como:
def mask(df, f):
return df[f(df)]
Entonces puedes hacer cosas como:
df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0)
Los filtros se pueden encadenar usando una query Pandas:
df = pd.DataFrame( np.random.randn(30,3), columns = [''a'',''b'',''c''])
df_filtered = df.query(''a>0'').query(''0<b<2'')
Los filtros también se pueden combinar en una sola consulta:
df_filtered = df.query(''a>0 and 0<b<2'')
Mi respuesta es similar a las otras. Si no desea crear una nueva función, puede usar lo que pandas ya ha definido para usted. Utilice el método de la tubería.
df.pipe(lambda d: d[d[''column''] == value])
No estoy completamente seguro de lo que quiere, y su última línea de código tampoco ayuda, pero de todos modos:
El filtrado "encadenado" se realiza "encadenando" los criterios en el índice booleano.
In [96]: df
Out[96]:
A B C D
a 1 4 9 1
b 4 5 0 2
c 5 5 1 0
d 1 3 9 6
In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
A B C D
d 1 3 9 6
Si desea encadenar métodos, puede agregar su propio método de máscara y usar ese.
In [90]: def mask(df, key, value):
....: return df[df[key] == value]
....:
In [92]: pandas.DataFrame.mask = mask
In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list(''abcd''), columns=list(''ABCD''))
In [95]: df.ix[''d'',''A''] = df.ix[''a'', ''A'']
In [96]: df
Out[96]:
A B C D
a 1 4 9 1
b 4 5 0 2
c 5 5 1 0
d 1 3 9 6
In [97]: df.mask(''A'', 1)
Out[97]:
A B C D
a 1 4 9 1
d 1 3 9 6
In [98]: df.mask(''A'', 1).mask(''D'', 6)
Out[98]:
A B C D
d 1 3 9 6
Ofrezco esto para ejemplos adicionales. Esta es la misma respuesta que https://.com/a/28159296/
Añadiré otras ediciones para hacer esta publicación más útil.
query
query
fue hecha exactamente para este propósito. Considere el df
marco de datos
import pandas as pd
import numpy as np
np.random.seed([3,1415])
df = pd.DataFrame(
np.random.randint(10, size=(10, 5)),
columns=list(''ABCDE'')
)
df
A B C D E
0 0 2 7 3 8
1 7 0 6 8 6
2 0 2 0 4 9
3 7 3 2 4 3
4 3 6 7 7 4
5 5 3 7 5 9
6 8 7 6 4 7
7 6 2 6 6 5
8 2 8 7 5 8
9 4 7 6 1 5
Usemos la query
para filtrar todas las filas donde D > B
df.query(''D > B'')
A B C D E
0 0 2 7 3 8
1 7 0 6 8 6
2 0 2 0 4 9
3 7 3 2 4 3
4 3 6 7 7 4
5 5 3 7 5 9
7 6 2 6 6 5
Que encadenamos
df.query(''D > B'').query(''C > B'')
# equivalent to
# df.query(''D > B and C > B'')
# but defeats the purpose of demonstrating chaining
A B C D E
0 0 2 7 3 8
1 7 0 6 8 6
4 3 6 7 7 4
5 5 3 7 5 9
7 6 2 6 6 5
Si configura sus columnas para buscar como índices, entonces puede usar DataFrame.xs()
para tomar una sección transversal. Esto no es tan versátil como responde la query
, pero puede ser útil en algunas situaciones.
import pandas as pd
import numpy as np
np.random.seed([3,1415])
df = pd.DataFrame(
np.random.randint(3, size=(10, 5)),
columns=list(''ABCDE'')
)
df
# Out[55]:
# A B C D E
# 0 0 2 2 2 2
# 1 1 1 2 0 2
# 2 0 2 0 0 2
# 3 0 2 2 0 1
# 4 0 1 1 2 0
# 5 0 0 0 1 2
# 6 1 0 1 1 1
# 7 0 0 2 0 2
# 8 2 2 2 2 2
# 9 1 2 0 2 1
df.set_index([''A'', ''D'']).xs([0, 2]).reset_index()
# Out[57]:
# A D B C E
# 0 0 2 2 2 2
# 1 0 2 1 1 0
Si desea aplicar todas las máscaras booleanas comunes, así como una máscara de propósito general, puede colocar lo siguiente en un archivo y luego asignarlos a todos de la siguiente manera:
pd.DataFrame = apply_masks()
Uso:
A = pd.DataFrame(np.random.randn(4, 4), columns=["A", "B", "C", "D"])
A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary
Es un poco intrincado, pero puede hacer las cosas un poco más limpias si está continuamente cortando y cambiando conjuntos de datos según los filtros. También hay un filtro de propósito general adaptado de Daniel Velkov arriba en la función gen_mask que puede usar con las funciones lambda o si lo desea.
Archivo a guardar (uso masks.py):
import pandas as pd
def eq_mask(df, key, value):
return df[df[key] == value]
def ge_mask(df, key, value):
return df[df[key] >= value]
def gt_mask(df, key, value):
return df[df[key] > value]
def le_mask(df, key, value):
return df[df[key] <= value]
def lt_mask(df, key, value):
return df[df[key] < value]
def ne_mask(df, key, value):
return df[df[key] != value]
def gen_mask(df, f):
return df[f(df)]
def apply_masks():
pd.DataFrame.eq_mask = eq_mask
pd.DataFrame.ge_mask = ge_mask
pd.DataFrame.gt_mask = gt_mask
pd.DataFrame.le_mask = le_mask
pd.DataFrame.lt_mask = lt_mask
pd.DataFrame.ne_mask = ne_mask
pd.DataFrame.gen_mask = gen_mask
return pd.DataFrame
if __name__ == ''__main__'':
pass
Solo desea agregar una demostración usando loc
para filtrar no solo por filas sino también por columnas y algunos méritos a la operación encadenada.
El siguiente código puede filtrar las filas por valor.
df_filtered = df.loc[df[''column''] == value]
Al modificarlo un poco puedes filtrar las columnas también.
df_filtered = df.loc[df[''column''] == value, [''year'', ''column'']]
Entonces, ¿por qué queremos un método encadenado? La respuesta es que es fácil de leer si tiene muchas operaciones. Por ejemplo,
res = df/
.loc[df[''station'']==''USA'', [''TEMP'', ''RF'']]/
.groupby(''year'')/
.agg(np.nanmean)
También puede aprovechar la biblioteca numpy para operaciones lógicas. Es bastante rápido.
df[np.logical_and(df[''A''] == 1 ,df[''B''] == 6)]
Tenía la misma pregunta, excepto que quería combinar los criterios en una condición OR. El formato dado por Wouter Overmeire combina los criterios en una condición AND tal que ambos deben cumplirse:
In [96]: df
Out[96]:
A B C D
a 1 4 9 1
b 4 5 0 2
c 5 5 1 0
d 1 3 9 6
In [99]: df[(df.A == 1) & (df.D == 6)]
Out[99]:
A B C D
d 1 3 9 6
Pero descubrí que, si envuelve cada condición en (... == True)
y une los criterios con una canalización, los criterios se combinan en una condición OR, satisfechos siempre que alguno de ellos sea verdadero:
df[((df.A==1) == True) | ((df.D==6) == True)]
pandas ofrece dos alternativas a la respuesta de Wouter Overmeire que no requieren ninguna anulación. Uno es .loc[.]
Con una llamada, como en
df_filtered = df.loc[lambda x: x[''column''] == value]
el otro es .pipe()
, como en
df_filtered = df.pipe(lambda x: x[''column''] == value)