Eliminar filas de pandas con índices duplicados

Resuelto Paul H asked hace 12 años • 7 respuestas

¿Cómo eliminar filas con valores de índice duplicados?

En el marco de datos meteorológico a continuación, a veces un científico regresa y corrige las observaciones, no editando las filas erróneas, sino agregando una fila duplicada al final de un archivo.

Estoy leyendo algunos datos meteorológicos automatizados de la web (las observaciones se realizan cada 5 minutos y se compilan en archivos mensuales para cada estación meteorológica). Después de analizar un archivo, el DataFrame se ve así:

                      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

Ejemplo de un caso duplicado:

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

Y entonces necesito df3llegar a ser:

                       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

Pensé que agregar una columna de números de fila ( df3['rownum'] = range(df3.shape[0])) me ayudaría a seleccionar la fila inferior para cualquier valor de DatetimeIndex, pero no puedo descubrir las declaraciones group_byo pivot(o ???) para que eso funcione.

Paul H avatar Oct 24 '12 00:10 Paul H
Aceptado

Sugeriría utilizar el método duplicado en el propio Índice Pandas:

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

Si bien todos los demás métodos funcionan, .drop_duplicateses, con diferencia, el de menor rendimiento para el ejemplo proporcionado. Además, aunque el método groupby tiene un rendimiento ligeramente menor, encuentro que el método duplicado es más legible.

Usando los datos de muestra proporcionados:

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

Tenga en cuenta que puede conservar el último elemento cambiando el argumento keep a 'last'.

También cabe señalar que este método MultiIndextambién funciona (usando df1 como se especifica en el ejemplo de Paul ):

>>> %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
n8yoder avatar Dec 15 '2015 19:12 n8yoder

Esto agrega el índice como una columna de DataFrame, elimina duplicados y luego elimina la nueva columna:

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

Tenga en cuenta que el uso de lo .sort_index()anterior al final es según sea necesario y es opcional.

D. A. avatar Feb 15 '2013 17:02 D. A.

Oh mi. ¡Esto es realmente tan simple!

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

Edición de seguimiento 2013-10-29 En el caso de que tenga un problema bastante complejo MultiIndex, creo que prefiero el groupbyenfoque. He aquí un ejemplo sencillo para la posteridad:

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

y aquí está la parte importante

# 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
Paul H avatar Oct 23 '2012 18:10 Paul H

Eliminar duplicados (mantener primero)

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

Eliminar duplicados (mantenerse al final)

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

Pruebas: bucles de 10k utilizando datos de 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
 avatar Jul 30 '2019 18:07