Comment trouver les principales corrélations dans une matrice de corrélation avec Pandas ? Il existe de nombreuses réponses sur la façon de le faire avec R ( Montrer les corrélations sous la forme d'une liste ordonnée, et non d'une grande matrice. ou Un moyen efficace d'obtenir des paires hautement corrélées à partir d'un grand ensemble de données en Python ou R. ), mais je me demande comment le faire avec des pandas ? Dans mon cas, la matrice est de 4460x4460, donc je ne peux pas le faire visuellement.
Réponses
Trop de publicités?Vous pouvez utiliser DataFrame.values
pour obtenir un tableau numpy des données et ensuite utiliser les fonctions NumPy telles que argsort()
pour obtenir les paires les plus corrélées.
Mais si vous voulez faire cela dans pandas, vous pouvez unstack
et trier le DataFrame :
import pandas as pd
import numpy as np
shape = (50, 4460)
data = np.random.normal(size=shape)
data[:, 1000] += data[:, 2000]
df = pd.DataFrame(data)
c = df.corr().abs()
s = c.unstack()
so = s.sort_values(kind="quicksort")
print so[-4470:-4460]
Voici le résultat :
2192 1522 0.636198
1522 2192 0.636198
3677 2027 0.641817
2027 3677 0.641817
242 130 0.646760
130 242 0.646760
1171 2733 0.670048
2733 1171 0.670048
1000 2000 0.742340
2000 1000 0.742340
dtype: float64
La réponse de @HYRY est parfaite. Je m'appuie juste sur cette réponse en ajoutant un peu plus de logique pour éviter les doublons et les auto-corrélations et un tri approprié :
import pandas as pd
d = {'x1': [1, 4, 4, 5, 6],
'x2': [0, 0, 8, 2, 4],
'x3': [2, 8, 8, 10, 12],
'x4': [-1, -4, -4, -4, -5]}
df = pd.DataFrame(data = d)
print("Data Frame")
print(df)
print()
print("Correlation Matrix")
print(df.corr())
print()
def get_redundant_pairs(df):
'''Get diagonal and lower triangular pairs of correlation matrix'''
pairs_to_drop = set()
cols = df.columns
for i in range(0, df.shape[1]):
for j in range(0, i+1):
pairs_to_drop.add((cols[i], cols[j]))
return pairs_to_drop
def get_top_abs_correlations(df, n=5):
au_corr = df.corr().abs().unstack()
labels_to_drop = get_redundant_pairs(df)
au_corr = au_corr.drop(labels=labels_to_drop).sort_values(ascending=False)
return au_corr[0:n]
print("Top Absolute Correlations")
print(get_top_abs_correlations(df, 3))
Cela donne le résultat suivant :
Data Frame
x1 x2 x3 x4
0 1 0 2 -1
1 4 0 8 -4
2 4 8 8 -4
3 5 2 10 -4
4 6 4 12 -5
Correlation Matrix
x1 x2 x3 x4
x1 1.000000 0.399298 1.000000 -0.969248
x2 0.399298 1.000000 0.399298 -0.472866
x3 1.000000 0.399298 1.000000 -0.969248
x4 -0.969248 -0.472866 -0.969248 1.000000
Top Absolute Correlations
x1 x3 1.000000
x3 x4 0.969248
x1 x4 0.969248
dtype: float64
Solution en quelques lignes sans paires redondantes de variables :
corr_matrix = df.corr().abs()
#the matrix is symmetric so we need to extract upper triangle matrix without diagonal (k = 1)
sol = (corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(np.bool))
.stack()
.sort_values(ascending=False))
#first element of sol series is the pair with the biggest correlation
Vous pouvez ensuite itérer à travers les paires de noms de variables (qui sont des multi-index pandas.Series) et leurs valeurs comme ceci :
for index, value in sol.items():
# do some staff
En combinant certaines fonctionnalités des réponses de @HYRY et @arun, vous pouvez imprimer les corrélations les plus importantes pour le cadre de données. df
dans une seule ligne en utilisant :
df.corr().unstack().sort_values().drop_duplicates()
Note : le seul inconvénient est que si vous avez des corrélations de 1.0 qui sont no une variable à elle-même, le drop_duplicates()
l'addition les supprimerait
J'ai préféré le post d'Addison Klinke, car il est le plus simple, mais j'ai utilisé la suggestion de Wojciech Moszczynsk pour le filtrage et la représentation graphique, mais j'ai étendu le filtre pour éviter les valeurs absolues, donc, étant donné une grande matrice de corrélation, il faut la filtrer, la représenter graphiquement, puis l'aplatir :
Créé, filtré et classé
dfCorr = df.corr()
filteredDf = dfCorr[((dfCorr >= .5) | (dfCorr <= -.5)) & (dfCorr !=1.000)]
plt.figure(figsize=(30,10))
sn.heatmap(filteredDf, annot=True, cmap="Reds")
plt.show()
Fonction
Au final, j'ai créé une petite fonction pour créer la matrice de corrélation, la filtrer, puis l'aplatir. En tant qu'idée, elle pourrait facilement être étendue, par exemple, des limites supérieures et inférieures asymétriques, etc.
def corrFilter(x: pd.DataFrame, bound: float):
xCorr = x.corr()
xFiltered = xCorr[((xCorr >= bound) | (xCorr <= -bound)) & (xCorr !=1.000)]
xFlattened = xFiltered.unstack().sort_values().drop_duplicates()
return xFlattened
corrFilter(df, .7)
Suivi
Finalement, j'ai affiné les fonctions
# Returns correlation matrix
def corrFilter(x: pd.DataFrame, bound: float):
xCorr = x.corr()
xFiltered = xCorr[((xCorr >= bound) | (xCorr <= -bound)) & (xCorr !=1.000)]
return xFiltered
# flattens correlation matrix with bounds
def corrFilterFlattened(x: pd.DataFrame, bound: float):
xFiltered = corrFilter(x, bound)
xFlattened = xFiltered.unstack().sort_values().drop_duplicates()
return xFlattened
# Returns correlation for a variable from flattened correlation matrix
def filterForLabels(df: pd.DataFrame, label):
try:
sideLeft = df[label,]
except:
sideLeft = pd.DataFrame()
try:
sideRight = df[:,label]
except:
sideRight = pd.DataFrame()
if sideLeft.empty and sideRight.empty:
return pd.DataFrame()
elif sideLeft.empty:
concat = sideRight.to_frame()
concat.rename(columns={0:'Corr'},inplace=True)
return concat
elif sideRight.empty:
concat = sideLeft.to_frame()
concat.rename(columns={0:'Corr'},inplace=True)
return concat
else:
concat = pd.concat([sideLeft,sideRight], axis=1)
concat["Corr"] = concat[0].fillna(0) + concat[1].fillna(0)
concat.drop(columns=[0,1], inplace=True)
return concat
- Réponses précédentes
- Plus de réponses