在Multiindex DataFrame中添加和重命名列

时间:2016-07-20 22:35:24

标签: python pandas dataframe

本文的目的是了解如何使用MultiIndex.DataFrameapply()

将列添加到shift()中的某个级别

创建DataFrame

import pandas as pd

df = pd.DataFrame(
[
    [5777, 100, 5385, 200, 5419, 4887, 100, 200],
    [4849, 0, 4539, 0, 3381, 0, 0, ],
    [4971, 0, 3824, 0, 4645, 3424, 0, 0, ],
    [4827, 200, 3459, 300, 4552, 3153, 100, 200, ],
    [5207, 0, 3670, 0, 4876, 3358, 0, 0, ],
],
index=pd.to_datetime(['2010-01-01',
                      '2010-01-02',
                      '2010-01-03',
                      '2010-01-04',
                      '2010-01-05']),
columns=pd.MultiIndex.from_tuples(
    [('Portfolio A', 'GBP', 'amount'), ('Portfolio A', 'GBP', 'injection'),
     ('Portfolio B', 'EUR', 'amount'), ('Portfolio B', 'EUR', 'injection'),
     ('Portfolio A', 'USD', 'amount'), ('Portfolio A', 'USD', 'injection'),
     ('Portfolio B', 'JPY', 'amount'), ('Portfolio B', 'JPY', 'injection')])
).sortlevel(axis=1)

print df

我想使用以下方法为名为daily_added_value的第2级的每种货币添加新列:

def do_nothing(group):
   return group

def calc_daily_added_value(group):
    g = (group['amount'] - group['amount'].shift(periods=1, freq=None, axis=0)
          -df['injection'].shift(periods=1, freq=None, axis=0)).round(decimals=2)
    g.index = ['daily_added_value']
    return g

pd.concat([df.T.groupby(level=0).apply(f).T for f in [calc_daily_added_value,do_nothing ]], axis=1).sort_index(axis=1)

然而,这会引发一个关键错误:KeyError: 'amount'

方法calc_daily_added_value()的正确语法是什么?

继续下面的答案后仍有问题

添加每日返回作品

dav = df.loc[:, pd.IndexSlice[:, :, 'daily_added_value']]
amount = df.loc[:, pd.IndexSlice[:, :, 'amount']]
dr = (dav.values / amount.shift()) * 100
dr.columns.set_levels(['daily_return'], level=2, inplace=True)
df = pd.concat([df, dr], axis=1).sortlevel(axis=1)

添加累积复合返回FAILS

dr = df.loc[:, pd.IndexSlice[:, :, 'daily_return']]
drc = 100*((1+dr / 100).cumprod()-1)
drc.columns.set_levels(['daily_return_cumulative'], level=2, inplace=True)
df = pd.concat([df, drc], axis=1).sort_index(axis=1)
df.head()

这会失败,因为它缺少.values,但是如果我添加它就会变成一个数组?

虽然drc实际上是一个形状正确的DataFrame,但似乎包含了正确的结果。

这行失败了:

drc.columns.set_levels(['daily_return_cumulative'], level=2, inplace=True)

错误为ValueError: On level 2, label max (2) >= length of level (1). NOTE: this index is in an inconsistent state

如何将索引置于一致状态?

1 个答案:

答案 0 :(得分:2)

跳过没有必要的groupby

amount = df.loc[:, pd.IndexSlice[:, :, 'amount']]
inject = df.loc[:, pd.IndexSlice[:, :, 'injection']]
dav = amount - amount.shift() - inject.shift().values
#dav.columns.set_levels(['daily_added_value'], level=2, inplace=True)

pd.concat([df, dav], axis=1).sort_index(axis=1).T

注意:我使用T来获得容易适合的图片

enter image description here

set_levels似乎有bug,因此不建议使用它。

在DataFrame dav中重命名MultiIndex列的解决方法

def map_level(df, dct, level=2):
    index = df.index
    index.set_levels([[dct.get(item, item) for item in names] if i==level else    names
                       for i, names in enumerate(index.levels)], inplace=True)
dct = {'amount':'daily_added_value'}
map_level(dav.T, dct, level=2)