Расширьте кадр данных pandas, включив в него «недостающие» недели

У меня есть фрейм данных pandas, который содержит данные временных рядов, поэтому индекс фрейма данных имеет тип datetime64 с недельными интервалами, каждая дата приходится на понедельник каждой календарной недели.

В фрейме данных есть только записи, когда был записан заказ, поэтому, если заказ не был размещен, в фрейме данных нет соответствующей записи. Я хотел бы «дополнить» этот кадр данных, чтобы любые недели в заданном диапазоне дат были включены в кадр данных, и было введено соответствующее нулевое количество.

Мне удалось заставить это работать, создав фиктивный фрейм данных, который включает запись для каждой недели, которую я хочу, с нулевым количеством, а затем объединил эти два фрейма данных и удалил столбец фиктивного фрейма данных. Это приводит к третьему заполненному кадру данных.

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

Код, который я использовал, приведен ниже, чтобы получить мое текущее решение:

# Create the dummy product
# Week hold the week date of the order, want to set this as index later
group_by_product_name = df_all_products.groupby(['Week', 'Product Name'])['Qty'].sum()
first_date = group_by_product_name.head(1) # First date in entire dataset
last_date = group_by_product_name.tail().index[-1] # last date in the data set
bdates = pd.bdate_range(start=first_date, end=last_date, freq='W-MON')
qty = np.zeros(bdates.shape)
dummy_product = {'Week':bdates, 'DummyQty':qty}
df_dummy_product = pd.DataFrame(dummy_product)
df_dummy_product.set_index('Week', inplace=True)


group_by_product_name = df_all_products.groupby('Week')['Qty'].sum()
df_temp = pd.concat([df_dummy_product, group_by_product_name], axis=1, join='outer')
df_temp.fillna(0, inplace=True)
df_temp.drop(columns=['DummyQty'], axis=1, inplace=True)

Проблема с этим подходом иногда (я не знаю, почему) индексы не совпадают правильно, я думаю, что каким-то образом dtype индекса в одном из фреймов данных теряет свой тип и переходит к объекту вместо того, чтобы оставаться с dtype datetime64. Поэтому я уверен, что есть лучший способ решить эту проблему, чем мое текущее решение.

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

Вот пример кадра данных с «отсутствующими записями»

df1 = pd.DataFrame({'Week':['2018-05-28', '2018-06-04',
   '2018-06-11', '2018-06-25'], 'Qty':[100, 200, 300, 500]})
df1.set_index('Week', inplace=True)
df1.head()

Вот пример дополненного фрейма данных, который содержит дополнительные отсутствующие даты между диапазоном дат.

 df_zero = pd.DataFrame({'Week':['2018-05-21', '2018-05-28', '2018-06-04',
   '2018-06-11', '2018-06-18', '2018-06-25', '2018-07-02'], 'Dummy Qty':[0, 0, 0, 0, 0, 0, 0]})
df_zero.set_index('Week', inplace=True)
df_zero.head()

И это предполагаемый результат после объединения двух кадров данных

df_padded = pd.concat([df_zero, df1], axis=1, join='outer')
df_padded.fillna(0, inplace=True)
df_padded.drop(columns=['Dummy Qty'], inplace=True)
df_padded.head(6)

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

Редактировать 2:

В соответствии с запросом, вот пример того, как будет выглядеть исходный кадр данных продукта:

df_all_products = pd.DataFrame({'Week':['2018-05-21', '2018-05-28', '2018-05-21', '2018-06-11', '2018-06-18',
   '2018-06-25', '2018-07-02'], 
                            'Product Name':['A', 'A', 'B', 'A', 'B', 'A', 'A'], 
                            'Qty':[100, 200, 300, 400, 500, 600, 700]})

person Aesir    schedule 10.10.2018    source источник
comment
Я думаю, что есть лучший способ сделать это. Не могли бы вы включить образец ваших данных, пожалуйста, с ожидаемым результатом с учетом данных образца.   -  person gyx-hh    schedule 10.10.2018
comment
Конечно, теперь я добавил пример с двумя фреймами данных и окончательным результатом слияния.   -  person Aesir    schedule 10.10.2018
comment
хорошо, поэтому первая запись в df1 не обязательно является start_date. Вы действительно хотите заполнить его с 2018-05-21 по 2018-07-02? Разве не было бы достаточно добавить недостающие недели в df1 между диапазоном дат первой и последней дат в данных, представленных в df1? поэтому в этом случае он добавит только 2018-06-18   -  person gyx-hh    schedule 10.10.2018
comment
Вы можете сделать это, используя повторную выборку: df1.resample('W-MON').asfreq().fillna(0)   -  person gyx-hh    schedule 10.10.2018
comment
Вау, спасибо за это, это намного проще. Было бы идеально заполнять весь заданный диапазон дат, но, по крайней мере, для промежуточного заполнения это очень простое решение. Спасибо!   -  person Aesir    schedule 10.10.2018
comment
Нп.. возможно. Откуда вы берете весь диапазон дат? df_all_products ?   -  person gyx-hh    schedule 10.10.2018
comment
Да. поэтому цель состоит в том, чтобы все временные ряды для продуктов были одинаковой длины. Поэтому я просто беру в нем первую дату и последнюю, чтобы определить этот временной диапазон.   -  person Aesir    schedule 10.10.2018
comment
Я понимаю. Я думаю, что это можно сделать лучше, тбх. Не могли бы вы предоставить образец df_all_products, пожалуйста?   -  person gyx-hh    schedule 10.10.2018
comment
Я добавил пример к исходному вопросу. Таким образом, конечным результатом должны быть два отдельных кадра данных для каждого продукта с их собственными записями количества, но с одинаковыми записями даты.   -  person Aesir    schedule 10.10.2018


Ответы (1)


Хорошо, учитывая ваши исходные данные, вы можете достичь ожидаемых результатов, используя pivot и повторную выборку для любых отсутствующих недель, например следующее:

results = df_all_products.groupby(
    ['Week','Product Name']
)['Qty'].sum().reset_index().pivot(
    index='Week',columns='Product Name', values='Qty'
).resample('W-MON').asfreq().fillna(0)

Выходные результаты:

Product Name    A   B
Week        
2018-05-21  100.0   300.0
2018-05-28  200.0   0.0
2018-06-04  0.0     0.0
2018-06-11  400.0   0.0
2018-06-18  0.0     500.0
2018-06-25  600.0   0.0
2018-07-02  700.0   0.0

Итак, если вы хотите получить df для названия продукта A, вы можете сделать results['A'].

person gyx-hh    schedule 10.10.2018