Entfernen Sie Pandas-Zeilen mit doppelten Indizes

Lesezeit: 7 Minuten

Benutzer-Avatar
Paul H

Wie entferne ich Zeilen mit doppelten Indexwerten?

Im Wetterdatenrahmen unten geht ein Wissenschaftler manchmal zurück und korrigiert Beobachtungen – nicht durch Bearbeiten der fehlerhaften Zeilen, sondern durch Anhängen einer doppelten Zeile an das Ende einer Datei.

Ich lese einige automatisierte Wetterdaten aus dem Internet (Beobachtungen erfolgen alle 5 Minuten und werden für jede Wetterstation in monatlichen Dateien zusammengestellt.) Nach dem Analysieren einer Datei sieht der DataFrame wie folgt aus:

                      Sta  Precip1hr  Precip5min  Temp  DewPnt  WindSpd  WindDir  AtmPress
Date                                                                                      
2001-01-01 00:00:00  KPDX          0           0     4       3        0        0     30.31
2001-01-01 00:05:00  KPDX          0           0     4       3        0        0     30.30
2001-01-01 00:10:00  KPDX          0           0     4       3        4       80     30.30
2001-01-01 00:15:00  KPDX          0           0     3       2        5       90     30.30
2001-01-01 00:20:00  KPDX          0           0     3       2       10      110     30.28

Beispiel für einen doppelten Fall:

import pandas 
import datetime

startdate = datetime.datetime(2001, 1, 1, 0, 0)
enddate = datetime.datetime(2001, 1, 1, 5, 0)
index = pandas.DatetimeIndex(start=startdate, end=enddate, freq='H')
data1 = {'A' : range(6), 'B' : range(6)}
data2 = {'A' : [20, -30, 40], 'B' : [-50, 60, -70]}
df1 = pandas.DataFrame(data=data1, index=index)
df2 = pandas.DataFrame(data=data2, index=index[:3])
df3 = df2.append(df1)

df3
                       A   B
2001-01-01 00:00:00   20 -50
2001-01-01 01:00:00  -30  60
2001-01-01 02:00:00   40 -70
2001-01-01 03:00:00    3   3
2001-01-01 04:00:00    4   4
2001-01-01 05:00:00    5   5
2001-01-01 00:00:00    0   0
2001-01-01 01:00:00    1   1
2001-01-01 02:00:00    2   2

Und so brauche ich df3 schließlich werden:

                       A   B
2001-01-01 00:00:00    0   0
2001-01-01 01:00:00    1   1
2001-01-01 02:00:00    2   2
2001-01-01 03:00:00    3   3
2001-01-01 04:00:00    4   4
2001-01-01 05:00:00    5   5

Ich dachte, dass das Hinzufügen einer Spalte mit Zeilennummern (df3['rownum'] = range(df3.shape[0])) würde mir helfen, die unterste Zeile für jeden Wert von auszuwählen DatetimeIndexaber ich hänge daran, das herauszufinden group_by oder pivot (oder ???) Anweisungen, damit das funktioniert.

  • Eine andere Möglichkeit, Duplikate zu erhalten, sind stündliche Daten in der Nacht, wenn die Uhren auf Sommerzeit zurückgestellt werden: 1 Uhr morgens, 2, 3, 2, wieder 3, 4 …

    – Denis

    28. August 2017 um 9:42 Uhr

  • Wenn du sagst “Duplikate entfernen”, Ihr Kontext hier ist implizit “Behalte den Ersten”. dh drop_duplicates(keep='first'). (Das ist nicht immer der Fall, manchmal ist es schwieriger, anhand der anderen Felder herauszufinden, welche Zeile beibehalten werden soll, oder mehrere zusammenzuführen und NAs aus verschiedenen Zeilen zu füllen).

    – smci

    21. Januar 2021 um 19:54 Uhr


Benutzer-Avatar
n8yoder

Ich würde vorschlagen, die zu verwenden dupliziert Methode auf dem Pandas-Index selbst:

df3 = df3[~df3.index.duplicated(keep='first')]

Während alle anderen Methoden funktionieren, .drop_duplicates ist bei weitem die leistungsschwächste für das bereitgestellte Beispiel. Während die groupby-Methode nur geringfügig weniger performant ist, finde ich die duplizierte Methode außerdem lesbarer.

Verwenden der bereitgestellten Beispieldaten:

>>> %timeit df3.reset_index().drop_duplicates(subset="index", keep='first').set_index('index')
1000 loops, best of 3: 1.54 ms per loop

>>> %timeit df3.groupby(df3.index).first()
1000 loops, best of 3: 580 µs per loop

>>> %timeit df3[~df3.index.duplicated(keep='first')]
1000 loops, best of 3: 307 µs per loop

Beachten Sie, dass Sie das letzte Element beibehalten können, indem Sie das Argument keep in ändern 'last'.

Es sollte auch beachtet werden, dass diese Methode mit funktioniert MultiIndex auch (unter Verwendung von df1 wie in Pauls Beispiel angegeben):

>>> %timeit df1.groupby(level=df1.index.names).last()
1000 loops, best of 3: 771 µs per loop

>>> %timeit df1[~df1.index.duplicated(keep='last')]
1000 loops, best of 3: 365 µs per loop

  • loc möglicherweise nicht erforderlich. Einfach machen df3 = df3[~df3.index.duplicated(keep='first')]wodurch alle Zeilen mit doppeltem Index außer dem ersten Vorkommen gelöscht werden.

    – Lingjiankong

    16. September 2019 um 18:30 Uhr

  • was macht ~ in df3 = df3.loc[~df3.index.duplicated(keep=’first’)] wenn jemand nichts dagegen hat zu antworten?

    – jsl5703

    27. Februar 2020 um 1:38 Uhr

  • @ jsl5703 Es kehrt die Maske um. Also wird alles, was wahr war, falsch und umgekehrt. In diesem Fall bedeutet dies, dass wir diejenigen auswählen, die nicht gemäß der Methode dupliziert werden.

    – n8yoder

    27. Februar 2020 um 2:35 Uhr

  • Was für eine Hexerei ist das, eine bitweise (~) Operation auf einem DataFrame zu verwenden und es funktioniert sogar. Haben Sie eine positive Stimme und einen Kommentar, denn eine positive Stimme scheint nicht genug zu sein. Bonus für %timeit hinzugefügt.

    – Harper

    21. September 2020 um 13:30 Uhr


  • Für diejenigen, die “Ketten” bevorzugen: df3.query("~index.duplicated(keep='first')")

    – MikeGM

    19. März 2021 um 11:47 Uhr


Benutzer-Avatar
DA

Dies fügt den Index als DataFrame-Spalte hinzu, löscht Duplikate darauf und entfernt dann die neue Spalte:

df = (df.reset_index()
        .drop_duplicates(subset="index", keep='last')
        .set_index('index').sort_index())

Beachten Sie, dass die Verwendung von .sort_index() oben am Ende ist nach Bedarf und optional.

  • Eine weitere Variante dazu ist: df.reset_index().drop_duplicates(cols='index',take_last=True).set_index('index')

    – Luciano

    20. Februar 2014 um 10:35 Uhr

  • Obwohl diese Methode funktioniert, erstellt sie auch zwei temporäre Kopien des DataFrame und ist deutlich weniger leistungsfähig als die Verwendung der duplizierten Index- oder Groupby-Methoden, die als alternative Antworten vorgeschlagen werden.

    – n8yoder

    19. Dezember 2015 um 21:26 Uhr

  • Wenn Ihr Index ein MultiIndex ist, reset_index() fügt die Spalten level_0, level_1 usw. hinzu. Und wenn Ihr Index einen Namen hat, wird dieser Name anstelle des “Index”-Labels verwendet. Das macht dies zu etwas mehr als einem Einzeiler, um es für jeden DataFrame richtig zu machen. index_label = getattr(df.index, 'names', getattr(df.index, 'name', 'index')) dann cols=index_label dann set_index(index_labels) und selbst das ist nicht narrensicher (funktioniert nicht für unbenannte Multiindizes).

    – Kochfelder

    23. Mai 2016 um 22:35 Uhr


  • Das Verschieben des Indexes in eine Spalte, das Löschen von Duplikaten und das Zurücksetzen des Indexes war großartig, das war genau das, was ich brauchte!

    – SienaD.

    27. Dezember 2016 um 19:03 Uhr

  • Gegeben idx = df.index.name or 'index'könnte man auch machen df2 = df.reset_index(); df2.drop_duplicates(idx, inplace=True); df2.set_index(idx, inplace=True) Um die Zwischenkopien zu vermeiden (aufgrund der inplace=True)

    – Anakhand

    27. Mai 2019 um 16:16 Uhr


Benutzer-Avatar
Paul H

Oh mein. Das ist eigentlich so einfach!

grouped = df3.groupby(level=0)
df4 = grouped.last()
df4
                      A   B  rownum

2001-01-01 00:00:00   0   0       6
2001-01-01 01:00:00   1   1       7
2001-01-01 02:00:00   2   2       8
2001-01-01 03:00:00   3   3       3
2001-01-01 04:00:00   4   4       4
2001-01-01 05:00:00   5   5       5

Folgebearbeitung 2013-10-29
In dem Fall, wo ich eine ziemlich komplexe habe MultiIndexich glaube, ich bevorzuge die groupby sich nähern. Hier ist ein einfaches Beispiel für die Nachwelt:

import numpy as np
import pandas

# fake index
idx = pandas.MultiIndex.from_tuples([('a', letter) for letter in list('abcde')])

# random data + naming the index levels
df1 = pandas.DataFrame(np.random.normal(size=(5,2)), index=idx, columns=['colA', 'colB'])
df1.index.names = ['iA', 'iB']

# artificially append some duplicate data
df1 = df1.append(df1.select(lambda idx: idx[1] in ['c', 'e']))
df1
#           colA      colB
#iA iB                    
#a  a  -1.297535  0.691787
#   b  -1.688411  0.404430
#   c   0.275806 -0.078871
#   d  -0.509815 -0.220326
#   e  -0.066680  0.607233
#   c   0.275806 -0.078871  # <--- dup 1
#   e  -0.066680  0.607233  # <--- dup 2

und hier ist der wichtige Teil

# group the data, using df1.index.names tells pandas to look at the entire index
groups = df1.groupby(level=df1.index.names)  
groups.last() # or .first()
#           colA      colB
#iA iB                    
#a  a  -1.297535  0.691787
#   b  -1.688411  0.404430
#   c   0.275806 -0.078871
#   d  -0.509815 -0.220326
#   e  -0.066680  0.607233

  • wenn sie Namen haben, sonst (wenn ein Name None ist), sagen wir mal level=[0,1] funktioniert, wenn es 2 Ebenen gibt df1.groupby(level=[0,1]).last(). Dies sollte Teil von Pandas als Ergänzung sein drop_duplicates

    – schneidig

    12. April 2015 um 18:38 Uhr

  • @dassy ja. Verwenden df.index.names ist nur eine einfache Möglichkeit, nach allen Ebenen des Index zu gruppieren.

    – PaulH

    12. April 2015 um 18:45 Uhr

  • Super Lösung, danke! Ich werde auch hinzufügen, dass dies funktioniert xarray auch für den Umgang mit doppelten DateTime-Indizes, die make ds.resample und ds.groupby Operationen scheitern

    – drg

    15. Mai 2018 um 23:26 Uhr

  • Ergänzung zu meinem früheren Kommentar: Es funktioniert in xarray solange du das änderst grouped = df3.groupby(level=0) zu grouped = df3.groupby(dim='time') oder was auch immer die Dimension ist, die Duplikate enthält

    – drg

    15. Mai 2018 um 23:34 Uhr

Duplikate entfernen (Keeping First)

idx = np.unique( df.index.values, return_index = True )[1]
df = df.iloc[idx]

Duplikate entfernen (Keeping Last)

df = df[::-1]
df = df.iloc[ np.unique( df.index.values, return_index = True )[1] ]

Tests: 10.000 Schleifen mit den Daten von OP

numpy method - 3.03 seconds
df.loc[~df.index.duplicated(keep='first')] - 4.43 seconds
df.groupby(df.index).first() - 21 seconds
reset_index() method - 29 seconds

Leider glaube ich nicht, dass Pandas es einem erlaubt, Dups aus den Indizes zu entfernen. Ich würde folgendes vorschlagen:

df3 = df3.reset_index() # makes date column part of your data
df3.columns = ['timestamp','A','B','rownum'] # set names
df3 = df3.drop_duplicates('timestamp',take_last=True).set_index('timestamp') #done!

Benutzer-Avatar
bbiegel

Wenn jemand wie ich die verkettete Datenmanipulation mit der Pandas-Punktnotation (wie Piping) mag, dann kann das Folgende nützlich sein:

df3 = df3.query('~index.duplicated()')

Dies ermöglicht das Verketten von Anweisungen wie folgt:

df3.assign(C=2).query('~index.duplicated()').mean()

Benutzer-Avatar
Bernie

Ich hatte die Erfahrung mit demselben Fehler, und nachdem ich in jedes df eingetaucht war, stellte sich heraus, dass eine der 2 Spalten mit demselben Namen hatte, Sie erwähnen, dass Sie einige Spalten löschen, wahrscheinlich könnte dies ein Grund sein.

1101160cookie-checkEntfernen Sie Pandas-Zeilen mit doppelten Indizes

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy