467 Stimmen

Pandas-Zeilen mit doppelten Indizes entfernen

Wie entfernt man Zeilen mit doppelten Indexwerten?

In dem unten stehenden Wetter-DataFrame geht ein Wissenschaftler manchmal zurück und korrigiert Beobachtungen - nicht, indem er die fehlerhaften Zeilen bearbeitet, sondern indem er eine doppelte Zeile an das Ende einer Datei anhängt.

Ich lese gerade einige automatische Wetterdaten aus dem Internet aus (die Beobachtungen erfolgen alle 5 Minuten und werden in monatlichen Dateien für jede Wetterstation zusammengefasst). Nach dem Parsen 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 as pd
import datetime

startdate = datetime.datetime(2001, 1, 1, 0, 0)
enddate = datetime.datetime(2001, 1, 1, 5, 0)
index = pd.date_range(start=startdate, end=enddate, freq='H')
data1 = {'A' : range(6), 'B' : range(6)}
data2 = {'A' : [20, -30, 40], 'B' : [-50, 60, -70]}
df1 = pd.DataFrame(data=data1, index=index)
df2 = pd.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 deshalb brauche ich df3 zu 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 der DatetimeIndex aber ich komme nicht weiter, wenn ich die group_by o pivot (oder ???) Anweisungen, damit das funktioniert.

2 Stimmen

Eine andere Möglichkeit, Duplikate zu erhalten, sind stündliche Daten in der Nacht, wenn die Uhren für die Sommerzeit zurückgestellt werden: 1 AM, 2, 3, 2, wieder 3, 4 ...

1 Stimmen

Wenn Sie sagen "Duplikate entfernen" Ihr Kontext ist hier implizit "behalte das Erste", d.h. drop_duplicates(keep='first') . (Das ist nicht immer der Fall, manchmal ist es schwieriger, aus den anderen Feldern herauszufinden, welche Zeile beibehalten werden soll, oder mehrere zusammenzuführen, indem man NAs aus verschiedenen Zeilen ausfüllt).

853voto

n8yoder Punkte 8983

Ich würde vorschlagen, die vervielfältigt Methode auf dem Pandas Index selbst:

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

Alle anderen Methoden funktionieren zwar, .drop_duplicates ist für das angegebene Beispiel bei weitem am wenigsten leistungsfähig. Außerdem, während die Groupby-Verfahren nur geringfügig weniger leistungsfähig ist, finde ich die duplizierte Methode besser lesbar.

Verwenden Sie die 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 behalten können, indem Sie das Argument keep in 'last' .

Es ist auch zu beachten, dass diese Methode funktioniert mit MultiIndex auch (unter Verwendung von df1 wie in Das Beispiel des Paulus ):

>>> %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

10 Stimmen

loc nicht notwendig sein. Einfach tun df3 = df3[~df3.index.duplicated(keep='first')] , wodurch alle Zeilen mit doppeltem Index außer dem ersten Vorkommen gelöscht werden.

1 Stimmen

Wäre es sinnvoll, dies für sehr große Zeitreihen zu verwenden, bei denen die Duplikate normalerweise nur die ersten oder letzten Werte sind?

8 Stimmen

Was macht ~ in df3 = df3.loc[~df3.index.duplicated(keep='first')], wenn jemand die Frage beantworten kann?

140voto

D. A. Punkte 3161

Dies fügt den Index als DataFrame-Spalte hinzu, löscht Duplikate in dieser Spalte 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() am Ende des Textes ist fakultativ und bedarf keiner weiteren Angaben.

11 Stimmen

Eine andere Variante davon ist : df.reset_index().drop_duplicates(cols='index',take_last=True‌​).set_index('index')

2 Stimmen

Diese Methode funktioniert zwar, erstellt aber auch zwei temporäre Kopien des DataFrame und ist deutlich weniger leistungsfähig als die als Alternative vorgeschlagenen Methoden duplicated index oder groupby.

1 Stimmen

Wenn Ihr Index ein MultiIndex ist, reset_index() fügt die Spalten level_0, level_1, etc. hinzu. Und wenn Ihr Index einen Namen hat, wird dieser Name anstelle der Bezeichnung "index" verwendet. Das macht dies ein bisschen mehr als ein Einzeiler, um es richtig für jede DataFrame zu tun. 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 bei unbenannten Multiindizes).

85voto

Paul H Punkte 59192

Oje. Das ist wirklich 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

Nachbearbeitung 2013-10-29 In dem Fall, dass ich eine ziemlich komplexe MultiIndex Ich glaube, ich bevorzuge die groupby Ansatz. Hier 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

0 Stimmen

Wenn sie Namen haben, andernfalls (wenn ein Name keiner ist) sagen wir level=[0,1] wird funktionieren, wenn es 2 Ebenen gibt df1.groupby(level=[0,1]).last() . Dies sollte Teil von Pandas sein, als Ergänzung zu drop_duplicates

0 Stimmen

@dashesy Ja. Mit df.index.names ist nur eine einfache Möglichkeit, nach allen Ebenen des Index zu gruppieren.

0 Stimmen

Tolle Lösung, vielen Dank! Ich möchte auch hinzufügen, dass dies funktioniert in xarray auch für den Umgang mit doppelten DateTime-Indizes, die die ds.resample et ds.groupby Operationen scheitern

7voto

Entfernen von Duplikaten (Keeping First)

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

Duplikate entfernen (zuletzt behalten)

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

Prüfungen: 10k-Schleifen unter Verwendung der 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

4voto

user128754 Punkte 51

Leider glaube ich nicht, dass Pandas es erlaubt, Dups aus den Indizes zu entfernen. Ich würde das Folgende 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!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X