我有一个多索引数据框,我需要按列计算百分比变化。我将 apply
与 pd.pct_change 结合使用。只要我不考虑带有 groupby
的 MultiIndex 的外部级别,这种方法就有效。
# Creat pd.MultiIndex and include some NaNs
rng = pd.date_range(start='2018-12-20', periods=20, name='date')
date = np.concatenate([rng, rng])
perm = np.array([*np.repeat(1, 20), *np.repeat(2, 20)])
d = {'perm': perm,
'date': date,
'ser_1': np.random.randint(low=1, high=10, size=[40]),
'ser_2': np.random.randint(low=1, high=10, size=[40])}
df = pd.DataFrame(data=d)
df.iloc[5:8, 2:] = np.nan
df.iloc[11:13, 2] = np.nan
df.iloc[25:28, 2:] = np.nan
df.iloc[33:37, 3] = np.nan
df.set_index(['perm', 'date'], drop=True, inplace=True)
# Apply pd.pct_change to every column individually in order to take care of the
# NaNs at different positions. Also, use groupby for every 'perm'. This one is
# where I am struggling.
# This is working properly, but it doesn't take into account 'perm'. The first
# two rows of perm=2 (i.e. rows 20 and 21) must be NaN.
chg = df.apply(lambda x, periods:
x.dropna().pct_change(periods=2).
reindex(df.index, method='ffill'),
axis=0, periods=2)
# This one is causing an error:
# TypeError: () got an unexpected keyword argument 'axis'
chg = df.groupby('perm').apply(lambda x, periods:
x.dropna().pct_change(periods=2).
reindex(df.index, method='ffill'),
axis=0, periods=2)
最佳答案
“意外的关键字参数'axis'”错误来自 pandas.DataFrame.apply和 pandas.core.groupby.GroupBy.apply是两种不同的方法,具有相似但不同的参数:它们具有相同的名称,因为它们旨在执行非常相似的任务,但它们属于两个不同的类。
如果您查看文档,您会发现第一个需要 axis
参数。第二个不是。
因此,要使用 groupby
获得有效的代码,只需从 GroupBy.apply
中删除 axis
参数即可。由于 dropna
的原因,您希望逐列工作,因此需要在 GroupBy.apply
中使用 DataFrame.apply
:
chg = df.groupby('perm').apply(lambda x:
x.apply(lambda y : y.dropna().pct_change(periods=2)
.reindex(x.index, method='ffill'),
axis=0))
这会产生您想要的结果(“perm 2”的前两行是 NaN
,其他数字等于使用 apply
而不使用 获得的结果分组依据
)。
请注意,我还编辑了 reindex
中的第一个参数:是 x.index
而不是 df.index
否则你会得到一个 double 最终结果中的 perm
索引。
最后一点,如果您在 pc_change
中将其设置为硬编码,则无需将 period
参数传递给 lambda
函数。是多余的。
关于Python:结合 groupby 计算 MultiIndex DataFrame 中每列的百分比变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58095283/