multiindex multi index python pandas numpy dataframe multi-index

python - Rellenando huecos de fecha en el marco de datos de MultiIndex Pandas



pandas xs (2)

Me gustaría modificar un marco de datos MultiIndex de pandas para que cada grupo de índices incluya fechas entre un rango específico. Me gustaría que cada grupo complete las fechas que faltan del 2013-06-11 al 2013-12-31 con el valor 0 (o NaN ).

Group A, Group B, Date, Value loc_a group_a 2013-06-11 22 2013-07-02 35 2013-07-09 14 2013-07-30 9 2013-08-06 4 2013-09-03 40 2013-10-01 18 group_b 2013-07-09 4 2013-08-06 2 2013-09-03 5 group_c 2013-07-09 1 2013-09-03 2 loc_b group_a 2013-10-01 3

He visto algunas discusiones sobre reindex , pero eso es para datos de series de tiempo simples (no agrupados).

¿Hay una forma fácil de hacer esto?

A continuación hay algunos intentos que he hecho para lograr esto. Por ejemplo: una vez que haya desapilado por [''A'', ''B''] , puedo reindexar.

df = pd.DataFrame({''A'': [''loc_a''] * 12 + [''loc_b''], ''B'': [''group_a''] * 7 + [''group_b''] * 3 + [''group_c''] * 2 + [''group_a''], ''Date'': ["2013-06-11", "2013-07-02", "2013-07-09", "2013-07-30", "2013-08-06", "2013-09-03", "2013-10-01", "2013-07-09", "2013-08-06", "2013-09-03", "2013-07-09", "2013-09-03", "2013-10-01"], ''Value'': [22, 35, 14, 9, 4, 40, 18, 4, 2, 5, 1, 2, 3]}) df.Date = df[''Date''].apply(lambda x: pd.to_datetime(x).date()) df = df.set_index([''A'', ''B'', ''Date'']) dt_start = dt.datetime(2013,6,1) all_dates = [(dt_start + dt.timedelta(days=x)).date() for x in range(0,60)] df2 = df.unstack([''A'', ''B'']) df3 = df2.reindex(index=all_dates).fillna(0) df4 = df3.stack([''A'', ''B'']) ## df4 is about where I want to get, now I''m trying to get it back in the form of df... df5 = df4.reset_index() df6 = df5.rename(columns={''level_0'' : ''Date''}) df7 = df6.groupby([''A'', ''B'', ''Date''])[''Value''].sum()

Las últimas líneas me ponen un poco triste. Esperaba que en df6 pudiera simplemente volver a establecer el set_index en [''A'', ''B'', ''Date''] , pero eso no agrupaba los valores, ya que están agrupados en el DataFrame inicial de df .

¿Alguna idea sobre cómo puedo reindexar el DataFrame no apilado, volver a empacar y tener el DataFrame en el mismo formato que el original?


Puede crear un nuevo índice múltiple basado en el producto cartesiano de los niveles del índice múltiple existente. Luego, vuelva a indexar su marco de datos utilizando el nuevo índice.

new_index = pd.MultiIndex.from_product(df.index.levels) new_df = df.reindex(new_index) # Optional: convert missing values to zero, and convert the data back # to integers. See explanation below. new_df = new_df.fillna(0).astype(int)

¡Eso es! El nuevo marco de datos tiene todos los valores de índice posibles. Los datos existentes están indexados correctamente.

Siga leyendo para una explicación más detallada.

Explicación

Configurar datos de muestra

import pandas as pd df = pd.DataFrame({''A'': [''loc_a''] * 12 + [''loc_b''], ''B'': [''group_a''] * 7 + [''group_b''] * 3 + [''group_c''] * 2 + [''group_a''], ''Date'': ["2013-06-11", "2013-07-02", "2013-07-09", "2013-07-30", "2013-08-06", "2013-09-03", "2013-10-01", "2013-07-09", "2013-08-06", "2013-09-03", "2013-07-09", "2013-09-03", "2013-10-01"], ''Value'': [22, 35, 14, 9, 4, 40, 18, 4, 2, 5, 1, 2, 3]}) df.Date = pd.to_datetime(df.Date) df = df.set_index([''A'', ''B'', ''Date''])

Así es como se ven los datos de muestra

Value A B Date loc_a group_a 2013-06-11 22 2013-07-02 35 2013-07-09 14 2013-07-30 9 2013-08-06 4 2013-09-03 40 2013-10-01 18 group_b 2013-07-09 4 2013-08-06 2 2013-09-03 5 group_c 2013-07-09 1 2013-09-03 2 loc_b group_a 2013-10-01 3

Hacer nuevo índice

Usando from_product podemos hacer un nuevo índice múltiple. Este nuevo índice es el producto cartesiano de todos los valores de todos los niveles del índice anterior.

new_index = pd.MultiIndex.from_product(df.index.levels)

Reindexar

Utilice el nuevo índice para reindexar el marco de datos existente.

new_df = df.reindex(new_index)

Todas las combinaciones posibles están ahora presentes. Los valores que faltan son nulos (NaN).

El marco de datos expandido y re-indexado se ve así:

Value loc_a group_a 2013-06-11 22.0 2013-07-02 35.0 2013-07-09 14.0 2013-07-30 9.0 2013-08-06 4.0 2013-09-03 40.0 2013-10-01 18.0 group_b 2013-06-11 NaN 2013-07-02 NaN 2013-07-09 4.0 2013-07-30 NaN 2013-08-06 2.0 2013-09-03 5.0 2013-10-01 NaN group_c 2013-06-11 NaN 2013-07-02 NaN 2013-07-09 1.0 2013-07-30 NaN 2013-08-06 NaN 2013-09-03 2.0 2013-10-01 NaN loc_b group_a 2013-06-11 NaN 2013-07-02 NaN 2013-07-09 NaN 2013-07-30 NaN 2013-08-06 NaN 2013-09-03 NaN 2013-10-01 3.0 group_b 2013-06-11 NaN 2013-07-02 NaN 2013-07-09 NaN 2013-07-30 NaN 2013-08-06 NaN 2013-09-03 NaN 2013-10-01 NaN group_c 2013-06-11 NaN 2013-07-02 NaN 2013-07-09 NaN 2013-07-30 NaN 2013-08-06 NaN 2013-09-03 NaN 2013-10-01 NaN

Nulos en columna entera

Puede ver que los datos en el nuevo marco de datos se han convertido de ints a flotantes. Las pandas no pueden tener nulos en una columna entera . Opcionalmente, podemos convertir todos los nulos a 0 y convertir los datos a números enteros.

new_df = new_df.fillna(0).astype(int)

Resultado

Value loc_a group_a 2013-06-11 22 2013-07-02 35 2013-07-09 14 2013-07-30 9 2013-08-06 4 2013-09-03 40 2013-10-01 18 group_b 2013-06-11 0 2013-07-02 0 2013-07-09 4 2013-07-30 0 2013-08-06 2 2013-09-03 5 2013-10-01 0 group_c 2013-06-11 0 2013-07-02 0 2013-07-09 1 2013-07-30 0 2013-08-06 0 2013-09-03 2 2013-10-01 0 loc_b group_a 2013-06-11 0 2013-07-02 0 2013-07-09 0 2013-07-30 0 2013-08-06 0 2013-09-03 0 2013-10-01 3 group_b 2013-06-11 0 2013-07-02 0 2013-07-09 0 2013-07-30 0 2013-08-06 0 2013-09-03 0 2013-10-01 0 group_c 2013-06-11 0 2013-07-02 0 2013-07-09 0 2013-07-30 0 2013-08-06 0 2013-09-03 0 2013-10-01 0


Su pregunta no fue clara exactamente en qué fechas faltaba; Solo asumo que desea completar NaN para cualquier fecha para la cual tenga una observación en otro lugar. Mi solución tendrá que ser enmendada si esta suposición es defectuosa.

Nota al DataFrame : puede ser bueno incluir una línea para crear el DataFrame

In [55]: df = pd.DataFrame({''A'': [''loc_a''] * 12 + [''loc_b''], ....: ''B'': [''group_a''] * 7 + [''group_b''] * 3 + [''group_c''] * 2 + [''group_a''], ....: ''Date'': ["2013-06-11", ....: "2013-07-02", ....: "2013-07-09", ....: "2013-07-30", ....: "2013-08-06", ....: "2013-09-03", ....: "2013-10-01", ....: "2013-07-09", ....: "2013-08-06", ....: "2013-09-03", ....: "2013-07-09", ....: "2013-09-03", ....: "2013-10-01"], ....: ''Value'': [22, 35, 14, 9, 4, 40, 18, 4, 2, 5, 1, 2, 3]}) In [56]: In [56]: df.Date = pd.to_datetime(df.Date) In [57]: df = df.set_index([''A'', ''B'', ''Date'']) In [58]: In [58]: print(df) Value A B Date loc_a group_a 2013-06-11 22 2013-07-02 35 2013-07-09 14 2013-07-30 9 2013-08-06 4 2013-09-03 40 2013-10-01 18 group_b 2013-07-09 4 2013-08-06 2 2013-09-03 5 group_c 2013-07-09 1 2013-09-03 2 loc_b group_a 2013-10-01 3

Para completar los valores no observados, usaremos los métodos de unstack y stack . Desapilar creará los NaN que nos interesan, y luego los apilaremos para trabajar.

In [71]: df.unstack([''A'', ''B'']) Out[71]: Value A loc_a loc_b B group_a group_b group_c group_a Date 2013-06-11 22 NaN NaN NaN 2013-07-02 35 NaN NaN NaN 2013-07-09 14 4 1 NaN 2013-07-30 9 NaN NaN NaN 2013-08-06 4 2 NaN NaN 2013-09-03 40 5 2 NaN 2013-10-01 18 NaN NaN 3 In [59]: df.unstack([''A'', ''B'']).fillna(0).stack([''A'', ''B'']) Out[59]: Value Date A B 2013-06-11 loc_a group_a 22 group_b 0 group_c 0 loc_b group_a 0 2013-07-02 loc_a group_a 35 group_b 0 group_c 0 loc_b group_a 0 2013-07-09 loc_a group_a 14 group_b 4 group_c 1 loc_b group_a 0 2013-07-30 loc_a group_a 9 group_b 0 group_c 0 loc_b group_a 0 2013-08-06 loc_a group_a 4 group_b 2 group_c 0 loc_b group_a 0 2013-09-03 loc_a group_a 40 group_b 5 group_c 2 loc_b group_a 0 2013-10-01 loc_a group_a 18 group_b 0 group_c 0 loc_b group_a 3

Reordenar los niveles de índice según sea necesario.

Tuve que deslizar esa fillna(0) en el medio para que los NaN no se cayeran. stack tiene un argumento dropna . Pensaría que establecer ese valor en falso mantendría todas las filas de NaN alrededor. Un error tal vez?