Pandas 求和与滞后值的条件乘积?

3 投票
2 回答
1050 浏览
提问于 2025-04-20 04:26

我想要得到一个累积和,这个累积和会根据另一个变量的乘积和之前的累积和的值来变化(听起来有点像数学术语,我知道……请耐心听我说)

下面是一个例子的设置:

import pandas as pd
df = pd.DataFrame([1,1,1.004878,1,1.043394],columns=['xx'])
df['n'] = 1000000.0

这会组合成:

       xx        n
0  1.000000  1000000
1  1.000000  1000000
2  1.004878  1000000
3  1.000000  1000000
4  1.043394  1000000

现在,我们需要把 xx 乘以前一个的 n 值,然后逐步计算这个值的累积和:

cs = pd.Series([0.0] * len(df))
cs[0] = df.ix[0]['n']
for i,e in enumerate(df.iterrows()):
    if i == 0: continue
    cs[i] = df.ix[i]['xx'] * cs[(i - 1)]

这样就会产生以下结果:

0    1000000.000000
1    1000000.000000
2    1004878.000000
3    1004878.000000
4    1048483.675932
dtype: float64

问题是:有没有办法在 pandas/numpy 中做到这一点,而不需要逐行处理?如果没有,在被迫逐行处理时,有没有什么优化代码的小技巧?创造性地设计一个索引能否帮助到这个情况?在处理超过10000行的数据时,性能是个问题。

2 个回答

1

我不太明白'n'到底是用来干嘛的(它是不是总是等于1,000,000?),不过用cumprod来匹配你上面的结果还是挺简单的:

In [60]: df.xx.cumprod() * 1e6

Out[60]: 0    1000000.000000
         1    1000000.000000
         2    1004878.000000
         3    1004878.000000
         4    1048483.675932
4

首先,你的for循环可以简化成:

for i in xrange(1, len(df)):
    cs[i] = df.ix[i]['xx'] * cs[(i - 1)]

(更多数学的复杂内容)每个在 cs[1:] 中的项都是 df['xx'] 中所有之前项的乘积(累积乘积),再乘以 dfn 列的第一个项。

>>> df
         xx        n
0  1.000000  1000000
1  1.000000  1000000
2  1.004878  1000000
3  1.000000  1000000
4  1.043394  1000000
>>> a = df['xx']
>>> a
0    1.000000
1    1.000000
2    1.004878
3    1.000000
4    1.043394
Name: xx, dtype: float64
>>> a = a.cumprod()
>>> a
0    1.000000
1    1.000000
2    1.004878
3    1.004878
4    1.048484
Name: xx, dtype: float64
>>> a = a * df['n'][0]
>>> a
0    1000000.000000
1    1000000.000000
2    1004878.000000
3    1004878.000000
4    1048483.675932
Name: xx, dtype: float64
>>> np.all(a == cs)
True
>>> 

a = df['xx'].cumprod() * df['n'][0]

这不是一个技巧。之所以能这样做,是因为 df['xx'][0] 的值是1。如果它是其他任何值,并且 cs[0] = df.ix[0]['n'] 不是一个简化方式,那么 cumprod 就不管用了。

展开每个 cs 的项会得到:

cs[0] = df['n'][0]
cs[1] = df['xx'][1] * df['n'][0]
cs[2] = df['xx'][2] * df['xx'][1] * df['n'][0]
cs[3] = df['xx'][3] * df['xx'][2] * df['xx'][1] * df['n'][0]
cs[4] = df['xx'][4] * df['xx'][3] * df['xx'][2] * df['xx'][1] * df['n'][0]

因为 df['xx'][0] 等于1,并且 df['xx'][0] * df['n'][0] == df['n'][0],所以:

cs[0] = df['xx'][0] * df['n'][0]
cs[1] = df['xx'][1] * df['xx'][0] * df['n'][0]
cs[2] = df['xx'][2] * df['xx'][1] * df['xx'][0] * df['n'][0]
cs[3] = df['xx'][3] * df['xx'][2] * df['xx'][1] * df['xx'][0] * df['n'][0]
cs[4] = df['xx'][4] * df['xx'][3] * df['xx'][2] * df['xx'][1] * df['xx'][0] * df['n'][0]

如果你稍微改变一下问题的条件,比如在每次迭代后需要从上一次计算的n值中减去0.05%,那么cumprod还有效吗?

如果你做了项展开的练习,你应该会发现新的条件导致了乘以一个缩放因子的累积乘积。可以有两种方法来处理这个问题——这两种方法都会在循环计算中产生一些小的浮点误差。再次强调,你需要考虑到 df['xx'] 中第一个项的值是1。

for i in xrange(1, len(df)):
    cs[i] = df.ix[i]['xx'] * (.9995 * cs[(i - 1)])

>>> k
array([ 1.    ,  0.9995,  0.9995,  0.9995,  0.9995])
>>> z = df['xx'] * k
>>> z
0    1.000000
1    0.999500
2    1.004376
3    0.999500
4    1.042872
Name: xx, dtype: float64
>>> z = z.cumprod() * df['n'][0]
>>> cs - z
0    0.000000e+00
1    0.000000e+00
2    0.000000e+00
3    0.000000e+00
4   -1.164153e-10
dtype: float64
>>> 
>>> z = df['xx'].cumprod() * df['n'][0]
>>> z *= k.cumprod()
>>> cs - z
0    0.000000e+00
1    0.000000e+00
2   -1.164153e-10
3    0.000000e+00
4    0.000000e+00
dtype: float64
>>> 

撰写回答