Importância do recurso com variáveis ​​fictícias

17

Estou tentando entender como posso obter a importância do recurso de uma variável categórica que foi dividida em variáveis ​​dummy. Estou usando o scikit-learn, que não lida com variáveis ​​categóricas para você, como R ou H2O.

Se eu dividir uma variável categórica em variáveis ​​dummy, obtenho importâncias de recurso separadas por classe nessa variável.

Minha pergunta é: faz sentido recombinar essas importâncias variáveis ​​variáveis ​​em um valor de importância para uma variável categórica simplesmente somando-as?

Na página 368 de Os elementos da aprendizagem estatística:

A importância relativa ao quadrado da variável é a soma dessas melhorias ao quadrado em todos os nós internos para os quais foi escolhida como variável de divisãoX

Isso me faz pensar que, como o valor de importância já é criado pela soma de uma métrica em cada nó, a variável é selecionada, eu devo poder combinar os valores de importância da variável das variáveis ​​fictícias para "recuperar" a importância da variável categórica. É claro que não espero que seja exatamente correto, mas esses valores são realmente exatos, pois são encontrados através de um processo aleatório.

Eu escrevi o seguinte código python (no jupyter) como uma investigação:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestClassifier
import re

#%matplotlib inline
from IPython.display import HTML
from IPython.display import set_matplotlib_formats

plt.rcParams['figure.autolayout'] = False
plt.rcParams['figure.figsize'] = 10, 6
plt.rcParams['axes.labelsize'] = 18
plt.rcParams['axes.titlesize'] = 20
plt.rcParams['font.size'] = 14
plt.rcParams['lines.linewidth'] = 2.0
plt.rcParams['lines.markersize'] = 8
plt.rcParams['legend.fontsize'] = 14

# Get some data, I could not easily find a free data set with actual categorical variables, so I just created some from continuous variables
data = load_diabetes()
df = pd.DataFrame(data.data, columns=[data.feature_names])
df = df.assign(target=pd.Series(data.target))

# Functions to plot the variable importances
def autolabel(rects, ax):
    """
    Attach a text label above each bar displaying its height
    """
    for rect in rects:
        height = rect.get_height()
        ax.text(rect.get_x() + rect.get_width()/2.,
                1.05*height,
                f'{round(height,3)}',
                ha='center',
                va='bottom')

def plot_feature_importance(X,y,dummy_prefixes=None, ax=None, feats_to_highlight=None):

    # Find the feature importances by fitting a random forest
    forest = RandomForestClassifier(n_estimators=100)
    forest.fit(X,y)
    importances_dummy = forest.feature_importances_

    # If there are specified dummy variables, combing them into a single categorical 
    # variable by summing the importances. This code assumes the dummy variables were
    # created using pandas get_dummies() method names the dummy variables as
    # featurename_categoryvalue
    if dummy_prefixes is None:
        importances_categorical = importances_dummy
        labels = X.columns
    else:
        dummy_idx = np.repeat(False,len(X.columns))
        importances_categorical = []
        labels = []

        for feat in dummy_prefixes:
            feat_idx = np.array([re.match(f'^{feat}_', col) is not None for col in X.columns])
            importances_categorical = np.append(importances_categorical,
                                                sum(importances_dummy[feat_idx]))
            labels = np.append(labels,feat)
            dummy_idx = dummy_idx | feat_idx
        importances_categorical = np.concatenate((importances_dummy[~dummy_idx],
                                                  importances_categorical))
        labels = np.concatenate((X.columns[~dummy_idx], labels))

    importances_categorical /= max(importances_categorical)
    indices = np.argsort(importances_categorical)[::-1]

    # Plotting

    if ax is None:
        fig, ax = plt.subplots()

    plt.title("Feature importances")
    rects = ax.bar(range(len(importances_categorical)),
                   importances_categorical[indices],
                   tick_label=labels[indices],
                   align="center")
    autolabel(rects, ax)

    if feats_to_highlight is not None:
        highlight = [feat in feats_to_highlight for feat in labels[indices]]
        rects2 = ax.bar(range(len(importances_categorical)),
                       importances_categorical[indices]*highlight,
                       tick_label=labels[indices],
                       color='r',
                       align="center")
        rects = [rects,rects2]
    plt.xlim([-0.6, len(importances_categorical)-0.4])
    ax.set_ylim((0, 1.125))
    return rects

# Create importance plots leaving everything as categorical variables. I'm highlighting bmi and age as I will convert those into categorical variables later
X = df.drop('target',axis=1)
y = df['target'] > 140.5

plot_feature_importance(X,y, feats_to_highlight=['bmi', 'age'])
plt.title('Feature importance with bmi and age left as continuous variables')

#Create an animation of what happens to variable importance when I split bmi and age into n (n equals 2 - 25) different classes
# %%capture

fig, ax = plt.subplots()

def animate(i):
    ax.clear()

    # Split one of the continuous variables up into a categorical variable with i balanced classes
    X_test = X.copy()
    n_categories = i+2
    X_test['bmi'] = pd.cut(X_test['bmi'],
                           np.percentile(X['bmi'], np.linspace(0,100,n_categories+1)),
                           labels=[chr(num+65) for num in range(n_categories)])
    X_test['age'] = pd.cut(X_test['age'],
                           np.percentile(X['age'], np.linspace(0,100,n_categories+1)),
                           labels=[chr(num+65) for num in range(n_categories)])
    X_test = pd.get_dummies(X_test, drop_first=True)

    # Plot the feature importances
    rects = plot_feature_importance(X_test,y,dummy_prefixes=['bmi', 'age'],ax=ax, feats_to_highlight=['bmi', 'age'])
    plt.title(f'Feature importances for {n_categories} bmi and age categories')
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.spines['bottom'].set_visible(False)
    ax.spines['left'].set_visible(False)

    return [rects,]

anim = animation.FuncAnimation(fig, animate, frames=24, interval=1000)

HTML(anim.to_html5_video())

Aqui estão alguns dos resultados:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Podemos observar que a variável importância depende principalmente do número de categorias, o que me leva a questionar a utilidade desses gráficos em geral. Especialmente a importância de age atingir valores muito mais altos do que sua contraparte contínua.

E, finalmente, um exemplo, se eu os deixar como variáveis ​​fictícias (apenas bmi):

# Split one of the continuous variables up into a categorical variable with i balanced classes
X_test = X.copy()
n_categories = 5
X_test['bmi'] = pd.cut(X_test['bmi'],
                       np.percentile(X['bmi'], np.linspace(0,100,n_categories+1)),
                       labels=[chr(num+65) for num in range(n_categories)])
X_test = pd.get_dummies(X_test, drop_first=True)

# Plot the feature importances
rects = plot_feature_importance(X_test,y, feats_to_highlight=['bmi_B','bmi_C','bmi_D', 'bmi_E'])
plt.title(f"Feature importances for {n_categories} bmi categories")

insira a descrição da imagem aqui

Dan
fonte

Respostas:

8

Ao trabalhar na "importância dos recursos", geralmente é útil lembrar que, na maioria dos casos, uma abordagem de regularização costuma ser uma boa alternativa. Ele "selecionará automaticamente os recursos mais importantes" para o problema em questão. Agora, se não queremos seguir a noção de regularização (geralmente dentro do contexto de regressão), classificadores florestais aleatórios e a noção de testes de permutação naturalmente fornecem uma solução para destacar a importância do grupo de variáveis. Isso já foi perguntado antes aqui: " Importância relativa de um conjunto de preditores em uma classificação de florestas aleatórias em R " alguns anos atrás. Abordagens mais rigorosas, como Gregorutti et al.: " Agruparam importância variável com florestas aleatórias e aplicação à análise de dados funcionais multivariada". A seleção de grupos úteis de recursos de Chakraborty & Pal em uma estrutura conexionista analisa essa tarefa no contexto de um Perceptron de várias camadas. Voltando ao artigo de Gregorutti et al., Sua metodologia é diretamente aplicável a qualquer tipo de algoritmo de classificação / regressão Em resumo, usamos uma versão permutada aleatoriamente em cada amostra de malas usada durante o treinamento.

Tendo afirmado o exposto acima, embora os testes de permutação sejam, em última análise, uma heurística, o que foi resolvido com precisão no passado é a penalização de variáveis ​​dummy no contexto da regressão regular. A resposta a essa pergunta é Group-LASSO , Group-LARS e Group-Garotte . Os documentos seminais desse trabalho são Yuan e Lin: " Seleção e estimativa de modelos em regressão com variáveis ​​agrupadas " (2006) e Meier et al.: " Laço de grupo para regressão logística " (2008). Essa metodologia nos permite trabalhar em situações em que: " cada fator pode ter vários níveis e pode ser expresso por meio de um grupo de variáveis ​​dummy " (Y&L 2006). O efeito é tal que "o laço do grupo incentiva a esparsidade no nível do fator. "(Y&L 2006). Sem ir a detalhes excessivos, a idéia básica é que a penalidade padrão seja substituída pela norma de matrizes definidas positivas , onde é o número de grupos que examinamos O CV tem alguns tópicos interessantes sobre o Group-Lasso aqui , aqui e aqui, se você quiser prosseguir com isso. [Como mencionamos o Python especificamente: Eu não usei o pacote do Python, mas parece incluir o laço agrupado regularização .]eu1Kjj={1,...,J}Jpyglmnet

Em suma, não faz sentido simplesmente "somar" a importância das variáveis ​​das variáveis ​​fictícias individuais, porque isso não capturaria a associação entre elas nem levaria a resultados potencialmente sem sentido. Dito isto, ambos os métodos penalizados por grupos e os métodos de importância variável da permutação fornecem uma estrutura coerente e (especialmente no caso de procedimentos de importância da permutação) geralmente aplicável para isso.

Finalmente, para afirmar o óbvio: não armazene dados contínuos . É uma prática ruim, há um excelente tópico sobre esse assunto aqui (e aqui ). O fato de observarmos resultados espúrios após a discretização de variáveis ​​contínuas, como age, não é surpreendente. Frank Harrell também escreveu extensivamente sobre problemas causados ​​pela categorização de variáveis ​​contínuas .

usεr11852 diz Reinstate Monic
fonte
Você vincula A importância relativa de um conjunto de preditores em uma classificação de florestas aleatórias em R responde diretamente à pergunta. Ficaria feliz em aceitar se você mover a referência a esse link para o início, pois não acho que o resto seja diretamente relevante e o link possa se perder facilmente na resposta.
Dan
Sem problemas. Fiz algumas edições relevantes. Não descarte o conceito de regressão regularizada, como menciono no texto, as abordagens de regularização oferecem uma alternativa perfeitamente válida para destacar a importância / classificação.
usεr11852 diz Reinstate Monic
A regressão regularizada não é uma resposta a esta pergunta; pode responder a uma pergunta diferente, isto é, alternativas à importância das feições, mas essa questão é sobre a agregação das feições em uma única característica categórica dentro de um gráfico de importância das feições. Eu realmente acho que você deve mover o link que realmente responde à pergunta desde o início.
Dan
2

A questão é:

faz sentido recombinar essas importâncias variáveis ​​variáveis ​​em um valor de importância para uma variável categórica simplesmente somando-as?

A resposta curta:

A resposta simples é não. De acordo com o manual (página 368), a importância de e assim Em conclusão, você deve pegar a raiz quadrada primeiro.

Eumportumance(Xeu)=Eu
(Eu)2=t=1J-1Eu2Eu(v(t)=)
Eu=t=1J-1Eu2Eu(v(t)=)

A resposta mais longa e prática ..

Você não pode simplesmente somar valores de importância de variáveis ​​individuais para variáveis ​​fictícias porque corre o risco de

o mascaramento de variáveis ​​importantes por outras pessoas com as quais elas são altamente correlacionadas. (página 368)

Questões como possível multicolinearidade podem distorcer os valores e classificações de importância variável.

Na verdade, é um problema muito interessante entender como a importância variável é afetada por questões como a multicolinearidade. O artigo Determinando a Importância do Preditor em Regressão Múltipla sob Condições Correlacionais e Distribuicionais Variadas discute vários métodos para calcular a importância das variáveis ​​e compara o desempenho dos dados que violam as suposições estatísticas típicas. Os autores descobriram que

Embora a multicolinearidade tenha afetado o desempenho de métodos de importância relativa, a não normalidade multivariada não. (WHITTAKER p366)

ecedavis
fonte
Não acho que sua segunda cotação seja relevante. Essas não são variáveis ​​altamente correlacionadas, são a mesma variável e uma boa implementação de uma árvore de decisão não exigiria OHE, mas as trataria como uma única variável. Na verdade, a multicolinearidade é artificialmente introduzida pelo OHE.
Dan
Em relação ao seu primeiro ponto, me parece que o número de importância relativa proposto por Breiman é o valor ao quadrado. Portanto, não estou convencido de que o sklearn tenha raízes quadradas primeiro, como você sugeriu. Além disso, se eles não deveriam, então, primeiro quadrar os valores, adicioná-los e depois enraizar a soma ao quadrado? Não sei se entendi sua sugestão de pegar a raiz quadrada primeiro.
Dan
@ecedavis O que você quer dizer com livro didático? Você pode fornecer um link ou uma citação mais completa, por favor.
see24
Olá, obrigado pelas críticas e pelo meu primeiro voto como novo membro. Seus comentários apontam detalhes específicos que abordarei em minha revisão, mas também posso ter sua opinião sobre a qualidade geral da minha resposta? Este é o meu primeiro post e pretendo me tornar um colaborador regular. No mínimo, espero que minha resposta seja geralmente útil e em bom estilo. Quais são seus pensamentos?
31518 ecedavis
O estilo da sua resposta é bom, mas algumas informações e conteúdo não parecem completamente corretos. O artigo ao qual você vincula é sobre a importância do preditor na regressão múltipla, enquanto a pergunta é sobre a importância na floresta aleatória. Também acho sua extração da citação problemática, pois a frase completa é "Além disso, devido ao encolhimento (Seção 10.12.1), o mascaramento de variáveis ​​importantes por outras pessoas com as quais eles estão altamente correlacionados é muito menos problemático". que tem um significado muito diferente.
precisa