如何在Pandas中计算列之间的成对相关性的p值?

9
Pandas有非常方便的函数pd.corr()来对列进行成对相关性计算。这意味着可以比较任意长度的列之间的相关性。例如:
df = pd.DataFrame(np.random.randint(0,100,size=(100, 10)))

     0   1   2   3   4   5   6   7   8   9
0    9  17  55  32   7  97  61  47  48  46
1    8  83  87  56  17  96  81   8  87   0
2   60  29   8  68  56  63  81   5  24  52
3   42  76   6  75   7  59  19  17   3  63
...

现在可以使用df.corr(method='pearson')来测试所有10列之间的相关性:
      0         1         2         3         4         5         6         7         8         9
0  1.000000  0.082789 -0.094096 -0.086091  0.163091  0.013210  0.167204 -0.002514  0.097481  0.091020
1  0.082789  1.000000  0.027158 -0.080073  0.056364 -0.050978 -0.018428 -0.014099 -0.135125 -0.043797
2 -0.094096  0.027158  1.000000 -0.102975  0.101597 -0.036270  0.202929  0.085181  0.093723 -0.055824
3 -0.086091 -0.080073 -0.102975  1.000000 -0.149465  0.033130 -0.020929  0.183301 -0.003853 -0.062889
4  0.163091  0.056364  0.101597 -0.149465  1.000000 -0.007567 -0.017212 -0.086300  0.177247 -0.008612
5  0.013210 -0.050978 -0.036270  0.033130 -0.007567  1.000000 -0.080148 -0.080915 -0.004612  0.243713
6  0.167204 -0.018428  0.202929 -0.020929 -0.017212 -0.080148  1.000000  0.135348  0.070330  0.008170
7 -0.002514 -0.014099  0.085181  0.183301 -0.086300 -0.080915  0.135348  1.000000 -0.114413 -0.111642
8  0.097481 -0.135125  0.093723 -0.003853  0.177247 -0.004612  0.070330 -0.114413  1.000000 -0.153564
9  0.091020 -0.043797 -0.055824 -0.062889 -0.008612  0.243713  0.008170 -0.111642 -0.153564  1.000000

有没有一种简单的方法(最好在pandas中)获得相应的p值,就像scipy的kendalltau()函数返回的那样?


什么是P值?这里没有假设需要测试。 - Yuca
2
我猜测假设是“变量是否相关”。 - Rahul Agarwal
2
@Yuca 我正在寻找“双侧p值的假设检验,其零假设是缺乏关联”,就像kendalltau()函数返回的那样。 - n1000
4个回答

36

为什么不使用 pandas.DataFrame.corr() 的 "method" 参数:

  • pearson:标准相关系数。
  • kendall:Kendall Tau 相关系数。
  • spearman:Spearman 等级相关性。
  • callable:接受两个1d ndarrays输入并返回一个浮点数的可调用对象。
from scipy.stats import kendalltau, pearsonr, spearmanr

    def kendall_pval(x,y):
        return kendalltau(x,y)[1]
    
    def pearsonr_pval(x,y):
        return pearsonr(x,y)[1]
    
    def spearmanr_pval(x,y):
        return spearmanr(x,y)[1]

然后

corr = df.corr(method=pearsonr_pval)

谢谢!我刚测试了一下,但是df.corr(method='pearson')df.corr(method=pearsonr_pval)的结果不同。它们应该是相同的吗? - n1000
3
df.corr(method='pearson') 计算的是相关系数,而 df.corr(method=pearsonr_pval) 计算的是p值,它们是不同的东西。如果你定义:def pearson_corr(x,y): return pearsonr(x,y)[0],那么 df.corr(method='pearson') 应该会给出与 df.corr(method=pearson_corr) 相同的输出结果 :) - Ramon Dalmau
2
在我看来,最好的答案是你需要 pandas >= 0.24 版本才能使用它。非常好用! - user3017048
这是目前最好的答案,在 pandas > 0.24 中。 - EHB

7

很可能只是循环。基本上这就是pandas在源代码中生成相关矩阵时所做的事情:

import pandas as pd
import numpy as np
from scipy import stats

df_corr = pd.DataFrame() # Correlation matrix
df_p = pd.DataFrame()  # Matrix of p-values
for x in df.columns:
    for y in df.columns:
        corr = stats.pearsonr(df[x], df[y])
        df_corr.loc[x,y] = corr[0]
        df_p.loc[x,y] = corr[1]

如果你想利用对称性,只需要计算其中大约一半的内容,那么可以这样做:
mat = df.values.T
K = len(df.columns)
correl = np.empty((K,K), dtype=float)
p_vals = np.empty((K,K), dtype=float)

for i, ac in enumerate(mat):
    for j, bc in enumerate(mat):
        if i > j:
            continue
        else:
            corr = stats.pearsonr(ac, bc)
            #corr = stats.kendalltau(ac, bc)

        correl[i,j] = corr[0]
        correl[j,i] = corr[0]
        p_vals[i,j] = corr[1]
        p_vals[j,i] = corr[1]

df_p = pd.DataFrame(p_vals)
df_corr = pd.DataFrame(correl)
#pd.concat([df_corr, df_p], keys=['corr', 'p_val'])

1
这将有效:
from scipy.stats import pearsonr

column_values = [column for column in df.columns.tolist() ]


df['Correlation_coefficent'], df['P-value'] = zip(*df.T.apply(lambda x: pearsonr(x[column_values ],x[column_values ])))
df_result = df[['Correlation_coefficent','P-value']]

我得到了一个 ("pearsonr() missing 1 required positional argument: 'y'", 'occurred at index 0') 错误。我只需要重复 x[column_values] 吗? - n1000
这里仍然有些问题... 'Sam' 列应该从哪里来?像这样我会得到 KeyError: "['Sam'] not in index" - n1000
我在尝试另一个数据框时不加思考地复制粘贴了...现在可以试试。 - Rahul Agarwal
谢谢!这将返回一个2x100的表格。然而,我正在寻找一个10x10的表格,显示列之间的相关性/ p值(而不是行)。如果我的问题不够清楚,请原谅。请让我知道是否可以改进它。 - n1000

0

这个对你有用吗?

#call the correlation function, you could round the values if needed
df_c = df_c.corr().round(1)
#get the p values
pval = df_c.corr(method=lambda x, y: pearsonr(x, y)[1]) - np.eye(*rho.shape)
#set the p values, *** for less than 0.001, ** for less than 0.01, * for less than 0.05
p = pval.applymap(lambda x: ''.join(['*' for t in [0.001,0.01,0.05] if x<=t]))
#dfc_2 below will give you the dataframe with correlation coefficients and p values
df_c2 = df_c.astype(str) + p

#you could also plot the correlation matrix using sns.heatmap if you want
#plot the triangle
matrix = np.triu(df_c.corr())
#convert to array for the heatmap
df_c3 = df_c2.to_numpy()

#plot the heatmap
plt.figure(figsize=(13,8))
sns.heatmap(df_c, annot = df_c3, fmt='', vmin=-1, vmax=1, center= 0, cmap= 'coolwarm', mask = matrix)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接