在处理时间序列问题时,通常会遇到累计计算问题,即:对于每个时间,计算从开始到时间的样本的统计量值。pandas中有现成的cumsum、cumprod、cummax和cummin分别用于计算累计求和、累计乘积、累计最大值和最小值。不过没有cumvar函数用来计算累计方差。当然可以通过简单循环来计算每一个时间的累计方差,不过在数据量大的情况下,这样效率太低了。
有变量的一组长度为的历史样本(记为):
其均值为:
方差为:
其中为自由度,一般计算样本方差时(无偏估计),计算总体方差时(有偏估计)。
现在有一组长度为的增量样本(记为):
其均值和方差分别为:
我们要通过上面给出的两组样本分别的均值和方差公式来计算两组样本合并在一起之后的全样本:
的均值和方差。
全样本均值为:
全样本方差为:
通过Python实现增量方差算法。
以10万个样本作为测试,迭代算法用时13.93秒,增量算法用时3.42秒。
以100万个样本作为测试,迭代算法用时81.68秒,增量算法用时6.81秒。
迭代算法用时呈指数递增,增量算法使用是线性递增的,效率提升十分明显。
x# -*- coding: utf-8 -*-import timeimport numpy as npdef cumvar_iter(series, ddof=1): '''累计方差计算——迭代''' cumvar = np.nan * np.zeros(len(series),) for k in range(len(series)): cumvar[k] = np.var(series[:k+1], ddof=ddof) return cumvardef cumvar_delta(series, ddof=1): '''累计方差计算——增量算法''' def delta_var(n0, mean0, var0, n1, mean1, var1, ddof=1): ''' 增量方差算法 ''' n = n0+n1 if n0 <= ddof or n1 <= ddof or n <= ddof: # 样本量必须大于自由度 return np.nan fm = n - ddof mean = (n0 * mean0 + n1 * mean1) / n fz1 = (n0-ddof) * var0 + n0 * (mean - mean0) ** 2 fz2 = (n1-ddof) * var1 + n1 * (mean - mean1) ** 2 var = (fz1 + fz2) / fm return var # 累计均值 cummean = np.cumsum(series) / np.arange(1, len(series)+1) # 累计方差 cumvar = np.nan * np.ones(len(series),) if ddof == 0: cumvar[0] = 0 else: for k in range(ddof, ddof+ddof+1): cumvar[k] = np.var(series[:k+1], ddof=ddof) for k in range(ddof+ddof+1, len(series)): var0, mean0, n0 = cumvar[k-ddof-1], cummean[k-ddof-1], k-ddof var1 = np.var(series[k-ddof:k+1], ddof=ddof) mean1 = np.mean(series[k-ddof:k+1]) # 增量方差 cumvar[k] = delta_var(n0, mean0, var0, ddof+1, mean1, var1, ddof=ddof) return cumvarif __name__ == '__main__': # 生成一个长度为十万的测试序列 series = np.random.randint(10, 1000, (100000,)) start_time = time.time() cumvar1 = cumvar_iter(series, ddof=1) print(f'迭代算法用时: {round(time.time()-start_time, 6)}s.') start_time = time.time() cumvar2 = cumvar_delta(series, ddof=1) print(f'增量算法用时: {round(time.time()-start_time, 6)}s.') # 结果: # 迭代算法用时: 13.930764s. # 增量算法用时: 3.415858s.