Listar pares de correlação mais altos de uma grande matriz de correlação em pandas?

96

Como você encontra as principais correlações em uma matriz de correlação com Pandas? Há muitas respostas sobre como fazer isso com R ( Mostrar correlações como uma lista ordenada, não como uma grande matriz ou maneira eficiente de obter pares altamente correlacionados de grandes conjuntos de dados em Python ou R ), mas estou me perguntando como fazer isso com pandas? No meu caso, a matriz é 4460x4460, então não posso fazer isso visualmente.

Kyle Brandt
fonte

Respostas:

92

Você pode usar DataFrame.valuespara obter uma matriz numpy dos dados e, em seguida, usar funções NumPy, como argsort()obter os pares mais correlacionados.

Mas se você quiser fazer isso nos pandas, pode unstackclassificar o 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]

Aqui está o resultado:

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
HYRY
fonte
10
Com o Pandas v 0.17.0 e superior, você deve usar sort_values ​​em vez de order. Você obterá um erro se tentar usar o método de pedido.
Friendm1
46

A resposta de @HYRY é perfeita. Apenas desenvolvendo essa resposta adicionando um pouco mais de lógica para evitar duplicatas e autocorrelações e classificação adequada:

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

Isso dá a seguinte saída:

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
uma corrida
fonte
3
em vez de get_redundant_pairs (df), você pode usar "cor.loc [:,:] = np.tril (cor.values, k = -1)" e então "cor = cor [cor> 0]"
Sarah
2
Estou recebendo um erro de linha au_corr = au_corr.drop(labels=labels_to_drop).sort_values(ascending=False):# -- partial selection or non-unique index
stallingOne
40

Solução de poucas linhas sem pares redundantes de variáveis:

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

Em seguida, você pode iterar por meio de nomes de pares de variáveis ​​(que são pandas.Series multi-indexes) e seus valores como este:

for index, value in sol.items():
  # do some staff
MiFi
fonte
3
provavelmente uma má ideia usar oscomo um nome de variável porque mascara o osde import osse disponível no código
shadi
Obrigado por sua sugestão, eu mudei este nome var impróprio.
MiFi
1
a partir de 2018, use sort_values ​​(ascending = False) em vez de order
Serafins
1
como fazer um loop 'sol' ??
sirjay 01 de
2
@sirjay Respondi à sua pergunta acima
MiFi
13

Combinando alguns recursos das respostas de @HYRY e @arun, você pode imprimir as principais correlações para dataframe dfem uma única linha usando:

df.corr().unstack().sort_values().drop_duplicates()

Nota: a única desvantagem é se você tiver 1.0 correlações que não são uma variável para si mesmo, a drop_duplicates()adição as removeria

Addison Klinke
fonte
1
Não drop_duplicatesdescartaria todas as correlações que são iguais?
shadi
@shadi sim, você está correto. No entanto, assumimos que as únicas correlações que serão idênticas são correlações de 1,0 (ou seja, uma variável consigo mesma). As chances são de que a correlação para dois pares únicos de variáveis ​​(ou seja, v1para v2e v3para v4) não seria exatamente a mesma
Addison Klinke
Definitivamente meu favoirite, a própria simplicidade. no meu uso, eu primeiro filtrei para corrleations altas
James Igoe
9

Use o código abaixo para visualizar as correlações em ordem decrescente.

# See the correlations in descending order

corr = df.corr() # df is the pandas dataframe
c1 = corr.abs().unstack()
c1.sort_values(ascending = False)
prasantha
fonte
1
Sua 2ª linha deve ser: c1 = core.abs (). Unstack ()
Jack Fleeting
ou primeira linhacorr = df.corr()
vizyourdata
4

Você pode fazer graficamente de acordo com este código simples, substituindo seus dados.

corr = df.corr()

kot = corr[corr>=.9]
plt.figure(figsize=(12,8))
sns.heatmap(kot, cmap="Greens")

insira a descrição da imagem aqui

Wojciech Moszczyński
fonte
2

Muitas respostas boas aqui. A maneira mais fácil que encontrei foi uma combinação de algumas das respostas acima.

corr = corr.where(np.triu(np.ones(corr.shape), k=1).astype(np.bool))
corr = corr.unstack().transpose()\
    .sort_values(by='column', ascending=False)\
    .dropna()
Rich Wandell
fonte
2

Use itertools.combinationspara obter todas as correlações exclusivas da própria matriz de correlação do pandas .corr(), gerar uma lista de listas e alimentá-la de volta em um DataFrame para usar '.sort_values'. Defina ascending = Truepara exibir as correlações mais baixas no topo

corrankleva um DataFrame como argumento porque requer .corr().

  def corrank(X: pandas.DataFrame):
        import itertools
        df = pd.DataFrame([[(i,j),X.corr().loc[i,j]] for i,j in list(itertools.combinations(X.corr(), 2))],columns=['pairs','corr'])    
        print(df.sort_values(by='corr',ascending=False))

  corrank(X) # prints a descending list of correlation pair (Max on top)
Frederik Meinertsen
fonte
3
Embora este trecho de código possa ser a solução, incluir uma explicação realmente ajuda a melhorar a qualidade de sua postagem. Lembre-se de que você responderá à pergunta para leitores no futuro, e essas pessoas podem não saber os motivos de sua sugestão de código.
haindl
1

Eu não queria unstackcomplicar demais esse problema, já que só queria descartar alguns recursos altamente correlacionados como parte de uma fase de seleção de recursos.

Então, acabei com a seguinte solução simplificada:

# map features to their absolute correlation values
corr = features.corr().abs()

# set equality (self correlation) as zero
corr[corr == 1] = 0

# of each feature, find the max correlation
# and sort the resulting array in ascending order
corr_cols = corr.max().sort_values(ascending=False)

# display the highly correlated features
display(corr_cols[corr_cols > 0.8])

Nesse caso, se você quiser descartar os recursos correlacionados, poderá mapear a corr_colsmatriz filtrada e remover os de índice ímpar (ou mesmo).

falsarella
fonte
Isso dá apenas um índice (recurso) e não algo como recurso1 recurso2 0,98. Alterar linha corr_cols = corr.max().sort_values(ascending=False)para corr_cols = corr.unstack()
aunsid
Bem, o OP não especificou uma forma de correlação. Como mencionei, não queria desempilhar, então apenas trouxe uma abordagem diferente. Cada par de correlação é representado por 2 linhas, em meu código sugerido. Mas obrigado pelo comentário útil!
falsarella
0

Eu estava tentando algumas das soluções aqui, mas na verdade descobri a minha própria. Espero que isso possa ser útil para o próximo, então eu compartilho aqui:

def sort_correlation_matrix(correlation_matrix):
    cor = correlation_matrix.abs()
    top_col = cor[cor.columns[0]][1:]
    top_col = top_col.sort_values(ascending=False)
    ordered_columns = [cor.columns[0]] + top_col.index.tolist()
    return correlation_matrix[ordered_columns].reindex(ordered_columns)
KIC
fonte
0

Este é um código de melhoria de @MiFi. Este pedido em abs, mas não excluindo os valores negativos.

   def top_correlation (df,n):
    corr_matrix = df.corr()
    correlation = (corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(np.bool))
                 .stack()
                 .sort_values(ascending=False))
    correlation = pd.DataFrame(correlation).reset_index()
    correlation.columns=["Variable_1","Variable_2","Correlacion"]
    correlation = correlation.reindex(correlation.Correlacion.abs().sort_values(ascending=False).index).reset_index().drop(["index"],axis=1)
    return correlation.head(n)

top_correlation(ANYDATA,10)
Aibloy
fonte
0

A função a seguir deve resolver o problema. Esta implementação

  • Remove autocorrelações
  • Remove duplicatas
  • Permite a seleção dos N principais recursos correlacionados

e também é configurável para que você possa manter as autocorrelações e também as duplicatas. Você também pode relatar quantos pares de recursos desejar.


def get_feature_correlation(df, top_n=None, corr_method='spearman',
                            remove_duplicates=True, remove_self_correlations=True):
    """
    Compute the feature correlation and sort feature pairs based on their correlation

    :param df: The dataframe with the predictor variables
    :type df: pandas.core.frame.DataFrame
    :param top_n: Top N feature pairs to be reported (if None, all of the pairs will be returned)
    :param corr_method: Correlation compuation method
    :type corr_method: str
    :param remove_duplicates: Indicates whether duplicate features must be removed
    :type remove_duplicates: bool
    :param remove_self_correlations: Indicates whether self correlations will be removed
    :type remove_self_correlations: bool

    :return: pandas.core.frame.DataFrame
    """
    corr_matrix_abs = df.corr(method=corr_method).abs()
    corr_matrix_abs_us = corr_matrix_abs.unstack()
    sorted_correlated_features = corr_matrix_abs_us \
        .sort_values(kind="quicksort", ascending=False) \
        .reset_index()

    # Remove comparisons of the same feature
    if remove_self_correlations:
        sorted_correlated_features = sorted_correlated_features[
            (sorted_correlated_features.level_0 != sorted_correlated_features.level_1)
        ]

    # Remove duplicates
    if remove_duplicates:
        sorted_correlated_features = sorted_correlated_features.iloc[:-2:2]

    # Create meaningful names for the columns
    sorted_correlated_features.columns = ['Feature 1', 'Feature 2', 'Correlation (abs)']

    if top_n:
        return sorted_correlated_features[:top_n]

    return sorted_correlated_features

Giorgos Myrianthous
fonte
0

Eu gostei mais da postagem de Addison Klinke, por ser a mais simples, mas usei a sugestão de Wojciech Moszczyńsk para filtrar e mapear, mas estendeu o filtro para evitar valores absolutos, então, dada uma grande matriz de correlação, filtre, crie um gráfico e, em seguida, aplique-a:

Criado, filtrado e mapeado

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

mapa de calor filtrado

Função

No final, criei uma pequena função para criar a matriz de correlação, filtrá-la e depois nivelá-la. Como uma ideia, ele poderia ser facilmente estendido, por exemplo, limites superior e inferior assimétricos, 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)

insira a descrição da imagem aqui

James Igoe
fonte
como remover o último? HofstederPowerDx e Hofsteder PowerDx são as mesmas variáveis, certo?
Luc
pode-se usar .dropna () nas funções. Acabei de experimentar no VS Code e funcionou, onde uso a primeira equação para criar e filtrar a matriz de correlação e outra para achatá-la. Se você usar isso, poderá experimentar remover .dropduplicates () para ver se precisa de .dropna () e dropduplicates ().
James Igoe
Um bloco de notas que inclui este código e algumas outras melhorias está aqui: github.com/JamesIgoe/GoogleFitAnalysis
James Igoe