【问题标题】:pandas columns correlation with statistical significance具有统计显着性的熊猫列相关性
【发布时间】:2014-10-23 16:16:46
【问题描述】:

在给定 pandas 数据框 df 的情况下,获取其列 df.1df.2 之间的相关性的最佳方法是什么?

我不希望输出使用NaN 计算行数,pandas 内置相关性就是这样做的。但我也希望它输出pvalue 或标准错误,而内置不会。

SciPy 似乎被 NaN 追上了,尽管我相信它确实报告了重要性。

数据示例:

     1           2
0    2          NaN
1    NaN         1
2    1           2
3    -4          3
4    1.3         1
5    NaN         NaN

【问题讨论】:

  • 您能提供一个数据示例吗?

标签: python pandas scipy correlation


【解决方案1】:

您可以使用scipy.stats 相关函数来获取 p 值。

例如,如果您正在寻找诸如 pearson 相关之类的相关性,则可以使用 pearsonr 函数。

from scipy.stats import pearsonr
pearsonr([1, 2, 3], [4, 3, 7])

给出输出

(0.7205766921228921, 0.48775429164459994)

元组中的第一个值是相关值,第二个是p值。

在您的情况下,您可以先使用 pandas 的 dropna 函数删除 NaN 值。

df_clean = df[['column1', 'column2']].dropna()
pearsonr(df_clean['column1'], df_clean['column2'])

【讨论】:

    【解决方案2】:

    @Shashank 提供的答案很好。但是,如果您想要纯 pandas 的解决方案,您可能会喜欢这样:

    import pandas as pd
    from pandas.io.data import DataReader
    from datetime import datetime
    import scipy.stats  as stats
    
    
    gdp = pd.DataFrame(DataReader("GDP", "fred", start=datetime(1990, 1, 1)))
    vix = pd.DataFrame(DataReader("VIXCLS", "fred", start=datetime(1990, 1, 1)))
    
    #Do it with a pandas regression to get the p value from the F-test
    df = gdp.merge(vix,left_index=True, right_index=True, how='left')
    vix_on_gdp = pd.ols(y=df['VIXCLS'], x=df['GDP'], intercept=True)
    print(df['VIXCLS'].corr(df['GDP']), vix_on_gdp.f_stat['p-value'])
    

    结果:

    -0.0422917932738 0.851762475093
    

    与统计功能相同的结果:

    #Do it with stats functions. 
    df_clean = df.dropna()
    stats.pearsonr(df_clean['VIXCLS'], df_clean['GDP'])
    

    结果:

      (-0.042291793273791969, 0.85176247509284908)
    

    为了扩展到更多变量,我给你一个丑陋的基于循环的方法:

    #Add a third field
    oil = pd.DataFrame(DataReader("DCOILWTICO", "fred", start=datetime(1990, 1, 1))) 
    df = df.merge(oil,left_index=True, right_index=True, how='left')
    
    #construct two arrays, one of the correlation and the other of the p-vals
    rho = df.corr()
    pval = np.zeros([df.shape[1],df.shape[1]])
    for i in range(df.shape[1]): # rows are the number of rows in the matrix.
        for j in range(df.shape[1]):
            JonI        = pd.ols(y=df.icol(i), x=df.icol(j), intercept=True)
            pval[i,j]  = JonI.f_stat['p-value']
    

    rho 的结果:

                 GDP    VIXCLS  DCOILWTICO
     GDP         1.000000 -0.042292    0.870251
     VIXCLS     -0.042292  1.000000   -0.004612
     DCOILWTICO  0.870251 -0.004612    1.000000
    

    pval 的结果:

     [[  0.00000000e+00   8.51762475e-01   1.11022302e-16]
      [  8.51762475e-01   0.00000000e+00   9.83747425e-01]
      [  1.11022302e-16   9.83747425e-01   0.00000000e+00]]
    

    【讨论】:

    • 如果有超过 2 列怎么办,有没有办法获得一个很好的相关输出表?
    • df.corr() 将为您提供整个数据框的相关结构,但使用 p 值的回归计算方法会很麻烦。
    • pd.ols 在 v 0.20.0 中已弃用,DataReader 已移至 pandas-datareader: github.com/pydata/pandas-datareader
    【解决方案3】:

    我尝试在函数中求和逻辑,它可能不是最有效的方法,但会为您提供与 pandas df.corr() 类似的输出。要使用它,只需将以下函数放入您的代码中并提供您的数据框对象即调用它。 corr_pvalue(your_dataframe).

    我已将值四舍五入到小数点后 4 位,如果您想要不同的输出,请更改舍入函数中的值。

    from scipy.stats import pearsonr
    import numpy as np
    import pandas as pd
    
    def corr_pvalue(df):
    
    
        numeric_df = df.dropna()._get_numeric_data()
        cols = numeric_df.columns
        mat = numeric_df.values
    
        arr = np.zeros((len(cols),len(cols)), dtype=object)
    
        for xi, x in enumerate(mat.T):
            for yi, y in enumerate(mat.T[xi:]):
                arr[xi, yi+xi] = map(lambda _: round(_,4), pearsonr(x,y))
                arr[yi+xi, xi] = arr[xi, yi+xi]
    
        return pd.DataFrame(arr, index=cols, columns=cols)
    

    我已经用 pandas v0.18.1 测试过了

    【讨论】:

      【解决方案4】:

      要一次计算所有 p 值,您可以使用 calculate_pvalues 函数(代码如下):

      df = pd.DataFrame({'A':[1,2,3], 'B':[2,5,3], 'C':[5,2,1], 'D':['text',2,3] })
      calculate_pvalues(df) 
      

      输出类似于corr()(但带有 p 值):

                  A       B       C
          A       0  0.7877  0.1789
          B  0.7877       0  0.6088
          C  0.1789  0.6088       0
      

      详情:

      • D 列会被自动忽略,因为它包含文本。
      • p 值四舍五入到小数点后 4 位
      • 您可以设置子集以指示确切的列:calculate_pvalues(df[['A','B','C']]

      以下是函数代码

      from scipy.stats import pearsonr
      import pandas as pd
      
      def calculate_pvalues(df):
          df = df.dropna()._get_numeric_data()
          dfcols = pd.DataFrame(columns=df.columns)
          pvalues = dfcols.transpose().join(dfcols, how='outer')
          for r in df.columns:
              for c in df.columns:
                  pvalues[r][c] = round(pearsonr(df[r], df[c])[1], 4)
          return pvalues
      

      【讨论】:

        【解决方案5】:

        星号表示的统计意义:

        from scipy.stats import pearsonr
        import numpy as np
        rho = df.corr()
        pval = df.corr(method=lambda x, y: pearsonr(x, y)[1]) - np.eye(*rho.shape)
        p = pval.applymap(lambda x: ''.join(['*' for t in [0.01,0.05,0.1] if x<=t]))
        rho.round(2).astype(str) + p
        

        【讨论】:

        • 这非常适合实施。有没有可能将它制作成带有 np.triu 作为掩码的 sns.heatmap?
        【解决方案6】:

        oztalha 编写的代码非常有用。我只是在 r 不重要的地方更改了格式(四舍五入为 2 位)。

            rho = data.corr()
            pval = calculate_pvalues(data) # toto_tico's answer
            # create three masks
            r1 = rho.applymap(lambda x: '{:.2f}*'.format(x))
            r2 = rho.applymap(lambda x: '{:.2f}**'.format(x))
            r3 = rho.applymap(lambda x: '{:.2f}***'.format(x))
            r4 = rho.applymap(lambda x: '{:.2f}'.format(x))
            # apply them where appropriate --this could be a single liner
            rho = rho.mask(pval>0.1,r4)
            rho = rho.mask(pval<=0.1,r1)
            rho = rho.mask(pval<=0.05,r2)
            rho = rho.mask(pval<=0.01,r3)
            rho
        

        【讨论】:

        • 一般来说,如果答案包含对代码的用途的解释,以及为什么在不介绍其他人的情况下解决问题的原因,答案会更有帮助。
        【解决方案7】:

        来自@toto_tico 和@Somendra-joshi 的精彩回答。 但是,它会丢弃不必要的 NAs 值。在这个 sn-p 中,我只是删除了属于当前正在计算的相关性的 NA。在实际的corr implementation 中,他们也是这样做的。

        def calculate_pvalues(df):
            df = df._get_numeric_data()
            dfcols = pd.DataFrame(columns=df.columns)
            pvalues = dfcols.transpose().join(dfcols, how='outer')
            for r in df.columns:
                for c in df.columns:
                    if c == r:
                        df_corr = df[[r]].dropna()
                    else:
                        df_corr = df[[r,c]].dropna()
                    pvalues[r][c] = pearsonr(df_corr[r], df_corr[c])[1]
            return pvalues
        

        【讨论】:

          【解决方案8】:

          在 pandas v0.24.0 中,method 参数被添加到 corr。现在,您可以这样做:

          import pandas as pd
          import numpy as np
          from scipy.stats import pearsonr
          
          df = pd.DataFrame({'A':[1,2,3], 'B':[2,5,3], 'C':[5,2,1]})
          
          df.corr(method=lambda x, y: pearsonr(x, y)[1]) - np.eye(len(df.columns)) 
          
                    A         B         C
          A  0.000000  0.787704  0.178912
          B  0.787704  0.000000  0.608792
          C  0.178912  0.608792  0.000000
          

          请注意需要使用np.eye(len(df.columns)) 的解决方法,因为自相关始终设置为1.0(请参阅https://github.com/pandas-dev/pandas/issues/25726)。

          【讨论】:

            【解决方案9】:

            在一行代码中使用列表推导:

            >>> import pandas as pd
            >>> from scipy.stats import pearsonr
            >>> data = {'y':[0, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6],
            ...         'x1':[0, 4, 2, 6, 2, 8, 6, 10, 4, 13, 5],
            ...         'x2':[0.0, 1.3, 0.2, 4.5, 1.3, 1.6, 3.5, 1.7, 1.6, 3.7, 1.7]}
            >>> df = pd.DataFrame(data)
            >>> pvals = pd.DataFrame([[pearsonr(df[c], df[y])[1] for y in df.columns] for c in df.columns],
            ...                      columns=df.columns, index=df.columns)
            >>> pvals
                       y        x1        x2
            y   0.000000  0.000732  0.069996
            x1  0.000732  0.000000  0.036153
            x2  0.069996  0.036153  0.000000
            >>> 
            

            我对一种可以将上述数据框与这个数据框结合起来的巧妙技术感兴趣:

            >>> df.corr()
                       y        x1        x2
            y   1.000000  0.857786  0.565208
            x1  0.857786  1.000000  0.634093
            x2  0.565208  0.634093  1.000000
            

            期望的输出:

                          y        x1        x2
            y  c    1.000000  0.857786  0.565208
               p    (0.0000)  (0.0007)  (0.0699)
            x1 c    0.857786  1.000000  0.634093
               p    (0.0007)  (0.0000)  (0.0361)
            x2 c    0.565208  0.634093  1.000000
               p    (0.0699)  (0.0361)  (0.0000)
            

            其中有一个多索引,c 行是相关系数,而p 行提供 pvalue。

            有人想吗?

            【讨论】:

            • 不完全光滑,但这可以工作并获得所需的输出,p = pd.DataFrame([[pearsonr(df[c], df[y])[1] for y in df.columns ] for c in df.columns], columns=df.columns, index=df.columns).copy() p["type"] = "p" p.index.name="col" p = p.set_index( [p.index,"type"]) c = df.corr() c["type"] = "c" c.index.name = "col" c = c.set_index([c.index,"type" ]) c.combine_first(p)
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-04-26
            • 2015-11-20
            • 2015-05-31
            相关资源
            最近更新 更多