Pandas多重索引在子索引上的操作
我有一个叫做 indicator_df
的指示数据框,这个数据框用1和0来表示数据是否包含,1表示包含,0表示不包含。它的索引是时间序列,列是多重索引,结构大概是这样的:
Item0 Item1
A D A C
2014-04-02 0 1 0 1
2014-04-03 0 1 0 1
2014-04-04 1 1 0 1
另外,我还有一个叫做 data_df
的时间序列数据框,它的索引和 indicator_df
一样,并且有匹配的子列:
A B C D
2014-04-02 3 4 2 -3
2014-04-03 1 3 -2 1
2014-04-04 -1 -5 0 -2
我想要的是一个紧凑的方式,得到一个时间序列数据框,列名为 ['Item0', 'Item1']
,每一列都是根据指示数据框中包含的数据求和的结果。
new_df[col] = indicator_df[col].mul(data_df).sum(axis=1)
Item0 Item1
2014-04-02 -3 2
2014-04-03 1 -2
2014-04-04 -3 0
我可以通过循环遍历多重索引的第一层,然后把每一列拼接起来,但我觉得应该可以不使用循环来实现这个功能。也许可以用聪明的分组方法来完成?
2 个回答
0
这样对你来说够用吗?因为这些索引是一样的。
pd.DataFrame(np.array([(DF1[item]*DF2[DF[item].columns]).sum(axis=1) for item in ['Item0', 'Item1']]).T,
columns=['Item0', 'Item1'], index=DF1.index)
1
这里有一个不那么简洁的版本,但更符合pandas的用法:
首先,用 pandas.melt
把你的数据整理一下。处理两个数据框(DataFrame)会简单很多,每个数据框只是一些有共同列的列集合,而不是去搞复杂的多重索引。
In [127]: dfm = pd.melt(df, var_name=['items', 'labels'], id_vars=['index'], value_name='indicator')
In [128]: dfm
Out[128]:
index items labels indicator
0 2014-04-02 Item0 A 0
1 2014-04-03 Item0 A 0
2 2014-04-04 Item0 A 1
3 2014-04-02 Item0 D 1
4 2014-04-03 Item0 D 1
5 2014-04-04 Item0 D 1
6 2014-04-02 Item1 A 0
7 2014-04-03 Item1 A 0
8 2014-04-04 Item1 A 0
9 2014-04-02 Item1 C 1
10 2014-04-03 Item1 C 1
11 2014-04-04 Item1 C 1
[12 rows x 4 columns]
In [129]: df2m = pd.melt(df2, var_name=['labels'], id_vars=['index'], value_name='value')
In [130]: df2m
Out[130]:
index labels value
0 2014-04-02 A 3
1 2014-04-03 A 1
2 2014-04-04 A -1
3 2014-04-02 B 4
4 2014-04-03 B 3
5 2014-04-04 B -5
6 2014-04-02 C 2
7 2014-04-03 C -2
8 2014-04-04 C 0
9 2014-04-02 D -3
10 2014-04-03 D 1
11 2014-04-04 D -2
[12 rows x 3 columns]
现在你有了两个数据框,它们有一些共同的列(“标签”和“索引”),你可以用这些列来进行 pandas.merge
操作:
In [140]: merged = pd.merge(dfm, df2m, on=['labels', 'index'], how='outer')
In [141]: merged
Out[141]:
index items labels indicator value
0 2014-04-02 Item0 A 0 3
1 2014-04-02 Item1 A 0 3
2 2014-04-03 Item0 A 0 1
3 2014-04-03 Item1 A 0 1
4 2014-04-04 Item0 A 1 -1
5 2014-04-04 Item1 A 0 -1
6 2014-04-02 Item0 D 1 -3
7 2014-04-03 Item0 D 1 1
8 2014-04-04 Item0 D 1 -2
9 2014-04-02 Item1 C 1 2
10 2014-04-03 Item1 C 1 -2
11 2014-04-04 Item1 C 1 0
12 2014-04-02 NaN B NaN 4
13 2014-04-03 NaN B NaN 3
14 2014-04-04 NaN B NaN -5
[15 rows x 5 columns]
因为 indicator
实际上只是一个布尔索引,所以把它的 NaN
值去掉,并把它转换成布尔类型(bool dtype)。
In [147]: merged.dropna(subset=['indicator'], inplace=True)
In [148]: merged['indicator'] = merged.indicator.copy().astype(bool)
In [149]: merged
Out[149]:
index items labels indicator value
0 2014-04-02 Item0 A False 3
1 2014-04-02 Item1 A False 3
2 2014-04-03 Item0 A False 1
3 2014-04-03 Item1 A False 1
4 2014-04-04 Item0 A True -1
5 2014-04-04 Item1 A False -1
6 2014-04-02 Item0 D True -3
7 2014-04-03 Item0 D True 1
8 2014-04-04 Item0 D True -2
9 2014-04-02 Item1 C True 2
10 2014-04-03 Item1 C True -2
11 2014-04-04 Item1 C True 0
[12 rows x 5 columns]
现在用 indicator
来切片,并使用 pivot_table
来得到你想要的结果:
In [150]: merged.loc[merged.indicator].pivot_table(values='value', index='index', columns=['items'], aggfunc=sum)
Out[150]:
items Item0 Item1
index
2014-04-02 -3 2
2014-04-03 1 -2
2014-04-04 -3 0
[3 rows x 2 columns]
这看起来可能有点多,但可能是因为我把每一步都写出来了。实际上大约只需要五行代码。