Pandas:具有可变权重的指数衰减总和
Pandas: Exponentially decaying sum with variable weights
类似于这个问题Exponential Decay on Python Pandas DataFrame,我想快速计算数据框中某些列的指数衰减总和。但是,数据框中的行在时间上并不是均匀分布的。因此,虽然 exponential_sum[i] = column_to_sum[i] + np.exp(-const*(time[i]-time[i-1])) * exponential_sum[i-1]
,权重 np.exp(...)
没有分解,但我不清楚如何更改到该问题并仍然利用 pandas/numpy 矢量化。这个问题有 pandas 向量化的解决方案吗?
为了说明所需的计算,这里是一个样本帧,其中 A
的指数移动和存储在 Sum
中,使用衰减常数 1:
time A Sum
0 1.00 1 1.000000
1 2.10 3 3.332871
2 2.13 -1 2.234370
3 3.70 7 7.464850
4 10.00 2 2.013708
5 10.20 1 2.648684
根据您链接的 answer 进行扩展,我想出了以下方法。
首先,请注意:
exponential_sum[i] = column_to_sum[i] +
np.exp(-const*(time[i]-time[i-1])) * column_to_sum[i-1] +
np.exp(-const*(time[i]-time[i-2])) * column_to_sum[i-2] + ...
因此,要进行的主要更改是生成权重空间以匹配上面的公式。我是这样进行的:
time = pd.Series(np.random.rand(10)).cumsum()
weightspace = np.empty((10,10))
for i in range(len(time)):
weightspace[i] = time - time[i]
weightspace = np.exp(weightspace)
不用担心矩阵的左下三角,不会用到的。顺便说一句,必须有一种无需循环即可生成权重空间的方法。
然后在滚动函数中从权重空间中选取权重的方式略有变化:
def rollingsum(array):
weights = weightspace[len(array)-1][:len(array)]
# Convolve the array and the weights to obtain the result
a = np.dot(array, weights).sum()
return a
按预期工作:
dataset = pd.DataFrame(np.random.rand(10,3), columns=["A", "B","C"])
a = pd.expanding_apply(dataset, rollingsum)
这个问题比最初出现的要复杂。我最终使用 numba 的 jit 编译生成器函数来计算指数和。我的最终结果是在我的计算机上不到一秒就计算出 500 万行的指数总和,希望速度足以满足您的需求。
# Initial dataframe.
df = pd.DataFrame({'time': [1, 2.1, 2.13, 3.7, 10, 10.2],
'A': [1, 3, -1, 7, 2, 1]})
# Initial decay parameter.
decay_constant = 1
我们可以将衰减权重定义为exp(-time_delta * decay_constant),并将其初始值设置为1:
df['weight'] = np.exp(-df.time.diff() * decay_constant)
df.weight.iat[0] = 1
>>> df
A time weight
0 1 1.00 1.000000
1 3 2.10 0.332871
2 -1 2.13 0.970446
3 7 3.70 0.208045
4 2 10.00 0.001836
5 1 10.20 0.818731
现在我们将使用 numba 中的 jit 来优化计算指数和的生成器函数:
from numba import jit
@jit(nopython=True)
def exponential_sum(A, k):
total = A[0]
yield total
for i in xrange(1, len(A)): # Use range in Python 3.
total = total * k[i] + A[i]
yield total
我们将使用生成器将值添加到数据框:
df['expSum'] = list(exponential_sum(df.A.values, df.weight.values))
产生所需的输出:
>>> df
A time weight expSum
0 1 1.00 1.000000 1.000000
1 3 2.10 0.332871 3.332871
2 -1 2.13 0.970446 2.234370
3 7 3.70 0.208045 7.464850
4 2 10.00 0.001836 2.013708
5 1 10.20 0.818731 2.648684
所以让我们扩展到 500 万行并检查性能:
df = pd.DataFrame({'time': np.random.rand(5e6).cumsum(), 'A': np.random.randint(1, 10, 5e6)})
df['weight'] = np.exp(-df.time.diff() * decay_constant)
df.weight.iat[0] = 1
%%timeit -n 10
df['expSum'] = list(exponential_sum(df.A.values, df.weight.values))
10 loops, best of 3: 726 ms per loop
类似于这个问题Exponential Decay on Python Pandas DataFrame,我想快速计算数据框中某些列的指数衰减总和。但是,数据框中的行在时间上并不是均匀分布的。因此,虽然 exponential_sum[i] = column_to_sum[i] + np.exp(-const*(time[i]-time[i-1])) * exponential_sum[i-1]
,权重 np.exp(...)
没有分解,但我不清楚如何更改到该问题并仍然利用 pandas/numpy 矢量化。这个问题有 pandas 向量化的解决方案吗?
为了说明所需的计算,这里是一个样本帧,其中 A
的指数移动和存储在 Sum
中,使用衰减常数 1:
time A Sum
0 1.00 1 1.000000
1 2.10 3 3.332871
2 2.13 -1 2.234370
3 3.70 7 7.464850
4 10.00 2 2.013708
5 10.20 1 2.648684
根据您链接的 answer 进行扩展,我想出了以下方法。
首先,请注意:
exponential_sum[i] = column_to_sum[i] +
np.exp(-const*(time[i]-time[i-1])) * column_to_sum[i-1] +
np.exp(-const*(time[i]-time[i-2])) * column_to_sum[i-2] + ...
因此,要进行的主要更改是生成权重空间以匹配上面的公式。我是这样进行的:
time = pd.Series(np.random.rand(10)).cumsum()
weightspace = np.empty((10,10))
for i in range(len(time)):
weightspace[i] = time - time[i]
weightspace = np.exp(weightspace)
不用担心矩阵的左下三角,不会用到的。顺便说一句,必须有一种无需循环即可生成权重空间的方法。
然后在滚动函数中从权重空间中选取权重的方式略有变化:
def rollingsum(array):
weights = weightspace[len(array)-1][:len(array)]
# Convolve the array and the weights to obtain the result
a = np.dot(array, weights).sum()
return a
按预期工作:
dataset = pd.DataFrame(np.random.rand(10,3), columns=["A", "B","C"])
a = pd.expanding_apply(dataset, rollingsum)
这个问题比最初出现的要复杂。我最终使用 numba 的 jit 编译生成器函数来计算指数和。我的最终结果是在我的计算机上不到一秒就计算出 500 万行的指数总和,希望速度足以满足您的需求。
# Initial dataframe.
df = pd.DataFrame({'time': [1, 2.1, 2.13, 3.7, 10, 10.2],
'A': [1, 3, -1, 7, 2, 1]})
# Initial decay parameter.
decay_constant = 1
我们可以将衰减权重定义为exp(-time_delta * decay_constant),并将其初始值设置为1:
df['weight'] = np.exp(-df.time.diff() * decay_constant)
df.weight.iat[0] = 1
>>> df
A time weight
0 1 1.00 1.000000
1 3 2.10 0.332871
2 -1 2.13 0.970446
3 7 3.70 0.208045
4 2 10.00 0.001836
5 1 10.20 0.818731
现在我们将使用 numba 中的 jit 来优化计算指数和的生成器函数:
from numba import jit
@jit(nopython=True)
def exponential_sum(A, k):
total = A[0]
yield total
for i in xrange(1, len(A)): # Use range in Python 3.
total = total * k[i] + A[i]
yield total
我们将使用生成器将值添加到数据框:
df['expSum'] = list(exponential_sum(df.A.values, df.weight.values))
产生所需的输出:
>>> df
A time weight expSum
0 1 1.00 1.000000 1.000000
1 3 2.10 0.332871 3.332871
2 -1 2.13 0.970446 2.234370
3 7 3.70 0.208045 7.464850
4 2 10.00 0.001836 2.013708
5 1 10.20 0.818731 2.648684
所以让我们扩展到 500 万行并检查性能:
df = pd.DataFrame({'time': np.random.rand(5e6).cumsum(), 'A': np.random.randint(1, 10, 5e6)})
df['weight'] = np.exp(-df.time.diff() * decay_constant)
df.weight.iat[0] = 1
%%timeit -n 10
df['expSum'] = list(exponential_sum(df.A.values, df.weight.values))
10 loops, best of 3: 726 ms per loop