Wie filtert man Pandas-Datenrahmen nach mehreren Spalten?

97

Um einen Datenrahmen (df) nach einer einzelnen Spalte zu filtern, können wir, wenn wir Daten mit Männern und Frauen berücksichtigen, Folgendes tun:

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

Frage 1 - Aber was ist, wenn sich die Daten über mehrere Jahre erstrecken und ich für 2014 nur Männer sehen wollte?

In anderen Sprachen könnte ich so etwas tun:

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

(außer ich möchte dies tun und eine Teilmenge des ursprünglichen Datenrahmens in einem neuen Datenrahmenobjekt abrufen)

Frage 2. Wie mache ich das in einer Schleife und erstelle ein Datenrahmenobjekt für jeden eindeutigen Satz von Jahr und Geschlecht (dh ein df für: 2013-männlich, 2013-weiblich, 2014-männlich und 2014-weiblich

for y in year:

for g in gender:

df = .....
Yoshiserry
quelle
Möchten Sie es filtern oder gruppieren ? Wenn Sie einen separaten DataFrame für jeden eindeutigen Satz von Jahr und Geschlecht erstellen möchten, schauen Sie sich an groupby.
BrenBarn
1
Diese Antwort gibt einen umfassenden Überblick über die boolesche Indizierung und die logischen Operatoren in Pandas.
CS95

Antworten:

167

Mit &Operator, vergessen Sie nicht mit den Sub-Anweisungen zu wickeln ():

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

So speichern Sie Ihre Datenrahmen in dicteiner for-Schleife:

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

BEARBEITEN:

Eine Demo für Ihre getDF:

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

print genDF(dic, 'male', 2014)
zhangxaochen
quelle
tolle Antwort zhangxaochen - könnten Sie Ihre Antwort bearbeiten, um unten zu zeigen, wie Sie eine for-Schleife erstellen könnten, die die Datenrahmen (mit Jahres- und Geschlechtsdaten) erstellt, sie aber einem Wörterbuch hinzufügt, damit ich später mit meiner getDF-Methode darauf zugreifen kann? def GetDF (dikt, Schlüssel): return dict [Schlüssel]
yoshiserry
@yoshiserry Wie ist das keyin deinem getDF? ein einzelner Parameter oder ein Tupel von Schlüsseln? sei genau plz;)
zhangxaochen
Hallo, es ist ein einzelner Schlüssel, nur ein Wort, das dem Geschlecht (männlich oder weiblich) oder dem Jahr (13, 14) entspricht. Ich wusste nicht, dass Sie ein Tupel Schlüssel haben könnten. Können Sie uns ein Beispiel geben, wann und wie Sie dies tun würden?
Yoshiserry
Könnten Sie sich auch diese Frage ansehen? Ich habe das Gefühl, du könntest es beantworten. Bezieht sich wieder auf Pandas-Datenrahmen. stackoverflow.com/questions/22086619/…
yoshiserry
1
Beachten Sie, dass das Genderund Yearbeide Zeichenfolgen sein sollten, dh 'Gender'und 'Year'.
Steven C. Howell
22

Für allgemeinere boolesche Funktionen, die Sie als Filter verwenden möchten und die von mehr als einer Spalte abhängen, können Sie Folgendes verwenden:

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

Dabei ist f eine Funktion, die auf jedes Elementpaar (x1, x2) von col_1 und col_2 angewendet wird und je nach gewünschter Bedingung (x1, x2) True oder False zurückgibt.

Guibor
quelle
11

Ab Pandas 0.13 ist dies der effizienteste Weg.

df.query('Gender=="Male" & Year=="2014" ')
Redreamalität
quelle
1
Warum sollte dies effizienter sein als die akzeptierte Antwort?
Bouncner
@Bouncner verifiziere es einfach anhand der hochstimmigen Antwort.
Redreamality
4
Diese Antwort könnte verbessert werden, indem der Benchmark
nardeas
6

Falls sich jemand fragt, was der schnellere Weg zum Filtern ist (die akzeptierte Antwort oder die von @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)]

Ergebnisse für 100.000 Zeilen:

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)

Ergebnisse für 10.000.000 Zeilen:

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)

Die Ergebnisse hängen also von der Größe und den Daten ab. Auf meinem Laptop query()wird nach 500k Zeilen schneller. Außerdem hat die Zeichenfolgensuche Year=="2014"einen unnötigen Overhead ( Year==2014ist schneller).

Bouncner
quelle
1

Sie können eigene Filter - Funktion erstellen queryin pandas. Hier haben Sie die Filterung der dfErgebnisse nach allen kwargsParametern. Vergessen Sie nicht, einige Validatoren ( kwargsFilterung) hinzuzufügen , um die Filterfunktion für sich selbst zu erhalten 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
quelle
Danke für die elegante Lösung! Ich denke, es ist das Beste von allen anderen. Es kombiniert die Effizienz der Verwendung von Abfragen mit der Vielseitigkeit, sie als Funktion zu haben.
Ein Merii
0

Sie können nach mehreren Spalten (mehr als zwei) filtern, indem Sie den np.logical_andOperator zum Ersetzen &(oder np.logical_orzum Ersetzen |) verwenden.

Hier ist eine Beispielfunktion, die den Job ausführt, wenn Sie Zielwerte für mehrere Felder angeben. Sie können es für verschiedene Arten der Filterung anpassen und so weiter:

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

Verwendung:

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
quelle