Numpy.correlate计算时间序列滞后0互相关系数与其他方法结果不符的问题排查
嘿,别担心,新手遇到这种数值计算不一致的问题太正常了!让我们一步步拆解你代码里的问题,找到根源~
首先,先明确核心矛盾:你提到用三种方法得到lag0的相关系数是-0.178,但numpy返回-0.210。我们先从你的代码归一化步骤入手,这是最容易出错的地方。
你的代码里的关键错误:归一化方式不正确
看你对ts1的处理:
ts1 = (ts1-np.mean(ts1))/(np.std(ts1)*len(ts1)) ts2 = (ts2-np.mean(ts2))/(np.std(ts2))
这里你对ts1多除以了len(ts1),这完全不符合互相关/皮尔逊相关系数的归一化逻辑!
正确的归一化逻辑
要得到和皮尔逊相关系数(lag0互相关就是皮尔逊相关系数)一致的结果,你有两种正确的方式:
方式1:直接使用numpy/pandas的内置函数计算
不需要手动归一化,直接调用现成函数,结果最准确:
# 用numpy的corrcoef,返回相关矩阵,[0,1]位置就是S1和S2的相关系数 r = np.corrcoef(ts1, ts2)[0, 1] print(f"皮尔逊相关系数(lag0):{r:.3f}") # 或者用pandas的corr方法,更直观 r_pd = df['s1'].corr(df['s2']) print(f"Pandas计算的相关系数:{r_pd:.3f}")
这两个函数计算的都是标准的皮尔逊相关系数,公式为:
$$r = \frac{\sum_{i=1}^n (S1_i - \overline{S1})(S2_i - \overline{S2})}{\sqrt{\sum_{i=1}^n (S1_i - \overline{S1})^2} \sqrt{\sum_{i=1}^n (S2_i - \overline{S2})^2}}$$
方式2:手动标准化后用np.correlate计算
如果你想用np.correlate得到lag0的结果,需要对两个序列都做Z-score标准化(即(x - 均值)/标准差),然后取full模式结果的中间值(因为full模式返回所有滞后的互相关,长度为2n-1,中间索引为n-1,对应lag0):
# 正确的标准化:对两个序列都做Z-score转换 ts1_std = (ts1 - np.mean(ts1)) / np.std(ts1) ts2_std = (ts2 - np.mean(ts2)) / np.std(ts2) # 计算全滞后互相关 corr_full = np.correlate(ts1_std, ts2_std, "full") # 取lag0的结果(中间位置) lag0_corr = corr_full[len(ts1_std)-1] print(f"手动标准化后lag0互相关:{lag0_corr:.3f}")
标准化后的互相关lag0值,和皮尔逊相关系数完全一致,因为标准化后的序列协方差等于相关系数。
为什么你的代码得到了错误的结果?
你对ts1的归一化多除以了len(ts1),导致ts1的幅值被错误缩小了n倍,打乱了互相关的比例关系。另外你用np.flip颠倒了互相关结果的顺序,这会让lag0的位置从中间变成最后,也是一个小失误。
修正后的完整代码
import numpy as np import pandas as pd import pathlib home = pathlib.Path.home() filename = home/"Documents"/"Numcorr.csv" colnames = ["s1","s2"] df = pd.read_csv(filename, names=colnames, header=None) ts1 = df["s1"] ts2 = df["s2"] # 方法1:直接用numpy计算相关系数 r_corrcoef = np.corrcoef(ts1, ts2)[0, 1] print(f"numpy.corrcoef计算的lag0相关系数:{r_corrcoef:.3f}") # 方法2:手动标准化后用np.correlate计算 ts1_std = (ts1 - np.mean(ts1)) / np.std(ts1) ts2_std = (ts2 - np.mean(ts2)) / np.std(ts2) corr_full = np.correlate(ts1_std, ts2_std, "full") lag0_corr = corr_full[len(ts1_std)-1] print(f"np.correlate计算的lag0相关系数:{lag0_corr:.3f}") # 方法3:用pandas的corr方法 r_pd = df['s1'].corr(df['s2']) print(f"pandas.corr计算的相关系数:{r_pd:.3f}") # 保存全滞后互相关结果(无需flip) opfile = home/"Documents"/"xcorr.csv" df3 = pd.DataFrame(corr_full) df3.to_csv(opfile, index=False)
额外说明
如果你想让结果和spreadsheet完全一致,需要确认spreadsheet用的是总体标准差还是样本标准差:
- Excel的CORREL函数默认用总体标准差(除以n),numpy的
np.std默认也是ddof=0(总体标准差),结果应该一致。 - 如果你的spreadsheet用了样本标准差(除以n-1),那需要在
np.std里加上ddof=1参数,比如np.std(ts1, ddof=1)。
备注:内容来源于stack exchange,提问作者fidjohnpatent

