我有多年的交易数据,我正在通过客户ID处理这些数据。交易信息处于发票级别,id很容易在同一天有多个发票,或者多年没有发票。我试图创建数据帧,其中包含按客户每年列出的发票总额,但也显示未添加发票的年份。类似于:
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
想法?
假设原始值是在名为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})
Stefan发布了一条可能会有所帮助的评论。简单地将dropna=False
传递给您的.groupby
似乎是最好的选择;但是,您也可以采取这样的方法,即在之后将NaN
恢复,这可能是没有dropna=False
参数的早期版本的panda所需要的:
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