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