web-dev-qa-db-fra.com

corrélation des colonnes de pandas avec la signification statistique

Quel est le meilleur moyen, étant donné une base de données de pandas, df, d’obtenir la corrélation entre ses colonnes df.1 et df.2

Je ne veux pas que la sortie compte les lignes avec NaN, ce que fait la corrélation intégrée pandas. Mais je veux aussi qu’il produise une pvalue ou une erreur standard, ce que l’intégré n’a pas.

SciPy semble être rattrapé par les NaN, bien que je pense que cela indique une signification.

Exemple de données:

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

Réponse fournie par @Shashank is Nice. Cependant, si vous voulez une solution en pure pandas, vous pouvez aimer ceci:

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'])

Résultats:

-0.0422917932738 0.851762475093

Mêmes résultats que la fonction stats:

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

Résultats:

  (-0.042291793273791969, 0.85176247509284908)

Pour étendre à plus de vairables je vous donne une approche laide à base de boucle:

#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']

Résultats de 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

Résultats de 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]]
9
BKay

Vous pouvez utiliser les fonctions de corrélation scipy.stats pour obtenir la valeur p.

Par exemple, si vous recherchez une corrélation telle que la corrélation pearson, vous pouvez utiliser la fonction pearsonr .

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

Donne la sortie

(0.7205766921228921, 0.48775429164459994)

Où la première valeur du tuple est la valeur de corrélation, et la seconde est la valeur p.

Dans votre cas, vous pouvez utiliser la fonction dropna de pandas pour supprimer les valeurs NaN en premier.

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

Pour calculer toutes les valeurs p à la fois , vous pouvez utiliser la fonction calculate_pvalues ci-dessous:

df = pd.DataFrame({'A':[1,2,3], 'B':[2,5,3], 'C':[5,2,1], 'D':['text',2,3] })
calculate_pvalues(df)
  • La sortie est semblable à la corr()(mais avec des valeurs p):

            A       B       C
    A       0  0.7877  0.1789
    B  0.7877       0  0.6088
    C  0.1789  0.6088       0
    
  • les valeurs p sont arrondies à 4 décimales

  • La colonne D est ignorée car elle contient du texte.

Voici le code de la fonction :

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
8
toto_tico
rho = df.corr()
rho = rho.round(2)
pval = calculate_pvalues(df) # toto_tico's answer
# create three masks
r1 = rho.applymap(lambda x: '{}*'.format(x))
r2 = rho.applymap(lambda x: '{}**'.format(x))
r3 = rho.applymap(lambda x: '{}***'.format(x))
# apply them where appropriate
rho = rho.mask(pval<=0.1,r1)
rho = rho.mask(pval<=0.05,r2)
rho = rho.mask(pval<=0.01,r3)
rho
# note I prefer readability over the conciseness of code, 
# instead of six lines it could have been a single liner like this:
# [rho.mask(pval<=p,rho.applymap(lambda x: '{}*'.format(x)),inplace=True) for p in [.1,.05,.01]]

 Correlations with asterisks

7
tozCSS

J'ai essayé de résumer la logique d'une fonction. Ce n'est peut-être pas l'approche la plus efficace, mais je vais vous fournir un résultat similaire à celui de pandas df.corr (). Pour utiliser ceci, insérez simplement la fonction suivante dans votre code et appelez-la en fournissant votre objet dataframe c'est-à-dire. corr_pvalue (your_dataframe) .

J'ai arrondi les valeurs à 4 décimales, au cas où vous voudriez une sortie différente, veuillez changer la valeur en fonction round.

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)

Je l'ai testé avec des pandas v0.18.1

1
Somendra Joshi

Excellentes réponses de @toto_tico et @ Somendra-joshi . Cependant, il supprime les valeurs inutiles de NA. Dans cet extrait, je supprime simplement les NA qui appartiennent à la corrélation en cours d’informatique. Dans l'implémentation réelle de corr , ils font de même.

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
0
Matheus Araujo

C'était un code très utile de oztalha . Je viens de changer le formatage (arrondi à 2 chiffres) là où r n’était pas significatif.

    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
0
user2730303