Заполните все месяцы для мультииндекса данных в пандах

У меня есть таблица с продажами и прогнозом в месяц для тысяч продуктов на период с 2015 по 2017 год. Мои данные содержат данные о спросе и прогнозе для каждого сайта, типа, продукта и даты (только месяцы).

Проблема в том, что если в течение месяца не будет продаж и прогноза, я не вижу конкретной строки. В приведенном ниже примере вы видите, что строка для "2015-08-31" отсутствует. Я хотел бы видеть для этой линии спрос 0 и прогноз 0. (См. Ниже пример с df_expected).

В основном, я хотел бы заполнить эту таблицу 0 для всех дат в период с 2015-06-30 по 2017-09-30 для всех комбинаций Продукт / Тип / Сайт.

Как вы можете видеть из кода, я не определил ни одного индекса, но в основном ["Site","Type","Product", "Date"] можно рассматривать как multiIndex.

Обратите внимание, что у меня есть миллионы строк.

    import pandas as pd
data = [("W1","G1",1234,pd.to_datetime("2015-07-31"),8,4),
        ("W1","G1",1234,pd.to_datetime("2015-09-30"),2,4),
        ("W1","G1",1234,pd.to_datetime("2015-10-31"),2,4),
        ("W1","G1",1234,pd.to_datetime("2015-11-30"),4,4),
        ("W1","G2",2345,pd.to_datetime("2015-07-31"),5,0),
        ("W1","G2",2345,pd.to_datetime("2015-08-31"),1,3),
        ("W1","G2",2345,pd.to_datetime("2015-10-31"),1,3),
        ("W1","G2",2345,pd.to_datetime("2015-11-30"),3,3)]
labels = ["Site","Type","Product","Date","Demand","Forecast"]
df = pd.DataFrame(data,columns=labels)
df

   Site Type  Product       Date  Demand  Forecast
0   W1   G1     1234 2015-07-31       8         4
1   W1   G1     1234 2015-09-30       2         4
2   W1   G1     1234 2015-10-31       2         4
3   W1   G1     1234 2015-11-30       4         4
4   W1   G2     2345 2015-07-31       5         0
5   W1   G2     2345 2015-08-31       1         3
6   W1   G2     2345 2015-10-31       1         3
7   W1   G2     2345 2015-11-30       3         3

Это результат, который я ожидаю

data_expected = [("W1","G1",1234,pd.to_datetime("2015-07-31"),8,4),
                 ("W1","G1",1234,pd.to_datetime("2015-08-31"),0,0),
                 ("W1","G1",1234,pd.to_datetime("2015-09-30"),2,4),        
                 ("W1","G1",1234,pd.to_datetime("2015-10-31"),2,4),
                 ("W1","G1",1234,pd.to_datetime("2015-11-30"),4,4)]
df_expected = pd.DataFrame(data_expected,columns=labels)
df_expected

  Site Type  Product        Date  Demand  Forecast
0   W1   G1     1234  2015-07-31       8         4
1   W1   G1     1234  2015-08-31       0         0
2   W1   G1     1234  2015-09-30       2         4
3   W1   G1     1234  2015-10-31       2         4
4   W1   G1     1234  2015-11-30       4         4

Первоначально я думал о стеке /unstack, чтобы убедиться, что у меня есть все месяцы. Но это не оптимально для фрейма данных с миллионами строк.

df = (df
      .set_index("Date")
      .groupby(["Site","Product","Type",pd.TimeGrouper('M')])[["Forecast","Demand"]].sum()
      .unstack()
      .fillna(0)
      .astype(int))

Как вы думаете?

2 ответа

Решение

Ты можешь использовать DataFrameGroupBy.resampleс asfreq:

df = (df.set_index('Date')
       .groupby(["Site","Type","Product"])['Demand','Forecast']
       .resample('M')
       .asfreq()
       .fillna(0)
       .astype(int)
       .reset_index())
print (df)
  Site Type  Product       Date  Demand  Forecast
0   W1   G1     1234 2015-07-31       8         4
1   W1   G1     1234 2015-08-31       0         0
2   W1   G1     1234 2015-09-30       2         4
3   W1   G1     1234 2015-10-31       2         4
4   W1   G1     1234 2015-11-30       4         4

РЕДАКТИРОВАТЬ:

Я пытаюсь немного улучшить оригинальное решение с fill_value параметр в unstack:

(df.set_index("Date") 
   .groupby(["Site","Product","Type",pd.TimeGrouper('M')])['Dem‌​and','Forecast'].sum‌​() 
   .unstack(fill_value=0) 
   .stack())

Подход со стеком / без стека, кажется, работает намного быстрее. при этом все элементы имеют одинаковую дату начала и дату окончания

df = (df.set_index("Date")
        .groupby(["Site","Product","Type",pd.TimeGrouper('M')])['Demand','Forecast'].sum()
        .unstack()
        .fillna(0)
        .astype(int)
        .stack())


                              Demand  Forecast
Site Product Type Date                        
W1   1234    G1   2015-07-31       8         4
                  2015-08-31       0         0
                  2015-09-30       2         4
                  2015-10-31       2         4
                  2015-11-30       4         4
     2345    G2   2015-07-31       5         0
                  2015-08-31       1         3
                  2015-09-30       0         0
                  2015-10-31       1         3
                  2015-11-30       3         3
Другие вопросы по тегам