como você filtra dataframes do pandas por colunas múltiplas

99

Para filtrar um dataframe (df) por uma única coluna, se considerarmos dados com homens e mulheres, podemos:

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

Pergunta 1 - Mas e se os dados abrangessem vários anos e eu quisesse ver apenas homens em 2014?

Em outras línguas, posso fazer algo como:

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

(exceto que quero fazer isso e obter um subconjunto do dataframe original em um novo objeto dataframe)

Questão 2. Como faço isso em um loop e criar um objeto dataframe para cada conjunto exclusivo de ano e gênero (ou seja, um df para: 2013-Masculino, 2013-Feminino, 2014-Masculino e 2014-Feminino

for y in year:

for g in gender:

df = .....
Yoshiserry
fonte
Quer filtrá- lo ou agrupá- lo? Se você deseja criar um DataFrame separado para cada conjunto exclusivo de ano e gênero, consulte groupby.
BrenBarn
1
Esta resposta fornece uma visão geral abrangente da indexação booleana e operadores lógicos em pandas.
cs95

Respostas:

169

Usando o &operador, não se esqueça de envolver as subinstruções com ():

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

Para armazenar seus dataframes em um dictloop 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:

Uma demonstração para o seu getDF:

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

print genDF(dic, 'male', 2014)
Zhangxaochen
fonte
ótima resposta zhangxaochen - você poderia editar sua resposta para mostrar na parte inferior como você pode fazer um loop for, que cria os dataframes (com dados de ano e gênero), mas os adiciona a um dicionário para que possam ser acessados ​​posteriormente pelo meu método getDF? def GetDF (dict, key): return dict [key]
yoshiserry
@yoshiserry como é keyno seu getDF? um único parâmetro ou uma tupla de chaves? seja específico, por favor;)
zhangxaochen
oi é uma única chave, apenas uma palavra, que corresponderia ao gênero (masculino ou feminino) ou ano (13, 14) Não sabia que poderia ter uma tupla de chaves. Você poderia dar um exemplo de quando e como faria isso?
yoshiserry
você poderia dar uma olhada nesta questão também. Eu sinto que você poderia responder. Relaciona-se com os dataframes do pandas novamente. stackoverflow.com/questions/22086619/…
yoshiserry
1
Observe que Gendere Yeardevem ser strings, ou seja, 'Gender'e 'Year'.
Steven C. Howell
22

Para funções booleanas mais gerais que você gostaria de usar como filtro e que dependem de mais de uma coluna, você pode usar:

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

onde f é uma função que é aplicada a cada par de elementos (x1, x2) de col_1 e col_2 e retorna True ou False dependendo de qualquer condição que você deseja em (x1, x2).

guibor
fonte
11

Comece com pandas 0,13 , esta é a maneira mais eficiente.

df.query('Gender=="Male" & Year=="2014" ')
redreamalidade
fonte
1
Por que isso deveria ser mais eficiente do que a resposta aceita?
Bouncner
@Bouncner apenas verifique com a resposta votada.
redreamality
4
Esta resposta pode ser melhorada exibindo o benchmark
nardeas
6

Caso alguém se pergunte qual é a maneira mais rápida de filtrar (a resposta aceita ou a 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 para 100.000 linhas:

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 linhas:

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)

Portanto, os resultados dependem do tamanho e dos dados. No meu laptop, query()fica mais rápido depois de 500 mil linhas. Além disso, a pesquisa de string em Year=="2014"tem uma sobrecarga desnecessária ( Year==2014é mais rápida).

Bouncner
fonte
1

Você pode criar sua própria função de filtro usando queryem pandas. Aqui você tem a filtragem de dfresultados por todos os kwargsparâmetros. Não se esqueça de adicionar alguns validadores ( kwargsfiltragem) para obter a função de filtro para você 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
fonte
Obrigado pela solução elegante! Acho que é o melhor de todo o resto. Combina a eficiência de usar a consulta com a versatilidade de tê-la como função.
A Merii
0

Você pode filtrar por várias colunas (mais de duas) usando o np.logical_andoperador para substituir &(ou np.logical_orsubstituir |)

Aqui está um exemplo de função que faz o trabalho, se você fornecer valores de destino para vários campos. Você pode adaptá-lo para diferentes tipos de filtragem e outros enfeites:

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
fonte