Заполните все месяцы для мультииндекса данных в пандах
У меня есть таблица с продажами и прогнозом в месяц для тысяч продуктов на период с 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')])['Demand','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