У меня есть данные о транзакциях за годы, с которыми я работаю по идентификаторам клиентов. Информация о транзакциях находится на уровне счета-фактуры, и идентификатор может легко иметь несколько счетов-фактур в один и тот же день или не иметь счетов в течение многих лет. Я пытаюсь создать кадры данных, которые содержат суммы счетов-фактур по каждому году, но также показывают годы, когда счета-фактуры не были добавлены. Что-то похожее на:
tmp = invoices[invoice['invoice_year'].isin([2018,2019,2020]]
tmp = tmp.groupby(['id', pd.Grouper(key = 'invoice_date', freq = 'Y')])['sales'].sum()
Это вернет что-то вроде:
id invoice_year sales
1 2018 483982.20
1 2019 3453
1 2020 453533
2 2018 243
2 2020 23423
3 2020 2330202
Однако желаемый результат будет:
id invoice_year sales
1 2018 483982.20
1 2019 3453
1 2020 453533
2 2018 243
2 2019 nan
2 2020 23423
3 2018 nan
3 2019 nan
3 2020 2330202
Идеи?
Я так не думаю. Нет. В приведенном выше вопросе мы генерируем np.nans
из-за отсутствия данных, а не заменяем nans или управляем существующими nans.
Вы можете попробовать вариант dropna = False
, если у вас есть последние версии панд.
Предположим, что исходные значения определены в фрейме данных с именем df
, тогда вы можете попробовать следующее:
output = (df.groupby(['id', 'invoice_date'])['val'].sum()
.unstack(fill_value=0)
.stack()
.reset_index(name='val'))
В противном случае вы можете предварительно создать столбец invoice_year
:
df['invoice_year'] = df['invoice_date'].dt.year
И повторите тот же код, это выводит:
id invoice_year val
0 1 2018 1
1 1 2019 1
2 1 2020 0
3 2 2018 1
4 2 2019 0
5 2 2020 1
6 3 2018 0
7 3 2019 1
8 3 2020 1
Используя следующие данные в качестве примера:
df = pd.DataFrame({'id':[1]*2+[2]*2+[3]*2,'invoice_date':pd.to_datetime(['2018-12-01','2019-12-01','2020-12-01']*2,infer_datetime_format=True),'val':[1]*6})
Вам не хватает скобки перед df
:) Я добавлю это, чтобы ваш код работал с вашим фреймом данных.
Большое спасибо Давид! Должно быть, я что-то пропустил, когда копировал из colab!
Стефан опубликовал комментарий, который может помочь. Просто передать dropna=False
вашему .groupby
кажется лучшим выбором; но вы также можете использовать подход, при котором вы возвращаете NaN
s позже, что может потребоваться в более ранних версиях панд, которые не имеют параметра dropna=False
:
id invoice_year sales
1 2018 483982.20
1 2019 3453
1 2020 453533
2 2018 243
2 2020 23423
3 2020 2330202
Вы можете использовать pd.MultiIndex.from_product
и переиндексировать фрейм данных из вновь созданного индекса с именем idx
:
i, iy = df['id'], df['invoice_year']
idx = pd.MultiIndex.from_product([range(i.min(), i.max()+1),
range(iy.min(), iy.max()+1)],
names=[i.name, iy.name])
df = df.set_index([i.name, iy.name]).reindex(idx).reset_index()
df
Out[1]:
id invoice_year sales
0 1 2018 483982.2
1 1 2019 3453.0
2 1 2020 453533.0
3 2 2018 243.0
4 2 2019 NaN
5 2 2020 23423.0
6 3 2018 NaN
7 3 2019 NaN
8 3 2020 2330202.0
Отвечает ли это на ваш вопрос? столбцы pandas GroupBy со значениями NaN (отсутствуют)