Usuń wiersze pandy z zduplikowanymi indeksami

Jak usunąć wiersze o zduplikowanych wartościach indeksu?

W ramce danych pogodowych poniżej, czasami Naukowiec wraca i koryguje obserwacje-nie edytując błędnych wierszy, ale dodając zduplikowany wiersz na końcu pliku.

Czytam zautomatyzowane dane pogodowe z sieci (obserwacje odbywają się co 5 minut i są kompilowane do comiesięcznych plików dla każdej stacji pogodowej.) Po przetworzeniu pliku ramka Danych wygląda następująco:

                      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

Przykład przypadku duplikatu:

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

I tak potrzebuję df3 aby w końcu stać się:

                       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

Myślałem, że dodanie kolumny liczb wierszy (df3['rownum'] = range(df3.shape[0])) pomoże mi wybrać najbardziej dolny wiersz dla dowolnej wartości DatetimeIndex, ale utknąłem na wymyślaniu group_by lub pivot (lub ???) oświadczenia, aby to zadziałało.

Author: smci, 2012-10-23

7 answers

Sugerowałbym użycie metody duplikowanej na samym indeksie pand:

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

Podczas gdy wszystkie inne metody działają, aktualnie akceptowana ODPOWIEDŹ jest zdecydowanie najmniej wydajna dla podanego przykładu. Ponadto, podczas gdy metoda groupby jest tylko nieco mniej wydajna, uważam, że zduplikowana metoda jest bardziej czytelna.

Na podstawie dostarczonych przykładowych danych:

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

Zauważ, że możesz zachować ostatni element zmieniając argument keep do 'last'.

Należy również zauważyć, że ta metoda działa również z MultiIndex (używając df1, jak podano w przykładzie Paula):

>>> %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
 550
Author: n8yoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-20 03:15:57

To dodaje indeks jako kolumnę ramki danych, upuszcza duplikaty na niej, a następnie usuwa nową kolumnę:

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

Zauważ, że użycie .sort_index() powyżej na końcu jest w razie potrzeby i jest opcjonalne.

 125
Author: D. A.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-06 01:06:20

O mój Boże. To takie proste!

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

Kontynuacja edycji 2013-10-29 W przypadku, gdy mam dość złożony MultiIndex, myślę, że wolę podejście groupby. Oto prosty przykład dla potomności:

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

A oto ważna część

# 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
 73
Author: Paul H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-24 16:51:39

Niestety, nie sądzę, aby Pandy pozwalały zrzucić dups z indeksów. Proponuję:

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!
 4
Author: user128754,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-28 05:17:36

Usuń duplikaty (zachowując najpierw)

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

Usuń duplikaty (zachowując ostatni)

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

Testy: pętle 10K z wykorzystaniem danych 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
 3
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-30 18:00:57

Jeśli ktoś taki jak ja lubi manipulowanie danymi za pomocą notacji kropek pandy( np. Orurowanie), to może się przydać:

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

Umożliwia to łączenie wyrażeń w następujący sposób:

df3.assign(C=2).query('~index.duplicated()').mean()
 1
Author: bbiegel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-26 11:05:46

Można zrzucić duplikaty indeksu za pomocą 'drop_duplikaty':

df.loc[df.index.drop_duplicates(keep='first')]
 -1
Author: Mykola Zotko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-17 14:12:17