跳转至

对含有空值的数据使用 np.corrcoef 计算 Pearson 和 Spearman 相关系数

本文实验探究了 np.corrcoef 在对含有空值的数据计算 Pearson 相关系数和 Spearman 相关系数时的结果。

  • np.corrcoef 在计算相关系数时,如果数据中存在一个空值,那么空值所在列与其他列的相关系数也会为空值。
  • 如果需要忽略空值后计算相关系数,可以使用 np.ma.corrcoef,它的参数 allow_masked 默认为 True。当传入一个 MaskedArray 对象时,np.ma.corrcoef 会忽略掉其中的空值。
  • 在对含有空值的数据使用 argsort().argsort() 对数据进行排序后,空值会被当做最小值,它也会获得一个排序值,空值内部的排序值大小取决于该空值所在的位置。因此,计算 Spearman 相关系数时,需要先手动删除空值。

一列数据全为空值

Pearson 相关系数

Python
import numpy as np
import pandas as pd

np.random.seed(42)
Python
# 创建一个 DataFrame,其中一列全是 NaN
df = pd.DataFrame({"A": np.random.rand(10), "B": np.full(10, np.nan)})
df
Text Only
          A   B
0  0.374540 NaN
1  0.950714 NaN
2  0.731994 NaN
3  0.598658 NaN
4  0.156019 NaN
5  0.155995 NaN
6  0.058084 NaN
7  0.866176 NaN
8  0.601115 NaN
9  0.708073 NaN

若某一列全是空值,那么这一列与其他列的相关系数也为空值。

Python
np.corrcoef(df.values, rowvar=False)
Text Only
array([[ 1., nan],
       [nan, nan]])

Spearman 相关系数

需要注意的是,在对含有空值的数据进行排序后,空值也会获得一个排序值 ,这些排序值的大小取决于该空值所在的位置。例如,下面的 B 列中,原本空值的排序变成了 0, 1, 2, ..., 9

Python
# 使用 argsort().argsort() 对数据进行排序
df_ranks = df.apply(lambda x: x.argsort().argsort())
df_ranks
Text Only
   A  B
0  3  0
1  9  1
2  7  2
3  4  3
4  2  4
5  1  5
6  0  6
7  8  7
8  5  8
9  6  9

再计算相关系数时,空值的排序值会被当作实际值,因此会影响最终的计算结果。

Python
# 计算 Spearman 相关系数
corr_matrix = np.corrcoef(df_ranks.values, rowvar=False)
print(corr_matrix)
Text Only
[[ 1.         -0.05454545]
 [-0.05454545  1.        ]]

一列数据部分为空值

Pearson 相关系数

Python
# 创建两列随机数
df = pd.DataFrame({"A": np.random.rand(10), "B": np.random.rand(10)})

# 将 'B' 列的部分值设置为 NaN
df.loc[[0, 1], "B"] = np.nan
df
Text Only
          A         B
0  0.020584       NaN
1  0.969910       NaN
2  0.832443  0.292145
3  0.212339  0.366362
4  0.181825  0.456070
5  0.183405  0.785176
6  0.304242  0.199674
7  0.524756  0.514234
8  0.431945  0.592415
9  0.291229  0.046450

对于部分为空值的数据,np.corrcoef 的结果也是 nan

Python
np.corrcoef(df.values, rowvar=False)
Text Only
array([[ 1., nan],
       [nan, nan]])

如果我们需要忽略空值后计算相关系数,可以使用 np.ma.corrcoef,它的参数 allow_masked 默认为 True。当传入一个 MaskedArray 对象时,np.ma.corrcoef 会忽略掉其中的空值。如果某个变量的观测值全为空值,那么结果为 0。

np.ma.corrcoef 的 allow_masked 参数

If True, masked values are propagated pair-wise: if a value is masked in x, the corresponding value is masked in y. If False, raises an exception. Because bias is deprecated, this argument needs to be treated as keyword only to avoid a warning.

Python
masked_data = np.ma.masked_invalid(df.values)
print(masked_data)
print("type: ", type(masked_data))
Text Only
[[0.020584494295802447 --]
 [0.9699098521619943 --]
 [0.8324426408004217 0.29214464853521815]
 [0.21233911067827616 0.3663618432936917]
 [0.18182496720710062 0.45606998421703593]
 [0.18340450985343382 0.7851759613930136]
 [0.3042422429595377 0.19967378215835974]
 [0.5247564316322378 0.5142344384136116]
 [0.43194501864211576 0.5924145688620425]
 [0.2912291401980419 0.046450412719997725]]
type:  <class 'numpy.ma.core.MaskedArray'>

masked_data 中 第 1 行和第 2 行有空值,np.ma.corrcoef 会忽略掉空值所在的行,再计算相关系数。

Python
# 计算相关系数
corrcoef = np.ma.corrcoef(masked_data, rowvar=False)
print(corrcoef)
Text Only
[[1.0 -0.15541564545580966]
 [-0.15541564545580966 1.0]]

下面,我们手动将前 2 行删去,再用 np.corrcoef 计算相关系数,可以看到结果与 np.ma.corrcoef 的结果一致。

Python
print(np.corrcoef(df.values[2:], rowvar=False))
Text Only
[[ 1.         -0.15541565]
 [-0.15541565  1.        ]]

Spearman 相关系数

Python
# 使用 argsort().argsort() 对数据进行排序
df_ranks = df.apply(lambda x: x.argsort().argsort())
df_ranks
Text Only
   A  B
0  0  0
1  9  1
2  8  4
3  3  5
4  1  6
5  2  9
6  5  3
7  7  7
8  6  8
9  4  2

可以看到,空值会被当做最小值,并且同为空值的排序取决于空值所在的行号。因此,下面的结果是不准确的。

Python
# 计算 Spearman 相关系数
corr_matrix = np.corrcoef(df_ranks.values, rowvar=False)
print(corr_matrix)
Text Only
[[ 1.         -0.03030303]
 [-0.03030303  1.        ]]

如果想要忽略空值后再计算 Spearman 相关系数,需要手动先将空值删除:

Python
df_no_nan = df.dropna()
print(df_no_nan)
Text Only
          A         B
2  0.832443  0.292145
3  0.212339  0.366362
4  0.181825  0.456070
5  0.183405  0.785176
6  0.304242  0.199674
7  0.524756  0.514234
8  0.431945  0.592415
9  0.291229  0.046450
Python
df_no_nan_ranks = df_no_nan.apply(lambda x: x.argsort().argsort())
print(df_no_nan_ranks)
Text Only
   A  B
2  7  2
3  2  3
4  0  4
5  1  7
6  4  1
7  6  5
8  5  6
9  3  0
Python
# 计算 Spearman 相关系数
corr_matrix = np.corrcoef(df_no_nan_ranks.values, rowvar=False)
print(corr_matrix)
Text Only
[[ 1.         -0.16666667]
 [-0.16666667  1.        ]]

评论