Estou lendo alguns dados meteorológicos automatizados da web. As observações ocorrem a cada 5 minutos e são compiladas em arquivos mensais para cada estação meteorológica. Quando terminar de analisar um arquivo, o DataFrame se parece com isso:
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
O problema que estou tendo é que, às vezes, um cientista volta e corrige as observações - não editando as linhas incorretas, mas anexando uma linha duplicada ao final de um arquivo. Um exemplo simples desse caso é ilustrado abaixo:
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
E então eu preciso df3
me tornar:
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
Eu pensei que adicionar uma coluna de números de linha ( df3['rownum'] = range(df3.shape[0])
) me ajudaria a selecionar a linha mais inferior para qualquer valor de DatetimeIndex
, mas estou preso em descobrir as instruções group_by
ou pivot
(ou ???) para fazer esse trabalho.
Respostas:
Eu sugeriria o uso do método duplicado no próprio Índice Pandas:
Enquanto todos os outros métodos funcionam, a resposta atualmente aceita é de longe o menos eficiente para o exemplo fornecido. Além disso, enquanto o método groupby é apenas um pouco menos eficiente, acho o método duplicado mais legível.
Usando os dados de amostra fornecidos:
Observe que você pode manter o último elemento alterando o argumento keep.
Deve-se notar também que esse método também funciona
MultiIndex
(usando o df1 conforme especificado no exemplo de Paul ):fonte
loc
pode não ser necessário. Simplesmente façadf3 = df3[~df3.index.duplicated(keep='first')]
, que eliminará todas as linhas com índice duplicado, exceto a primeira ocorrência.Uma solução simples é usar
drop_duplicates
Para mim, isso operou rapidamente em grandes conjuntos de dados.
Isso requer que 'rownum' seja a coluna com duplicatas. No exemplo modificado, 'rownum' não possui duplicatas; portanto, nada é eliminado. O que realmente queremos é que os 'cols' sejam configurados para o índice. Eu não encontrei uma maneira de dizer ao drop_duplicates para considerar apenas o índice.
Aqui está uma solução que adiciona o índice como uma coluna de quadro de dados, elimina duplicatas e remove a nova coluna:
E se você quiser as coisas de volta na ordem correta, basta chamar
sort
o quadro de dados.fonte
df.reset_index().drop_duplicates(cols='index',take_last=True).set_index('index')
reset_index()
adicione as colunas level_0, level_1, etc. E se o seu índice tiver um nome, esse nome será usado no lugar do rótulo "index". Isso torna isso um pouco mais do que uma linha para fazer o certo para qualquer DataFrame.index_label = getattr(df.index, 'names', getattr(df.index, 'name', 'index'))
entãocols=index_label
,set_index(index_labels)
e mesmo isso não é infalível (não funcionará para multi-índices não nomeados).idx = df.index.name or 'index'
, pode-se também fazerdf2 = df.reset_index(); df2.drop_duplicates(idx, inplace=True); df2.set_index(idx, inplace=True)
a evitar as cópias intermédias (devido aoinplace=True
)Oh meu. Isto é realmente tão simples!
Acompanhamento editar 2013-10-29 No caso em que tenho um bastante complexo
MultiIndex
, acho que prefiro agroupby
abordagem. Aqui está um exemplo simples para a posteridade:e aqui está a parte importante
fonte
level=[0,1]
que funcionará se houver dois níveisdf1.groupby(level=[0,1]).last()
. Isto deve ser parte de pandas como uma cortesia paradrop_duplicates
df.index.names
é apenas uma maneira fácil de agrupar por todos os níveis do índice.xarray
para lidar com índices de DateTime duplicados bem que fazemds.resample
eds.groupby
operações falharxarray
contanto que você mudar ogrouped = df3.groupby(level=0)
quegrouped = df3.groupby(dim='time')
ou qualquer que seja a dimensão é que contém duplicatasInfelizmente, não acho que o Pandas permita que alguém jogue fora dos índices. Eu sugeriria o seguinte:
fonte
Se alguém como eu gosta de manipulação de dados encadeados usando a notação de ponto pandas (como canalização), o seguinte pode ser útil:
Isso permite instruções de encadeamento como este:
fonte
TypeError: 'Series' objects are mutable, thus they cannot be hashed
.. Isso realmente funcionou para você?Remover duplicatas (mantendo primeiro)
Remover duplicatas (mantendo por último)
Testes: 10k loops usando dados do OP
fonte