¿Cómo se filtran los marcos de datos de pandas por varias columnas?

100

Para filtrar un marco de datos (df) por una sola columna, si consideramos los datos con hombres y mujeres, podríamos:

males = df[df[Gender]=='Male']

Pregunta 1: ¿Pero qué pasa si los datos abarcan varios años y solo quisiera ver hombres en 2014?

En otros idiomas, podría hacer algo como:

if A = "Male" and if B = "2014" then 

(excepto que quiero hacer esto y obtener un subconjunto del marco de datos original en un nuevo objeto de marco de datos)

Pregunta 2. ¿Cómo hago esto en un bucle y creo un objeto de marco de datos para cada conjunto único de año y género (es decir, un df para: 2013-Masculino, 2013-Femenino, 2014-Masculino y 2014-Femenino

for y in year:

for g in gender:

df = .....
yoshiserry
fuente
¿Quieres filtrarlo o agruparlo ? Si desea crear un DataFrame separado para cada conjunto único de año y género, mire groupby.
BrenBarn
1
Esta respuesta brinda una descripción general completa de la indexación booleana y los operadores lógicos en pandas.
cs95

Respuestas:

172

Usando el &operador, no olvide envolver las sub-declaraciones con ():

males = df[(df[Gender]=='Male') & (df[Year]==2014)]

Para almacenar sus marcos de datos en un dictbucle for:

from collections import defaultdict
dic={}
for g in ['male', 'female']:
  dic[g]=defaultdict(dict)
  for y in [2013, 2014]:
    dic[g][y]=df[(df[Gender]==g) & (df[Year]==y)] #store the DataFrames to a dict of dict

EDITAR:

Una demostración para tu getDF:

def getDF(dic, gender, year):
  return dic[gender][year]

print genDF(dic, 'male', 2014)
zhangxaochen
fuente
gran respuesta zhangxaochen: ¿podría editar su respuesta para mostrar en la parte inferior cómo podría hacer un bucle for, que crea los marcos de datos (con datos de año y género) pero los agrega a un diccionario para que se pueda acceder a ellos más tarde con mi método getDF? def GetDF (dict, key): return dict [key]
yoshiserry
@yoshiserry ¿cómo es keytu getDF? ¿Un solo parámetro o una tupla de claves? sea ​​específico por
favor
hola, es una sola clave, solo una palabra, que correspondería al género (masculino o femenino) o al año (13, 14). No sabía que podía tener una tupla de claves. ¿Podría compartir un ejemplo de cuándo y cómo haría esto?
yoshiserry
¿Podrías echarle un vistazo a esta pregunta también? Siento que podrías responderlo. Se relaciona de nuevo con los marcos de datos de pandas. stackoverflow.com/questions/22086619/…
yoshiserry
1
Tenga en cuenta que Gendery Yeardeben ser cadenas, es decir, 'Gender'y 'Year'.
Steven C. Howell
22

Para funciones booleanas más generales que le gustaría usar como filtro y que dependen de más de una columna, puede usar:

df = df[df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)]

donde f es una función que se aplica a cada par de elementos (x1, x2) de col_1 y col_2 y devuelve Verdadero o Falso dependiendo de cualquier condición que desee en (x1, x2).

guibor
fuente
11

Comience desde pandas 0.13 , esta es la forma más eficiente.

df.query('Gender=="Male" & Year=="2014" ')
redreamalidad
fuente
1
¿Por qué debería ser esto más eficiente que la respuesta aceptada?
Bouncner
@Bouncner simplemente verifíquelo con la respuesta más votada.
reamalidad
5
Esta respuesta podría mejorarse mostrando el punto de referencia
nardeas
8

En caso de que alguien se pregunte cuál es la forma más rápida de filtrar (la respuesta aceptada o la de @redreamality):

import pandas as pd
import numpy as np

length = 100_000
df = pd.DataFrame()
df['Year'] = np.random.randint(1950, 2019, size=length)
df['Gender'] = np.random.choice(['Male', 'Female'], length)

%timeit df.query('Gender=="Male" & Year=="2014" ')
%timeit df[(df['Gender']=='Male') & (df['Year']==2014)]

Resultados de 100.000 filas:

6.67 ms ± 557 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.54 ms ± 536 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Resultados para 10,000,000 filas:

326 ms ± 6.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
472 ms ± 25.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Entonces, los resultados dependen del tamaño y los datos. En mi computadora portátil, se query()vuelve más rápido después de 500k filas. Además, la búsqueda de cadenas Year=="2014"tiene una sobrecarga innecesaria ( Year==2014es más rápida).

Bouncner
fuente
1
Sin embargo, creo que la querysintaxis es más ordenada y cercana a SQL, lo que la hace agradable para los datos desde entonces. Lo bueno del pastel es que es más rápido con muchas filas :)
csgroen
1

Puede crear su propia función de filtro usando queryin pandas. Aquí tienes filtrado de dfresultados por todos los kwargsparámetros. No olvide agregar algunos validadores ( kwargsfiltrado) para obtener la función de filtro por su cuenta df.

def filter(df, **kwargs):
    query_list = []
    for key in kwargs.keys():
        query_list.append(f'{key}=="{kwargs[key]}"')
    query = ' & '.join(query_list)
    return df.query(query)
Alex
fuente
¡Gracias por la elegante solución! Creo que es el mejor de todos los demás. Combina la eficacia de utilizar la consulta con la versatilidad de tenerla como función.
A Merii
0

Puede filtrar por varias columnas (más de dos) utilizando el np.logical_andoperador para reemplazar &(o np.logical_orreemplazar |)

Aquí hay una función de ejemplo que hace el trabajo, si proporciona valores de destino para varios campos. Puede adaptarlo para diferentes tipos de filtrado y demás:

def filter_df(df, filter_values):
    """Filter df by matching targets for multiple columns.

    Args:
        df (pd.DataFrame): dataframe
        filter_values (None or dict): Dictionary of the form:
                `{<field>: <target_values_list>}`
            used to filter columns data.
    """
    import numpy as np
    if filter_values is None or not filter_values:
        return df
    return df[
        np.logical_and.reduce([
            df[column].isin(target_values) 
            for column, target_values in filter_values.items()
        ])
    ]

Uso:

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [1, 2, 3, 4]})

filter_df(df, {
    'a': [1, 2, 3],
    'b': [1, 2, 4]
})
Tom Bug
fuente