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:

  1. Etikettenauswahl
  2. 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.

  1. df.columns.difference(dlst)

    Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
    
  2. np.setdiff1d(df.columns.values, dlst)

    array(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype=object)
    
  3. df.columns.drop(dlst, errors='ignore')

    Index(['A', 'B', 'C', 'D', 'E', 'F', 'G'], dtype='object')
    
  4. list(set(df.columns.values.tolist()).difference(dlst))

    # does not preserve order
    ['E', 'D', 'B', 'F', 'G', 'A', 'C']
    
  5. [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

  1. df.loc[:, cols]
  2. df[cols]
  3. df.reindex(columns=cols)
  4. 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

  1. ~df.columns.isin(dlst)
  2. ~np.in1d(df.columns.values, dlst)
  3. [x not in dlst for x in df.columns.values.tolist()]
  4. (df.columns.values[:, None] != dlst).all(1)

Spalten von Boolean
Zum Vergleich

bools = [x not in dlst for x in df.columns.values.tolist()]
  1. 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

  1. Ist ein Datenrahmen eine Sammlung von Spalten ?
  2. Ist ein Datenframe eine Sammlung von Zeilen ?
  3. Ist eine Spalte ein Attribut eines Datenrahmens?

Pandas antwortet:

  1. Ja, in jeder Hinsicht
  2. Nein, aber wenn Sie es möchten, können Sie die Methoden .ix , .loc oder .iloc verwenden.
  3. 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)




dataframe