comment filtrer les dataframes pandas par plusieurs colonnes

100

Pour filtrer un dataframe (df) par une seule colonne, si nous considérons les données avec des hommes et des femmes, nous pourrions:

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

Question 1 - Mais que se passerait-il si les données s'étalaient sur plusieurs années et que je voulais voir uniquement les hommes pour 2014?

Dans d'autres langues, je pourrais faire quelque chose comme:

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

(sauf que je veux faire cela et obtenir un sous-ensemble du dataframe d'origine dans un nouvel objet dataframe)

Question 2. Comment faire cela en boucle et créer un objet dataframe pour chaque ensemble unique d'année et de sexe (c'est-à-dire un df pour: 2013-Homme, 2013-Femme, 2014-Homme et 2014-Femme

for y in year:

for g in gender:

df = .....
yoshiserry
la source
Voulez-vous le filtrer ou le regrouper ? Si vous souhaitez créer un DataFrame distinct pour chaque ensemble unique d'année et de sexe, regardez groupby.
BrenBarn
1
Cette réponse donne un aperçu complet de l'indexation booléenne et des opérateurs logiques dans les pandas.
cs95

Réponses:

172

En utilisant l' &opérateur, n'oubliez pas d'encapsuler les sous-instructions avec ():

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

Pour stocker vos dataframes dans une à l' dictaide d'une boucle 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

ÉDITER:

Une démo pour votre getDF:

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

print genDF(dic, 'male', 2014)
Zhangxaochen
la source
excellente réponse zhangxaochen - pourriez-vous modifier votre réponse pour montrer en bas comment vous pourriez faire une boucle for, qui crée les dataframes (avec des données d'année et de sexe) mais les ajoute à un dictionnaire afin qu'elles puissent être consultées plus tard par ma méthode getDF? def GetDF (dict, key): return dict [key]
yoshiserry
@yoshiserry ce qui est le keycomme dans votre getDF? un seul paramètre ou un tuple de clés? be specific plz;)
zhangxaochen
salut c'est une seule clé, juste un mot, qui correspondrait au sexe (masculin ou féminin) ou à l'année (13, 14) Je ne savais pas que vous pouviez avoir un tuple de clés. Pourriez-vous donner un exemple de quand et comment vous feriez cela?
yoshiserry
pourriez-vous jeter un oeil à cette question aussi. J'ai l'impression que tu pourrais y répondre. Se rapporte à nouveau aux dataframes pandas. stackoverflow.com/questions/22086619/…
yoshiserry
1
Notez que Genderet Yeardoit être tous les deux des chaînes, c'est-à-dire 'Gender'et 'Year'.
Steven C. Howell
22

Pour des fonctions booléennes plus générales que vous souhaitez utiliser comme filtre et qui dépendent de plusieurs colonnes, vous pouvez utiliser:

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

où f est une fonction qui est appliquée à chaque paire d'éléments (x1, x2) de col_1 et col_2 et renvoie True ou False selon la condition que vous souhaitez (x1, x2).

guibor
la source
11

Commencez par pandas 0.13 , c'est le moyen le plus efficace.

df.query('Gender=="Male" & Year=="2014" ')
rééréalité
la source
1
Pourquoi cela devrait-il être plus efficace que la réponse acceptée?
Bouncner le
@Bouncner vient de le vérifier par rapport à la réponse la plus votée.
redreamality
4
Cette réponse pourrait être améliorée en montrant l'indice de référence
nardeas
8

Au cas où quelqu'un se demanderait quel est le moyen le plus rapide de filtrer (la réponse acceptée ou celle 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)]

Résultats pour 100 000 lignes:

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)

Résultats pour 10 000 000 lignes:

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)

Les résultats dépendent donc de la taille et des données. Sur mon ordinateur portable, query()devient plus rapide après 500 000 lignes. En outre, la recherche de chaîne dans Year=="2014"a une surcharge inutile ( Year==2014est plus rapide).

Bouncner
la source
1
Cependant, je pense que la querysyntaxe est plus nette et proche de SQL, ce qui la rend agréable pour les données depuis. Le chery sur le gâteau, c'est qu'il est plus rapide avec de nombreuses rangées :)
csgroen
1

Vous pouvez créer votre propre fonction de filtre en utilisant queryin pandas. Ici, vous avez le filtrage des dfrésultats par tous les kwargsparamètres. N'oubliez pas d'ajouter des validateurs ( kwargsfiltrage) pour obtenir votre propre fonction de filtre 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
la source
Merci pour la solution élégante! Je pense que c'est le meilleur de tout le reste. Il combine l'efficacité de l'utilisation de la requête avec la polyvalence de l'avoir en tant que fonction.
A Merii le
0

Vous pouvez filtrer par plusieurs colonnes (plus de deux) en utilisant l' np.logical_andopérateur pour remplacer &(ou np.logical_orpour remplacer |)

Voici un exemple de fonction qui fait le travail, si vous fournissez des valeurs cibles pour plusieurs champs. Vous pouvez l'adapter à différents types de filtrage et autres:

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()
        ])
    ]

Usage:

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
la source