136 votes

Lister les paires de corrélation les plus élevées d'une grande matrice de corrélation dans Pandas ?

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.

120voto

HYRY Points 26340

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

66voto

arun Points 2635

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

57voto

MiFi Points 689

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

27voto

Addison Klinke Points 137

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

17voto

James Igoe Points 43

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()

filtered heat map

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)

enter image description here

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X