Añadir fechas faltantes a pandas dataframe


Mis datos pueden tener varios eventos en una fecha determinada o NINGÚN evento en una fecha. Tomo estos eventos, obtengo un recuento por fecha y los trazo. Sin embargo, cuando los dibujo, mis dos series no siempre coinciden.

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

En el código anterior idx se convierte en un rango de, por ejemplo, 30 fechas. 09-01-2013 a 09-30-2013 Sin embargo S solo puede tener 25 o 26 días porque no ocurrieron eventos para una fecha determinada. Luego obtengo un AssertionError ya que los tamaños no coinciden cuando intento parcela:

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

¿Cuál es la manera correcta de abordar esto? Quiero eliminar fechas sin valores de IDX o (lo que prefiero hacer) es agregar a la serie la fecha que falta con un conteo de 0. Prefiero tener un gráfico completo de 30 días con 0 valores. Si este enfoque es correcto, ¿alguna sugerencia sobre cómo comenzar? ¿Necesito algún tipo de función dinámica reindex?

Aquí hay un fragmento de S ( df.groupby(['simpleDate']).size() ), noten que no hay entradas para 04 y 05.

09-02-2013     2
09-03-2013    10
09-06-2013     5
09-07-2013     1
Author: KHibma, 2013-10-11

5 answers

Podrías 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)

Rinde

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
...
 142
Author: unutbu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 18:36:56

Un problema es que reindex fallará si hay valores duplicados. Digamos que estamos trabajando con datos con marca de tiempo, que queremos indexar por fecha:

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

Rinde

            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

Debido a la fecha duplicada 2016-11-16, un intento de reindexar:

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

Falla con:

...
ValueError: cannot reindex from a duplicate axis

(con esto significa que el índice tiene duplicados, no que sea en sí mismo un dup)

En su lugar, podemos usar .loc para buscar entradas para todas las fechas en rango:

df.loc[all_days]

Rinde

            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 se puede usar en la serie de columnas para llenar espacios en blanco si es necesario.

 18
Author: Nick Edgar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-16 23:36:31

Una solución más rápida es utilizar .asfreq(). Esto no requiere la creación de un nuevo índice para llamar dentro de .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
 11
Author: Brad Solomon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-28 19:05:46

En muchos casos, resample (vea la documentación aquí) ofrece una solución general que puede manejar tanto las fechas faltantes como las duplicadas. Por ejemplo:

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

resample es una operación diferida como groupby por lo que necesita para seguir con otra operación. En este caso mean funciona bien, pero también puede usar muchos métodos estándar de pandas como max, sum, etc.

Aquí están los datos originales, pero con una entrada adicional para '2013-09-03':

            val
date           
2013-09-02    2
2013-09-03   10
2013-09-03   20
2013-09-06    5
2013-09-07    1

Y aquí están los 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

Tenga en cuenta que después de esto puede usar métodos como fillna o interpolate para llenar los valores faltantes como desee.

 3
Author: JohnE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-09 18:30:46

Aquí hay un buen método para completar las fechas faltantes en un dataframe, con su elección de fill_value, days_back para rellenar y ordenar el orden (date_order) por el cual ordenar el dataframe:

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
 2
Author: eiTan LaVi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-28 02:07:52