将自定义累积函数应用于pandas数据框

9

我有一个按照日期排序的数据框:

df = pd.DataFrame({'idx': [1, 1, 1, 2, 2, 2],
                   'date': ['2016-04-30', '2016-05-31', '2016-06-31',
                            '2016-04-30', '2016-05-31', '2016-06-31'],
                   'val': [10, 0, 5, 10, 0, 0],
                   'pct_val': [None, -10, None, None, -10, -10]})
df = df.sort('date')
print df

         date  idx  pct_val  val
3  2016-04-30    2      NaN   10
0  2016-04-30    1      NaN   10
4  2016-05-31    2      -10    0
1  2016-05-31    1      -10    0
5  2016-06-31    2      -10    0
2  2016-06-31    1      NaN    5

我希望你能按照 idx 进行分组,然后应用一个累积函数,并进行一些简单的逻辑处理。如果 pct_val 为 null,则将 val 添加到累加总和中;否则,将累加总和乘以 1 + pct_val/100'cumsum' 显示了 df.groupby('idx').val.cumsum() 的结果,'cumulative_func' 是我想要的结果。

         date  idx  pct_val  val  cumsum  cumulative_func
3  2016-04-30    2      NaN   10      10               10
0  2016-04-30    1      NaN   10      10               10
4  2016-05-31    2      -10    0      10                9
1  2016-05-31    1      -10    0      10                9
5  2016-06-31    2      -10    0      10                8
2  2016-06-31    1      NaN    5      15               14

您好!请问有没有办法对数据框应用自定义的累计函数或更好的方法来实现这一点?


你尝试过使用pct_change()函数或者shift()吗?很可能你需要一个可以传入数据框并执行逻辑并返回结果的方法。你可以使用apply()对单个列进行计算。 - ctrl-alt-delete
你能否更新上面的代码,以便我们可以轻松地创建df?此外,你的pct_val是“-10”,但看起来你真正想要的是乘以pct_val/100.0。 - flyingmeatball
我看到你已经编辑了,但是重要的是你需要除以100.0而不是100。除以100会进行整数运算(-10/100),这将得到0(一个整数,而不是浮点数)。 - flyingmeatball
@flyingmeatball 很好的观点,但那部分只是伪代码,仅用于解释逻辑。 - user2899059
1
关于如何将自定义累积函数应用于数据框,请参见https://dev59.com/GF4b5IYBdhLWcg3wfx5J - luca
好的,知道了,谢谢更新@luca - user2899059
2个回答

5

我认为使用向量化并没有简单的方法来实现你的目标。如果需要,我建议先尝试让它工作起来,然后再进行速度优化。

def cumulative_func(df):
    results = []
    for group in df.groupby('idx').groups.itervalues():
        total = 0
        result = []
        for p, v in df.ix[group, ['pct_val', 'val']].values:
            if np.isnan(p):
                total += v
            else:
                total *= (1 + .01 * p)
            result.append(total)
        results.append(pd.Series(result, index=group))
    return pd.concat(results).reindex(df.index)

df['cumulative_func'] = cumulative_func(df)

>>> df
         date  idx  pct_val  val  cumulative_func
3  2016-04-30    2      NaN   10             10.0
0  2016-04-30    1      NaN   10             10.0
4  2016-05-31    2      -10    0              9.0
1  2016-05-31    1      -10    0              9.0
5  2016-06-31    2      -10    0              8.1
2  2016-06-31    1      NaN    5             14.0

抢先一步了 - 我同意没有一个好的向量化方法来做这件事。如果你想稍微提高速度,可以将每个组的数据发送到一个numpy数组中 - itervalues()总是非常非常慢的。 - flyingmeatball
itervalues()是在字典上的,所以我认为它不会很慢。groups = {1: [0, 1, 2], 2: [3, 4, 5]} - Alexander
没错 - 我快速地阅读了你的脚本,你已经通过在字典上使用itervalues()来避免使用.iterrows()结构,因此速度加1! - flyingmeatball

1

首先,我清理了您的设置

设置

df = pd.DataFrame({'idx': [1, 1, 1, 2, 2, 2],
                   'date': ['2016-04-30', '2016-05-31', '2016-06-31',
                            '2016-04-30', '2016-05-31', '2016-06-31'],
                   'val': [10, 0, 5, 10, 0, 0],
                   'pct_val': [None, -10, None, None, -10, -10]})
df = df.sort_values(['date', 'idx'])
print df

看起来像:

         date  idx  pct_val  val
0  2016-04-30    1      NaN   10
3  2016-04-30    2      NaN   10
1  2016-05-31    1    -10.0    0
4  2016-05-31    2    -10.0    0
2  2016-06-31    1      NaN    5
5  2016-06-31    2    -10.0    0

解决方案
def cumcustom(df):
    df = df.copy()
    running_total = 0
    for idx, row in df.iterrows():
        if pd.isnull(row.ix['pct_val']):
            running_total += row.ix['val']
        else:
            running_total *= row.ix['pct_val'] / 100. + 1
        df.loc[idx, 'cumcustom'] = running_total
    return df

然后应用。
df.groupby('idx').apply(cumcustom).reset_index(drop=True).sort_values(['date', 'idx'])

看起来像:

         date  idx  pct_val  val  cumcustom
0  2016-04-30    1      NaN   10       10.0
3  2016-04-30    2      NaN   10       10.0
1  2016-05-31    1    -10.0    0        9.0
4  2016-05-31    2    -10.0    0        9.0
2  2016-06-31    1      NaN    5       14.0
5  2016-06-31    2    -10.0    0        8.1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接