Pandas多重索引在子索引上的操作

2 投票
2 回答
1316 浏览
提问于 2025-04-18 01:47

我有一个叫做 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]

这看起来可能有点多,但可能是因为我把每一步都写出来了。实际上大约只需要五行代码。

撰写回答