Python中是否存在支持快速滚动计算的PCA实现方案?
快速滚动PCA的实现方案
针对时间序列分析中滚动PCA的需求(避免前瞻偏差、动态计算方差解释所需主成分数、滚动降维),以下是几种适合处理大规模数据(>10000行)的高效实现方案:
1. Numba加速手动滚动计算
纯Python循环处理大数组效率极低,用Numba的JIT编译+并行计算可将速度提升数倍。核心是对每个滚动窗口计算协方差矩阵,再通过特征值分解提取主成分与方差解释率。
import numba import numpy as np @numba.jit(nopython=True, parallel=True) def rolling_pca_numba(X, window_size, n_components): n_samples, n_features = X.shape result = np.zeros((n_samples - window_size + 1, n_components)) var_ratios = np.zeros((n_samples - window_size + 1, n_components)) for i in numba.prange(n_samples - window_size + 1): window = X[i:i+window_size] # 中心化窗口数据 window_centered = window - window.mean(axis=0) # 计算协方差矩阵 cov = np.cov(window_centered, rowvar=False) # 特征值分解并按特征值降序排序 eig_vals, eig_vecs = np.linalg.eigh(cov) sorted_idx = np.argsort(eig_vals)[::-1] sorted_eig_vals = eig_vals[sorted_idx] sorted_eig_vecs = eig_vecs[:, sorted_idx] # 提取指定数量主成分并投影 top_vecs = sorted_eig_vecs[:, :n_components] result[i] = window_centered[-1] @ top_vecs # 取窗口最后一行的投影结果,可按需调整 # 计算累计方差解释率 total_var = sorted_eig_vals.sum() var_ratios[i] = sorted_eig_vals[:n_components].cumsum() / total_var return result, var_ratios
注:若需动态计算保留90%方差的主成分数,可在循环内判断累计方差占比,调整提取的主成分数量即可;prange开启多核并行,适合CPU算力充足的场景。
2. RAPIDS cuML GPU加速滚动PCA
如果有GPU资源,cuML库的PCA实现比CPU版本快几十倍,可轻松处理百万级以上的时间序列数据。结合滑动窗口索引即可实现快速滚动计算。
import cuml import numpy as np def rolling_pca_cuml(X, window_size, target_var=0.9): n_samples = X.shape[0] var_counts = [] reduced_data = [] pca = cuml.PCA() # 生成滑动窗口索引 for i in range(n_samples - window_size + 1): window = X[i:i+window_size] pca.fit(window) # 找到累计方差达标所需的最小主成分数 cum_var = np.cumsum(pca.explained_variance_ratio_) k = np.argmax(cum_var >= target_var) + 1 var_counts.append(k) # 对窗口最后一行数据降维 reduced = pca.transform(window[-1].reshape(1, -1))[:, :k] reduced_data.append(reduced.flatten()) return np.array(reduced_data), np.array(var_counts)
注:需提前配置RAPIDS GPU环境,适合超大规模数据集的快速处理。
3. 协方差递推更新优化
无需每次重新计算整个窗口的协方差,用递推公式更新协方差矩阵,减少重复计算量,速度远快于全量计算。
import numpy as np def rolling_pca_recursive(X, window_size, n_components): n_samples, n_features = X.shape result = np.zeros((n_samples - window_size + 1, n_components)) var_ratios = np.zeros((n_samples - window_size + 1, n_components)) # 初始化第一个窗口的均值与协方差 window = X[:window_size] window_mean = window.mean(axis=0) window_centered = window - window_mean cov = np.cov(window_centered, rowvar=False) # 处理第一个窗口 eig_vals, eig_vecs = np.linalg.eigh(cov) sorted_idx = np.argsort(eig_vals)[::-1] sorted_eig_vals = eig_vals[sorted_idx] sorted_eig_vecs = eig_vecs[:, sorted_idx] result[0] = window_centered[-1] @ sorted_eig_vecs[:, :n_components] total_var = sorted_eig_vals.sum() var_ratios[0] = sorted_eig_vals[:n_components].cumsum() / total_var # 递推更新后续窗口 for i in range(1, n_samples - window_size + 1): old_sample = X[i-1] new_sample = X[i+window_size-1] # 更新窗口均值 window_mean = window_mean + (new_sample - old_sample) / window_size # 递推更新协方差矩阵 cov = cov + (new_sample - old_sample).reshape(-1,1) @ (new_sample + old_sample - 2*window_mean).reshape(1,-1) / (window_size - 1) # 特征值分解与主成分提取 eig_vals, eig_vecs = np.linalg.eigh(cov) sorted_idx = np.argsort(eig_vals)[::-1] sorted_eig_vals = eig_vals[sorted_idx] sorted_eig_vecs = eig_vecs[:, sorted_idx] # 投影当前样本并计算方差解释率 current_centered = new_sample - window_mean result[i] = current_centered @ sorted_eig_vecs[:, :n_components] total_var = sorted_eig_vals.sum() var_ratios[i] = sorted_eig_vals[:n_components].cumsum() / total_var return result, var_ratios
注:递推方法存在微小的累积精度误差,但速度优势明显,适合对精度要求不是极端严格的场景。
内容的提问来源于stack exchange,提问作者PyRsquared
相关产品推荐
相关产品推荐

