Adicionar datas ausentes ao quadro de dados do pandas


126

Meus dados podem ter vários eventos em uma determinada data ou NENHUM evento em uma data. Pego esses eventos, conto por data e os traço. No entanto, quando os traço, minhas duas séries nem sempre correspondem.

idx = pd.date_range(df['simpleDate'].min(), df['simpleDate'].max())
s = df.groupby(['simpleDate']).size()

No código acima, o idx se torna um intervalo de, digamos, 30 datas. 01-09-2013 a 30-09-2013 No entanto, S pode ter apenas 25 ou 26 dias porque nenhum evento ocorreu em uma determinada data. Em seguida, recebo um AssertionError, pois os tamanhos não correspondem quando tento traçar:

fig, ax = plt.subplots()    
ax.bar(idx.to_pydatetime(), s, color='green')

Qual é a maneira correta de resolver isso? Eu quero remover datas sem valores do IDX ou (o que eu preferiria) é adicionar à série a data que falta com uma contagem de 0. Prefiro ter um gráfico completo de 30 dias com 0 valores. Se essa abordagem for correta, alguma sugestão de como começar? Preciso de algum tipo de reindexfunção dinâmica ?

Aqui está um trecho de S ( df.groupby(['simpleDate']).size() ), observe que não há entradas para 04 e 05.

09-02-2013     2
09-03-2013    10
09-06-2013     5
09-07-2013     1

Respostas:


254

Você poderia usar Series.reindex:

import pandas as pd

idx = pd.date_range('09-01-2013', '09-30-2013')

s = pd.Series({'09-02-2013': 2,
               '09-03-2013': 10,
               '09-06-2013': 5,
               '09-07-2013': 1})
s.index = pd.DatetimeIndex(s.index)

s = s.reindex(idx, fill_value=0)
print(s)

rendimentos

2013-09-01     0
2013-09-02     2
2013-09-03    10
2013-09-04     0
2013-09-05     0
2013-09-06     5
2013-09-07     1
2013-09-08     0
...

23
reindexé uma função incrível. Ele pode (1) reordenar os dados existentes para corresponder a um novo conjunto de etiquetas, (2) inserir novas linhas onde nenhuma etiqueta existia anteriormente, (3) preencher dados para etiquetas ausentes (incluindo preenchimento para frente / trás) (4) selecionar linhas por etiqueta!
Unutbu

@unutbu Isso responde parte de uma pergunta que eu também tinha, obrigado! Mas estava se perguntando se você sabia como criar dinamicamente uma lista de datas com eventos?
Nick Duddy

2
Porém, há um problema (ou bug) na reindexação: ele não funciona com datas anteriores a 1/1/1970, portanto, neste caso, o df.resample () funciona perfeitamente.
Sergey Gulbin

1
você pode usar isso para que o idx pule a inserção manual das datas de início e término:idx = pd.date_range(df.index.min(), df.index.max())
Reveille

Largando o link da documentação aqui, para salvar a pesquisa: pandas.pydata.org/pandas-docs/stable/reference/api/…
Harm te Molder

40

Uma solução mais rápida é usar .asfreq(). Isso não requer a criação de um novo índice para chamar dentro .reindex().

# "broken" (staggered) dates
dates = pd.Index([pd.Timestamp('2012-05-01'), 
                  pd.Timestamp('2012-05-04'), 
                  pd.Timestamp('2012-05-06')])
s = pd.Series([1, 2, 3], dates)

print(s.asfreq('D'))
2012-05-01    1.0
2012-05-02    NaN
2012-05-03    NaN
2012-05-04    2.0
2012-05-05    NaN
2012-05-06    3.0
Freq: D, dtype: float64

1
Eu realmente prefiro esse método; você evita ter que ligar, date_rangepois usa implicitamente o primeiro e o último índice como o início e o fim (que é o que você quase sempre deseja).
Michael Hays

Método muito limpo e profissional. Funciona bem com o uso de interpolar depois também.
msarafzadeh 12/07

26

Uma questão é que reindexfalhará se houver valores duplicados. Digamos que estamos trabalhando com dados com timestamp, que queremos indexar por data:

df = pd.DataFrame({
    'timestamps': pd.to_datetime(
        ['2016-11-15 1:00','2016-11-16 2:00','2016-11-16 3:00','2016-11-18 4:00']),
    'values':['a','b','c','d']})
df.index = pd.DatetimeIndex(df['timestamps']).floor('D')
df

rendimentos

            timestamps             values
2016-11-15  "2016-11-15 01:00:00"  a
2016-11-16  "2016-11-16 02:00:00"  b
2016-11-16  "2016-11-16 03:00:00"  c
2016-11-18  "2016-11-18 04:00:00"  d

Devido à 2016-11-16data duplicada , uma tentativa de reindexar:

all_days = pd.date_range(df.index.min(), df.index.max(), freq='D')
df.reindex(all_days)

falha com:

...
ValueError: cannot reindex from a duplicate axis

(com isso, significa que o índice tem duplicatas, não que seja um dup)

Em vez disso, podemos usar .locpara procurar entradas para todas as datas no intervalo:

df.loc[all_days]

rendimentos

            timestamps             values
2016-11-15  "2016-11-15 01:00:00"  a
2016-11-16  "2016-11-16 02:00:00"  b
2016-11-16  "2016-11-16 03:00:00"  c
2016-11-17  NaN                    NaN
2016-11-18  "2016-11-18 04:00:00"  d

fillna pode ser usado na série de colunas para preencher espaços em branco, se necessário.


Alguma idéia sobre o que fazer se a coluna Data contiver Blanksou NULLS? df.loc[all_days]não funcionará nesse caso.
Furqan Hashim

1
Passar curtidas da lista para .loc ou [] com qualquer etiqueta ausente aumentará o KeyError no futuro. Você pode usar .reindex () como alternativa. Veja a documentação aqui: pandas.pydata.org/pandas-docs/stable/…
Dmitrii Magas

19

Uma abordagem alternativa é resample, que pode lidar com datas duplicadas, além de datas ausentes. Por exemplo:

df.resample('D').mean()

resampleé uma operação adiada como essa, groupbyentão você precisa segui-la com outra operação. Neste caso meanfunciona bem, mas você também pode usar vários métodos de outros pandas como max, sum, etc.

Aqui estão os dados originais, mas com uma entrada extra para '2013-09-03':

             val
date           
2013-09-02     2
2013-09-03    10
2013-09-03    20    <- duplicate date added to OP's data
2013-09-06     5
2013-09-07     1

E aqui estão os resultados:

             val
date            
2013-09-02   2.0
2013-09-03  15.0    <- mean of original values for 2013-09-03
2013-09-04   NaN    <- NaN b/c date not present in orig
2013-09-05   NaN    <- NaN b/c date not present in orig
2013-09-06   5.0
2013-09-07   1.0

Deixei as datas ausentes como NaNs para esclarecer como isso funciona, mas você pode adicionar fillna(0)para substituir NaNs por zeros, conforme solicitado pelo OP ou, alternativamente, usar algo como interpolate()preencher valores diferentes de zero com base nas linhas vizinhas.


6

Aqui está um bom método para preencher datas ausentes em um quadro de dados, com sua escolha fill_value, days_backpreencher e classificar a ordem ( date_order) pela qual classificar o quadro de dados:

def fill_in_missing_dates(df, date_col_name = 'date',date_order = 'asc', fill_value = 0, days_back = 30):

    df.set_index(date_col_name,drop=True,inplace=True)
    df.index = pd.DatetimeIndex(df.index)
    d = datetime.now().date()
    d2 = d - timedelta(days = days_back)
    idx = pd.date_range(d2, d, freq = "D")
    df = df.reindex(idx,fill_value=fill_value)
    df[date_col_name] = pd.DatetimeIndex(df.index)

    return df
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.