Ich versuche zu verstehen, wie ich die Feature-Wichtigkeit einer kategorialen Variablen ermitteln kann, die in Dummy-Variablen zerlegt wurde. Ich benutze scikit-learn, das kategoriale Variablen für Sie nicht so behandelt, wie es R oder H2O tun.
Wenn ich eine kategoriale Variable in Dummy-Variablen zerlege, erhalte ich separate Feature-Wichtigkeiten pro Klasse in dieser Variablen.
Meine Frage ist, ob es Sinn macht, diese Dummy-Variablen-Wichtigkeiten zu einem Wichtigkeitswert für eine kategoriale Variable zu rekombinieren, indem man sie einfach summiert.
Ab Seite 368 der Elemente des statistischen Lernens:
Die quadratische relative Wichtigkeit der Variablen ist die Summe solcher quadratischen Verbesserungen über alle internen Knoten, für die sie als Aufteilungsvariable ausgewählt wurde
Da der Wichtigkeitswert bereits durch Summieren einer Metrik an jedem Knoten erstellt wurde, sollte es mir möglich sein, die Wichtigkeitswerte der Dummy-Variablen zu kombinieren, um die Wichtigkeit für die kategoriale Variable "wiederherzustellen". Natürlich erwarte ich nicht, dass es genau richtig ist, aber diese Werte sind sowieso wirklich genaue Werte, da sie durch einen zufälligen Prozess gefunden werden.
Ich habe den folgenden Python-Code (in Jupyter) als Untersuchung geschrieben:
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())
Hier sind einige der Ergebnisse:
Wir können beobachten, dass die variable Wichtigkeit hauptsächlich von der Anzahl der Kategorien abhängt, was mich dazu bringt, die Nützlichkeit dieser Diagramme im Allgemeinen in Frage zu stellen. Besonders die Wichtigkeit age
, viel höhere Werte als das kontinuierliche Gegenstück zu erreichen.
Und zum Schluss ein Beispiel, wenn ich sie als Dummy-Variablen belasse (nur 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")
Die Frage ist:
Die kurze Antwort:
Je länger, praktischer Antwort ..
Sie können nicht einfach einzelne Variablen-Wichtigkeitswerte für Dummy-Variablen zusammenfassen, da Sie das Risiko eingehen
Probleme wie die mögliche Multikollinearität können die Werte und die Rangfolge der variablen Wichtigkeit verzerren.
Tatsächlich ist es ein sehr interessantes Problem zu verstehen, wie sich Probleme wie Multikollinearität auf die variable Bedeutung auswirken. In der Arbeit zur Bestimmung der Prädiktorbedeutung bei multipler Regression unter verschiedenen Korrelations- und Verteilungsbedingungen werden verschiedene Methoden zur Berechnung der Variablenbedeutung erörtert und die Leistung für Daten verglichen, die typische statistische Annahmen verletzen. Die Autoren fanden das
quelle