python - tutorial - pandas series
Löschen Sie die Spalte aus Pandas DataFrame mit del df.column_name (8)
Pandas 0.21+ Antwort
Die Pandas-Version 0.21 hat die Drop-Methode leicht geändert, um sowohl den index
als auch die reindex
, dass sie der Signatur der rename
und reindex
Methoden entsprechen.
df.drop(columns=['column_a', 'column_c'])
Persönlich bevorzuge ich die Verwendung des axis
Parameters, um Spalten oder Indizes zu bezeichnen, da dies der vorherrschende Schlüsselwortparameter ist, der in fast allen Pandas-Methoden verwendet wird. Aber jetzt haben Sie einige Optionen in der Version 0.21.
Beim Löschen einer Spalte in einem DataFrame verwende ich:
del df['column_name']
und das funktioniert super. Warum kann ich nicht verwenden:
del df.column_name
Da Sie auf die Spalte / Series als df.column_name
, erwarte ich, dass dies funktioniert.
TL; DR
Viel Aufwand, um eine marginal effizientere Lösung zu finden. Schwierig, die zusätzliche Komplexität zu rechtfertigen und gleichzeitig die Einfachheit von df.drop(dlst, 1, errors='ignore')
opfern
df.reindex_axis(np.setdiff1d(df.columns.values, dlst), 1)
Präambel
Das Löschen einer Spalte ist semantisch dasselbe wie das Auswählen der anderen Spalten. Ich werde ein paar zusätzliche Methoden zeigen.
Ich werde mich auch auf die allgemeine Lösung konzentrieren, mehrere Spalten gleichzeitig zu löschen und den Versuch zu ermöglichen, nicht vorhandene Spalten zu löschen.
Die Verwendung dieser Lösungen ist allgemein und wird auch für den einfachen Fall funktionieren.
Konfiguration
Betrachten Sie den pd.DataFrame
df
und die Liste, um dlst
zu löschen
df = pd.DataFrame(dict(zip('ABCDEFGHIJ', range(1, 11))), range(3))
dlst = list('HIJKLM')
df
A B C D E F G H I J
0 1 2 3 4 5 6 7 8 9 10
1 1 2 3 4 5 6 7 8 9 10
2 1 2 3 4 5 6 7 8 9 10
dlst
['H', 'I', 'J', 'K', 'L', 'M']
Das Ergebnis sollte wie folgt aussehen:
df.drop(dlst, 1, errors='ignore')
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
Da ich das Löschen einer Spalte mit dem Auswählen der anderen Spalten gleichstelle, werde ich sie in zwei Typen aufteilen:
- Etikettenauswahl
- Boolesche Auswahl
Etikettenauswahl
Wir beginnen mit der Herstellung der Liste / des Arrays von Labels, die die Spalten darstellen, die wir behalten möchten, und ohne die Spalten, die wir löschen wollen.
df.columns.difference(dlst)
Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
np.setdiff1d(df.columns.values, dlst)
array(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype=object)
df.columns.drop(dlst, errors='ignore')
Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
list(set(df.columns.values.tolist()).difference(dlst))
# does not preserve order ['E', 'D', 'B', 'F', 'G', 'A', 'C']
[x for x in df.columns.values.tolist() if x not in dlst]
['A', 'B', 'C', 'D', 'E', 'F', 'G']
Spalten von Labels
Um den Auswahlprozess zu vergleichen, nehmen wir an:
cols = [x for x in df.columns.values.tolist() if x not in dlst]
Dann können wir bewerten
-
df.loc[:, cols]
-
df[cols]
-
df.reindex(columns=cols)
-
df.reindex_axis(cols, 1)
Welche alle bewerten zu:
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
Boolesches Slice
Wir können ein Array / eine Liste von booleschen Werten für das Slicing konstruieren
-
~df.columns.isin(dlst)
-
~np.in1d(df.columns.values, dlst)
-
[x not in dlst for x in df.columns.values.tolist()]
-
(df.columns.values[:, None] != dlst).all(1)
Spalten von Boolean
Zum Vergleich
bools = [x not in dlst for x in df.columns.values.tolist()]
-
df.loc[: bools]
Welche alle bewerten zu:
A B C D E F G
0 1 2 3 4 5 6 7
1 1 2 3 4 5 6 7
2 1 2 3 4 5 6 7
Robustes Timing
Funktionen
setdiff1d = lambda df, dlst: np.setdiff1d(df.columns.values, dlst)
difference = lambda df, dlst: df.columns.difference(dlst)
columndrop = lambda df, dlst: df.columns.drop(dlst, errors='ignore')
setdifflst = lambda df, dlst: list(set(df.columns.values.tolist()).difference(dlst))
comprehension = lambda df, dlst: [x for x in df.columns.values.tolist() if x not in dlst]
loc = lambda df, cols: df.loc[:, cols]
slc = lambda df, cols: df[cols]
ridx = lambda df, cols: df.reindex(columns=cols)
ridxa = lambda df, cols: df.reindex_axis(cols, 1)
isin = lambda df, dlst: ~df.columns.isin(dlst)
in1d = lambda df, dlst: ~np.in1d(df.columns.values, dlst)
comp = lambda df, dlst: [x not in dlst for x in df.columns.values.tolist()]
brod = lambda df, dlst: (df.columns.values[:, None] != dlst).all(1)
Testen
res1 = pd.DataFrame(
index=pd.MultiIndex.from_product([
'loc slc ridx ridxa'.split(),
'setdiff1d difference columndrop setdifflst comprehension'.split(),
], names=['Select', 'Label']),
columns=[10, 30, 100, 300, 1000],
dtype=float
)
res2 = pd.DataFrame(
index=pd.MultiIndex.from_product([
'loc'.split(),
'isin in1d comp brod'.split(),
], names=['Select', 'Label']),
columns=[10, 30, 100, 300, 1000],
dtype=float
)
res = res1.append(res2).sort_index()
dres = pd.Series(index=res.columns, name='drop')
for j in res.columns:
dlst = list(range(j))
cols = list(range(j // 2, j + j // 2))
d = pd.DataFrame(1, range(10), cols)
dres.at[j] = timeit('d.drop(dlst, 1, errors="ignore")', 'from __main__ import d, dlst', number=100)
for s, l in res.index:
stmt = '{}(d, {}(d, dlst))'.format(s, l)
setp = 'from __main__ import d, dlst, {}, {}'.format(s, l)
res.at[(s, l), j] = timeit(stmt, setp, number=100)
rs = res / dres
rs
10 30 100 300 1000
Select Label
loc brod 0.747373 0.861979 0.891144 1.284235 3.872157
columndrop 1.193983 1.292843 1.396841 1.484429 1.335733
comp 0.802036 0.732326 1.149397 3.473283 25.565922
comprehension 1.463503 1.568395 1.866441 4.421639 26.552276
difference 1.413010 1.460863 1.587594 1.568571 1.569735
in1d 0.818502 0.844374 0.994093 1.042360 1.076255
isin 1.008874 0.879706 1.021712 1.001119 0.964327
setdiff1d 1.352828 1.274061 1.483380 1.459986 1.466575
setdifflst 1.233332 1.444521 1.714199 1.797241 1.876425
ridx columndrop 0.903013 0.832814 0.949234 0.976366 0.982888
comprehension 0.777445 0.827151 1.108028 3.473164 25.528879
difference 1.086859 1.081396 1.293132 1.173044 1.237613
setdiff1d 0.946009 0.873169 0.900185 0.908194 1.036124
setdifflst 0.732964 0.823218 0.819748 0.990315 1.050910
ridxa columndrop 0.835254 0.774701 0.907105 0.908006 0.932754
comprehension 0.697749 0.762556 1.215225 3.510226 25.041832
difference 1.055099 1.010208 1.122005 1.119575 1.383065
setdiff1d 0.760716 0.725386 0.849949 0.879425 0.946460
setdifflst 0.710008 0.668108 0.778060 0.871766 0.939537
slc columndrop 1.268191 1.521264 2.646687 1.919423 1.981091
comprehension 0.856893 0.870365 1.290730 3.564219 26.208937
difference 1.470095 1.747211 2.886581 2.254690 2.050536
setdiff1d 1.098427 1.133476 1.466029 2.045965 3.123452
setdifflst 0.833700 0.846652 1.013061 1.110352 1.287831
fig, axes = plt.subplots(2, 2, figsize=(8, 6), sharey=True)
for i, (n, g) in enumerate([(n, g.xs(n)) for n, g in rs.groupby('Select')]):
ax = axes[i // 2, i % 2]
g.plot.bar(ax=ax, title=n)
ax.legend_.remove()
fig.tight_layout()
Dies ist relativ zu der Zeit, die zum Ausführen von df.drop(dlst, 1, errors='ignore')
. Es sieht so aus, als würden wir nach all dem Aufwand die Leistung nur moderat verbessern.
In der Tat verwenden die besten Lösungen reindex
oder reindex_axis
auf der Hack- list(set(df.columns.values.tolist()).difference(dlst))
. Eine nahe Sekunde und immer noch marginal besser als drop
ist np.setdiff1d
.
rs.idxmin().pipe(
lambda x: pd.DataFrame(
dict(idx=x.values, val=rs.lookup(x.values, x.index)),
x.index
)
)
idx val
10 (ridx, setdifflst) 0.653431
30 (ridxa, setdifflst) 0.746143
100 (ridxa, setdifflst) 0.816207
300 (ridx, setdifflst) 0.780157
1000 (ridxa, setdifflst) 0.861622
Ab Version 0.16.1 können Sie tun
df.drop(['column_name'], axis = 1, inplace = True, errors = 'ignore')
Der beste Weg, dies in Pandas zu tun, ist drop
:
df = df.drop('column_name', 1)
Dabei ist 1
die Achsennummer ( 0
für Zeilen und 1
für Spalten).
Um die Spalte zu löschen, ohne df
neu df
, können Sie df
tun:
df.drop('column_name', axis=1, inplace=True)
Um nach der Spaltennummer anstatt nach der Spaltenbezeichnung zu suchen, versuchen Sie dies zu löschen, zB die 1., 2. und 4. Spalte:
df.drop(df.columns[[0, 1, 3]], axis=1) # df.columns is zero-based pd.Index
Die eigentliche Frage gestellt, von den meisten Antworten hier fehlt ist:
Warum kann ich del df.column_name
nicht verwenden?
Zuerst müssen wir das Problem verstehen, was uns erfordert, in die Python-Magie-Methoden einzutauchen .
Wie Wes in seiner Antwort darauf hinweist, bildet del df['column']
die python- magische Methode df.__delitem__('column')
die in Pandas implementiert ist, um die Spalte zu df.__delitem__('column')
Wie oben im Link zu python-magischen Methoden erwähnt :
In der Tat sollte del wegen der prekären Umstände, unter denen es aufgerufen wird, fast nie verwendet werden; Benutze es mit Vorsicht!
Sie könnten argumentieren, dass del df['column_name']
nicht verwendet oder del df.column_name
sollte, und dabei sollte del df.column_name
nicht einmal berücksichtigt werden.
del df.column_name
könnte del df.column_name
jedoch in pandas mit der magischen Methode __delattr__
. Dies führt jedoch zu bestimmten Problemen, die die del df['column_name']
Implementierung bereits hat, aber in geringerem Maße.
Beispiel Problem
Was passiert, wenn ich eine Spalte in einem Datenrahmen namens "dtypes" oder "columns" definiere.
Dann nehme ich an, dass ich diese Spalten löschen möchte.
del df.dtypes
würde die Methode __delattr__
, als ob sie das Attribut "dtypes" oder die Spalte "dtypes" löschen müsste.
Architektonische Fragen hinter diesem Problem
- Ist ein Datenrahmen eine Sammlung von Spalten ?
- Ist ein Datenframe eine Sammlung von Zeilen ?
- Ist eine Spalte ein Attribut eines Datenrahmens?
Pandas antwortet:
- Ja, in jeder Hinsicht
- Nein, aber wenn Sie es möchten, können Sie die Methoden
.ix
,.loc
oder.iloc
verwenden. - Vielleicht möchten Sie Daten lesen ? Dann ja , es sei denn, der Name des Attributs wird bereits von einem anderen zum Datenrahmen gehörenden Attribut übernommen. Möchten Sie Daten ändern ? Dann nein .
TLDR;
Du kannst del df.column_name
nicht tun, weil Pandas eine ziemlich wild gewachsene Architektur hat, die neu überdacht werden muss, damit diese Art von kognitiver Dissonanz ihren Benutzern nicht del df.column_name
.
Protip:
Verwenden Sie nicht df.column_name, es kann hübsch sein, aber es verursacht kognitive Dissonanz
Zen of Python zitiert das passt hier rein:
Es gibt mehrere Möglichkeiten, eine Spalte zu löschen.
Es sollte eine - und vorzugsweise nur eine - offensichtliche Möglichkeit geben, dies zu tun.
Spalten sind manchmal Attribute, aber manchmal nicht.
Sonderfälle sind nicht speziell genug, um die Regeln zu brechen.
Löscht del df.dtypes
das dtypes-Attribut oder die dtypes-Spalte?
Angesichts der Mehrdeutigkeit, lehnen Sie die Versuchung ab zu erraten.
Eine nette Ergänzung ist die Möglichkeit, Spalten nur dann zu löschen , wenn sie existieren . Auf diese Weise können Sie mehr Anwendungsfälle abdecken und nur die vorhandenen Spalten aus den an sie übergebenen Labels löschen:
füge einfach errors = 'ignore' hinzu , zB:
df.drop(['col_name_1','col_name_2',...,'col_name_N'],inplace=True,axis=1,errors='ignore')
- Das ist neu von Pandas 0.16.1, Dokumente sind here
Es ist schwierig, del df.column_name
einfach als Ergebnis von syntaktischen Einschränkungen in Python zu machen. del df[name]
wird in df.__delitem__(name)
unter die Deckblätter von Python übersetzt.
In Pandas 0.16.1+ können Sie Spalten nur dann löschen, wenn sie für die von @eiTanLaVi veröffentlichte Lösung existieren. Vor dieser Version können Sie dasselbe Ergebnis über ein bedingtes Listenverständnis erreichen:
df.drop([col for col in ['col_name_1','col_name_2',...,'col_name_N'] if col in df],
axis=1, inplace=True)