python - index - pandas drop
Eliminar filas con índices duplicados(Pandas DataFrame y TimeSeries) (4)
Estoy leyendo algunos datos meteorológicos automáticos de la web. Las observaciones ocurren cada 5 minutos y se compilan en archivos mensuales para cada estación meteorológica. Una vez que termine de analizar un archivo, el DataFrame se ve más o menos 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
El problema que tengo es que a veces un científico retrocede y corrige las observaciones, no editando las filas erróneas, sino agregando una fila duplicada al final de un archivo. Un ejemplo simple de tal caso se ilustra a continuación:
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'')
data = {''A'' : range(6), ''B'' : range(6)}
data1 = {''A'' : [20, -30, 40], ''B'' : [-50, 60, -70]}
df1 = pandas.DataFrame(data=data, index=index)
df2 = pandas.DataFrame(data=data1, index=index[:3])
df3 = df1.append(df2)
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 df3
para convertirse evenutally:
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 estoy atascado en calcular fuera de las group_by
o pivot
(o ???) para que funcione.
Tenga en cuenta que hay una mejor respuesta (a continuación) basada en los últimos pandas
Esta debería ser la respuesta aceptada.
Mi respuesta original, que ahora está desactualizada, se conserva como referencia.
Una solución simple es usar drop_duplicates
df4 = df3.drop_duplicates(subset=''rownum'', keep=''last'')
Para mí, esto funcionó rápidamente en grandes conjuntos de datos.
Esto requiere que ''rownum'' sea la columna con duplicados. En el ejemplo modificado, ''rownum'' no tiene duplicados, por lo tanto, nada se elimina. Lo que realmente queremos es tener los ''cols'' en el índice. No he encontrado una manera de decirle a drop_duplicates que solo considere el índice.
Aquí hay una solución que agrega el índice como una columna de marco de datos, descarta los duplicados y luego elimina la nueva columna:
df3 = df3.reset_index().drop_duplicates(subset=''index'', keep=''last'').set_index(''index'')
Y si quiere que las cosas vuelvan a estar en el orden correcto, simplemente llame a sort
en el marco de datos.
df3 = df3.sort()
Lamentablemente, no creo que Pandas le permita a uno abandonar los dúplex de los índices. Sugeriría lo siguiente:
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!
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 en que tengo un MultiIndex
bastante complejo, creo que prefiero el enfoque groupby
. Aquí hay un ejemplo simple 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
Sugeriría usar el método duplicado en el índice de Pandas:
df3 = df3[~df3.index.duplicated(keep=''first'')]
Mientras que todos los demás métodos funcionan, la respuesta actualmente aceptada es de lejos la menos efectiva para el ejemplo proporcionado. Además, aunque el método groupby es solo un poco menos eficiente , creo 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 de mantener.
También se debe tener en cuenta que este método también funciona con MultiIndex
(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