如何提升SciPy皮尔逊相关系数大量成对计算的速度?
我明白你的问题——处理这么大的数据集时,双重循环逐对计算确实会慢到让人抓狂。咱们来一步步优化这个流程,把运行时间从几十分钟压缩到几分钟甚至更短。
核心问题分析
你的原始代码慢主要卡两个点:
- 双重循环+逐行
append:DataFrame.append每次都会创建新的DataFrame,随着结果集变大,开销会指数级增长;而且逐个调用pearsonr完全没利用numpy向量化运算的优势。 - 重复列筛选:每次循环都重新筛选
_PC和_lncRNAs列,虽然影响不大,但可以提前预处理减少冗余操作。
优化方案1:向量化计算(首选最快方案)
我们可以把两组列分别提取为矩阵,用向量化方式批量计算相关系数和p值,彻底丢掉循环。
import pandas as pd import numpy as np from scipy import stats def correlation_analysis_fast(lncRNA_PC_T): # 提前筛选两组列,避免循环中重复计算 pc_cols = [col for col in lncRNA_PC_T.columns if "_PC" in col] lnc_cols = [col for col in lncRNA_PC_T.columns if "_lncRNAs" in col] # 转成numpy数组,加快后续计算 X = lncRNA_PC_T[pc_cols].values # shape: (1222, 列数_PC) Y = lncRNA_PC_T[lnc_cols].values # shape: (1222, 列数_lncRNAs) n_rows = X.shape[0] # 预计算均值和标准差,用于相关系数和p值推导 X_mean = X.mean(axis=0) X_std = X.std(axis=0, ddof=1) Y_mean = Y.mean(axis=0) Y_std = Y.std(axis=0, ddof=1) # 批量计算协方差矩阵,再转换为相关系数矩阵 cov_matrix = np.dot(X.T, Y - Y_mean) / (n_rows - 1) corr_matrix = cov_matrix / np.outer(X_std, Y_std) # shape: (列数_PC, 列数_lncRNAs) # 基于t分布计算p值(Pearson相关的p值公式) t_stat = corr_matrix * np.sqrt((n_rows - 2) / (1 - corr_matrix**2)) p_matrix = 2 * (1 - stats.t.cdf(np.abs(t_stat), df=n_rows-2)) # 把矩阵结果转换成目标格式的DataFrame index = [f"{pc}_{lnc}" for pc in pc_cols for lnc in lnc_cols] result = pd.DataFrame({ "PCC": corr_matrix.flatten(), "p-value": p_matrix.flatten() }, index=index) return result
为什么这个快?
- 所有计算都是numpy底层的C实现向量化操作,比Python循环快几个数量级。
- 只做一次列筛选和矩阵运算,没有重复冗余操作。
- 最后一次性构建DataFrame,彻底避免了逐行
append的巨大开销。
优化方案2:并行计算(向量化仍有压力时可选)
如果你的机器有多核心CPU,可以用并行库把列对计算拆分到多个进程中。这里推荐joblib,用法简单:
from joblib import Parallel, delayed import pandas as pd from scipy.stats import pearsonr def correlation_analysis_parallel(lncRNA_PC_T, n_jobs=-1): pc_cols = [col for col in lncRNA_PC_T.columns if "_PC" in col] lnc_cols = [col for col in lncRNA_PC_T.columns if "_lncRNAs" in col] # 定义单个列对的计算函数 def compute_single_pair(pc_col, lnc_col): corr, p_val = pearsonr(lncRNA_PC_T[pc_col], lncRNA_PC_T[lnc_col]) return pd.Series( [corr, p_val], index=["PCC", "p-value"], name=f"{pc_col}_{lnc_col}" ) # 并行计算所有列对,n_jobs=-1会用满所有CPU核心 results = Parallel(n_jobs=n_jobs)( delayed(compute_single_pair)(pc, lnc) for pc in pc_cols for lnc in lnc_cols ) # 合并所有结果 return pd.concat(results, axis=1).T
注意点
- 并行计算的开销主要在进程间数据传输,所以适合列对数量极多的场景,但速度还是不如向量化方案。
- 如果你的数据集已经占满内存,并行可能会导致内存溢出,需要谨慎使用。
额外小优化建议
- 提前转numpy数组:不管用哪种方案,先把DataFrame列转成numpy数组,比直接访问DataFrame列更快。
- 内存压缩:如果精度足够,可以用
dtype=np.float32存储数据,减少内存占用,间接加快计算。 - 避免全局变量:把DataFrame作为函数参数传入,减少不必要的内存拷贝。
内容的提问来源于stack exchange,提问作者ARJ
相关产品推荐
相关产品推荐

