跳转至

计算部分相关系数矩阵

本文使用相关系数的矩阵表达形式,实现了计算部分相关系数矩阵的加速算法,并实证检验了三种计算相关系数矩阵方法的运行速度。

  • 在计算部分相关系数矩阵时,自定义的加速算法 相比 Pandas .corr() 方法提速约 2180 倍,比 Numpy .corrcoef() 方法提速约 115 倍。
  • 在计算全部相关系数矩阵时,Numpy .corrcoef() 方法比自定义的加速算法略快 \(10\%\),比 Pandas .corr() 方法快约 20 倍。

问题描述

我们需要计算若干列的相关系数矩阵的一部分。例如,原始数据有 10000 列、100 行,如果直接对这 10000 列数据计算相关系数矩阵,那么会得到一个 \(10000 \times 10000\) 的矩阵,但我们只需要计算某 10 列与其他列的相关系数,那么我们只需要得到 \(10000 \times 10\) 的矩阵即可。

第一种方法是,先计算出 \(10000 \times 10000\) 的矩阵(这用 Pandas 的 .corr() 很容易实现),然后再取出我们需要的 \(10000 \times 10\) 的矩阵。但这样做的问题是,计算出的 \(10000 \times 10000\) 的矩阵的运算量非常大,这其中有很多值是我们不需要的,因此计算速度很慢。虽然 Numpy 中的 .corrcoef() 的计算速度比 Pandas 中的 .corr() 快很多,但是它仍然进行了很多不必要的运算。

第二种方法是,从相关系数的矩阵表达形式进行推导,手动用向量化运算得到我们需要的 10000x10 的矩阵。它的结果保证准确,且不会进行不必要的计算。

生成数据

本例中,我们生成一个 100 * 10000 的 Pandas 数据框,用于比较各种计算协方差矩阵方法的速度。

Python
import pandas as pd
import numpy as np
Python
np.random.seed(0)

df = pd.DataFrame(np.random.rand(100, 10000))
df

image-20231030012252199

使用不同方法计算相关系数矩阵

使用 Pandas 的 .corr() 方法计算相关系数矩阵

Python
df.corr()

image-20231030012318172

如果我们只需要得到某 10 列与其他列的相关系数矩阵,那么我们只需要取出相关系数矩阵中的这 10 列即可。

Python
result_pd = df.corr()[range(10)]
result_pd

image-20231030012340336

使用 Numpy 的 .corrcoef() 方法计算相关系数矩阵

Numpy 的 .corrcoef() 方法的调用中,需要注意将 rowvar 参数设置为 False,表示每一列为一个变量,每一行为一个观测值。

Python
result_np = pd.DataFrame(np.corrcoef(df.values, rowvar=False)[:, :10])
result_np

image-20231030012358091

使用矩阵表达式计算相关系数矩阵

相关系数的矩阵表达形式的数学原理,可以参考 Quickest way to calculate subset of correlation matrix - stackoverflow

image-20231030012536572

Python
def cal_corr(x, select_cols):
    n = x.shape[0]
    xsum = np.sum(x, 0, keepdims=True)
    xstd = np.std(x, 0, keepdims=True)

    return (
        np.dot(x.T, x[:, select_cols]) - np.dot(xsum.T, xsum[:, select_cols]) / n
    ) / (np.dot(xstd.T, xstd[:, select_cols]) * n)
Python
result_cal_corr = pd.DataFrame(cal_corr(df.values, range(10)))
result_cal_corr

image-20231030012422277

检验各方法计算结果的一致性

Python
np.isclose(result_pd, result_np).all()
Text Only
True
Python
np.isclose(result_pd, result_cal_corr).all()
Text Only
True

比较各方法的计算速度

若只需要计算某 10 列与其他列的相关系数,那么:

  • 使用矩阵表达式的方法的计算速度最快,因为它不会进行不必要的计算。
  • 其次是 Numpy 的 .corrcoef() 方法。
  • 最慢的是 Pandas 的 .corr() 方法。
Python
%%timeit
df.corr()[range(10)]
Text Only
10.9 s ± 85.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Python
%%timeit
pd.DataFrame(np.corrcoef(df.values, rowvar=False)[:, :10])
Text Only
587 ms ± 22.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Python
%%timeit
pd.DataFrame(cal_corr(df.values, range(10)))
Text Only
5.08 ms ± 334 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

若需要计算所有列之间的相关系数,那么:

  • Numpy 的 .corrcoef() 方法的计算速度最快。
  • 其次是矩阵表达式的方法。
  • 最慢的是 Pandas 的 .corr() 方法。
Python
%%timeit
df.corr()
Text Only
11 s ± 92.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Python
%%timeit
pd.DataFrame(np.corrcoef(df.values, rowvar=False))
Text Only
576 ms ± 14.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Python
%%timeit
pd.DataFrame(cal_corr(df.values, range(10000)))
Text Only
674 ms ± 50.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

评论