Es gibt verschiedene Möglichkeiten, Zeilen aus einem Pandas-Datenrahmen auszuwählen:
- Boolesche Indizierung (
df[df['col'] == value])
- Positionsindizierung (
df.iloc[...])
- Etikettenindizierung (
df.xs(...))
df.query(...) API
Im Folgenden zeige ich Ihnen jeweils Beispiele mit Ratschlägen zur Verwendung bestimmter Techniken. Angenommen, unser Kriterium ist Spalte 'A'=='foo'
(Hinweis zur Leistung: Für jeden Basistyp können wir die Dinge mithilfe der Pandas-API einfach halten oder uns außerhalb der API wagen, normalerweise in die API hinein numpy, und die Dinge beschleunigen.)
Setup
Das erste, was wir brauchen, ist die Identifizierung einer Bedingung, die als unser Kriterium für die Auswahl von Zeilen dient. Wir beginnen mit dem Fall des OP column_name == some_valueund schließen einige andere häufige Anwendungsfälle ein.
Ausleihen bei @unutbu:
import pandas as pd, numpy as np
df = pd.DataFrame({'A': 'foo bar foo bar foo bar foo foo'.split(),
'B': 'one one two three two two one three'.split(),
'C': np.arange(8), 'D': np.arange(8) * 2})
1. Boolesche Indizierung
... Für die boolesche Indizierung muss ermittelt werden, ob der wahre Wert der 'A'Spalte jeder Zeile gleich ist 'foo', und anhand dieser Wahrheitswerte ermittelt werden, welche Zeilen beibehalten werden sollen. Normalerweise nennen wir diese Reihe eine Reihe von Wahrheitswerten mask. Das machen wir auch hier.
mask = df['A'] == 'foo'
Wir können diese Maske dann verwenden, um den Datenrahmen zu schneiden oder zu indizieren
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Dies ist eine der einfachsten Möglichkeiten, um diese Aufgabe zu erfüllen. Wenn Leistung oder Intuitivität kein Problem darstellen, sollte dies die von Ihnen gewählte Methode sein. Wenn jedoch die Leistung ein Problem darstellt, sollten Sie eine alternative Methode zum Erstellen des in Betracht ziehen mask.
2. Positionsindizierung
Die Positionsindizierung ( df.iloc[...]) hat ihre Anwendungsfälle, aber dies ist keiner von ihnen. Um herauszufinden, wo geschnitten werden soll, müssen wir zuerst dieselbe boolesche Analyse durchführen, die wir oben durchgeführt haben. Dadurch müssen wir einen zusätzlichen Schritt ausführen, um dieselbe Aufgabe zu erfüllen.
mask = df['A'] == 'foo'
pos = np.flatnonzero(mask)
df.iloc[pos]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
3. Etikettenindizierung
Die Indizierung von Etiketten kann sehr praktisch sein, aber in diesem Fall erledigen wir wieder mehr Arbeit ohne Nutzen
df.set_index('A', append=True, drop=False).xs('foo', level=1)
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
4. df.query()API
pd.DataFrame.queryist eine sehr elegante / intuitive Methode, um diese Aufgabe auszuführen, ist jedoch häufig langsamer. Allerdings , wenn Sie die Aufmerksamkeit auf die Timings unten zahlen, für große Daten, ist die Abfrage sehr effizient. Mehr als der Standardansatz und von ähnlicher Größe wie mein bester Vorschlag.
df.query('A == "foo"')
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Ich bevorzuge die Boolean mask
Tatsächliche Verbesserungen können vorgenommen werden, indem geändert wird, wie wir unsere erstellen Boolean mask.
maskAlternative 1
Verwenden Sie das zugrunde liegende numpyArray und verzichten Sie auf den Aufwand für die Erstellung eines anderenpd.Series
mask = df['A'].values == 'foo'
Ich werde am Ende vollständigere Zeittests zeigen, aber werfen Sie einen Blick auf die Leistungssteigerungen, die wir mit dem Beispieldatenrahmen erzielen. Zunächst betrachten wir den Unterschied bei der Erstellung dermask
%timeit mask = df['A'].values == 'foo'
%timeit mask = df['A'] == 'foo'
5.84 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
166 µs ± 4.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Die Auswertung maskmit dem numpyArray ist ~ 30 mal schneller. Dies ist teilweise darauf zurückzuführen, dass die numpyBewertung häufig schneller erfolgt. Dies ist auch teilweise auf den fehlenden Overhead zurückzuführen, der zum Erstellen eines Index und eines entsprechenden pd.SeriesObjekts erforderlich ist .
Als nächstes schauen wir uns den Zeitpunkt für das Schneiden mit dem einen maskgegen den anderen an.
mask = df['A'].values == 'foo'
%timeit df[mask]
mask = df['A'] == 'foo'
%timeit df[mask]
219 µs ± 12.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
239 µs ± 7.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Die Leistungssteigerungen sind nicht so ausgeprägt. Wir werden sehen, ob dies gegenüber robusteren Tests Bestand hat.
maskAlternative 2
Wir hätten auch den Datenrahmen rekonstruieren können. Bei der Rekonstruktion eines Datenrahmens gibt es eine große Einschränkung - Sie müssen sich dabei um die kümmern dtypes!
Stattdessen werden df[mask]wir dies tun
pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)
Wenn der Datenrahmen vom gemischten Typ ist, was unser Beispiel ist, dann sind, wenn wir df.valuesdas resultierende Array erhalten, dtype objectund folglich alle Spalten des neuen Datenrahmens von dtype object. Dies erfordert astype(df.dtypes)und tötet potenzielle Leistungssteigerungen.
%timeit df[m]
%timeit pd.DataFrame(df.values[mask], df.index[mask], df.columns).astype(df.dtypes)
216 µs ± 10.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
1.43 ms ± 39.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Wenn der Datenrahmen jedoch nicht vom gemischten Typ ist, ist dies eine sehr nützliche Methode.
Gegeben
np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))
d1
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
%%timeit
mask = d1['A'].values == 7
d1[mask]
179 µs ± 8.73 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Gegen
%%timeit
mask = d1['A'].values == 7
pd.DataFrame(d1.values[mask], d1.index[mask], d1.columns)
87 µs ± 5.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Wir haben die Zeit halbiert.
maskAlternative 3
@unutbu zeigt uns auch, wie wir pd.Series.isinjedes Element df['A']in einer Reihe von Werten berücksichtigen können. Dies ergibt dasselbe, wenn unser Wertesatz ein Satz von einem Wert ist, nämlich 'foo'. Es wird jedoch auch verallgemeinert, bei Bedarf größere Wertesätze einzuschließen. Es stellt sich heraus, dass dies immer noch ziemlich schnell ist, obwohl es eine allgemeinere Lösung ist. Der einzige wirkliche Verlust liegt in der Intuitivität für diejenigen, die mit dem Konzept nicht vertraut sind.
mask = df['A'].isin(['foo'])
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Nach wie vor können wir jedoch numpydie Leistung verbessern und dabei praktisch nichts opfern. Wir werden verwendennp.in1d
mask = np.in1d(df['A'].values, ['foo'])
df[mask]
A B C D
0 foo one 0 0
2 foo two 2 4
4 foo two 4 8
6 foo one 6 12
7 foo three 7 14
Timing
Ich werde andere Konzepte, die in anderen Posts erwähnt werden, auch als Referenz aufnehmen.
Code unten
Jede Spalte in dieser Tabelle repräsentiert einen Datenrahmen unterschiedlicher Länge, über den wir jede Funktion testen. Jede Spalte zeigt die relative Zeit, die benötigt wird, wobei die schnellste Funktion einen Basisindex von hat 1.0.
res.div(res.min())
10 30 100 300 1000 3000 10000 30000
mask_standard 2.156872 1.850663 2.034149 2.166312 2.164541 3.090372 2.981326 3.131151
mask_standard_loc 1.879035 1.782366 1.988823 2.338112 2.361391 3.036131 2.998112 2.990103
mask_with_values 1.010166 1.000000 1.005113 1.026363 1.028698 1.293741 1.007824 1.016919
mask_with_values_loc 1.196843 1.300228 1.000000 1.000000 1.038989 1.219233 1.037020 1.000000
query 4.997304 4.765554 5.934096 4.500559 2.997924 2.397013 1.680447 1.398190
xs_label 4.124597 4.272363 5.596152 4.295331 4.676591 5.710680 6.032809 8.950255
mask_with_isin 1.674055 1.679935 1.847972 1.724183 1.345111 1.405231 1.253554 1.264760
mask_with_in1d 1.000000 1.083807 1.220493 1.101929 1.000000 1.000000 1.000000 1.144175
Sie werden feststellen, dass die schnellsten Zeiten zwischen mask_with_valuesund geteilt zu werden scheinenmask_with_in1d
res.T.plot(loglog=True)

Funktionen
def mask_standard(df):
mask = df['A'] == 'foo'
return df[mask]
def mask_standard_loc(df):
mask = df['A'] == 'foo'
return df.loc[mask]
def mask_with_values(df):
mask = df['A'].values == 'foo'
return df[mask]
def mask_with_values_loc(df):
mask = df['A'].values == 'foo'
return df.loc[mask]
def query(df):
return df.query('A == "foo"')
def xs_label(df):
return df.set_index('A', append=True, drop=False).xs('foo', level=-1)
def mask_with_isin(df):
mask = df['A'].isin(['foo'])
return df[mask]
def mask_with_in1d(df):
mask = np.in1d(df['A'].values, ['foo'])
return df[mask]
Testen
res = pd.DataFrame(
index=[
'mask_standard', 'mask_standard_loc', 'mask_with_values', 'mask_with_values_loc',
'query', 'xs_label', 'mask_with_isin', 'mask_with_in1d'
],
columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
dtype=float
)
for j in res.columns:
d = pd.concat([df] * j, ignore_index=True)
for i in res.index:a
stmt = '{}(d)'.format(i)
setp = 'from __main__ import d, {}'.format(i)
res.at[i, j] = timeit(stmt, setp, number=50)
Sonderzeitpunkt
Betrachten Sie den Sonderfall, wenn wir ein einzelnes Nichtobjekt dtypefür den gesamten Datenrahmen haben.
Code unten
spec.div(spec.min())
10 30 100 300 1000 3000 10000 30000
mask_with_values 1.009030 1.000000 1.194276 1.000000 1.236892 1.095343 1.000000 1.000000
mask_with_in1d 1.104638 1.094524 1.156930 1.072094 1.000000 1.000000 1.040043 1.027100
reconstruct 1.000000 1.142838 1.000000 1.355440 1.650270 2.222181 2.294913 3.406735
Es stellt sich heraus, dass sich der Wiederaufbau nach ein paar hundert Reihen nicht lohnt.
spec.T.plot(loglog=True)

Funktionen
np.random.seed([3,1415])
d1 = pd.DataFrame(np.random.randint(10, size=(10, 5)), columns=list('ABCDE'))
def mask_with_values(df):
mask = df['A'].values == 'foo'
return df[mask]
def mask_with_in1d(df):
mask = np.in1d(df['A'].values, ['foo'])
return df[mask]
def reconstruct(df):
v = df.values
mask = np.in1d(df['A'].values, ['foo'])
return pd.DataFrame(v[mask], df.index[mask], df.columns)
spec = pd.DataFrame(
index=['mask_with_values', 'mask_with_in1d', 'reconstruct'],
columns=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
dtype=float
)
Testen
for j in spec.columns:
d = pd.concat([df] * j, ignore_index=True)
for i in spec.index:
stmt = '{}(d)'.format(i)
setp = 'from __main__ import d, {}'.format(i)
spec.at[i, j] = timeit(stmt, setp, number=50)