Wie kann man kollineare Variablen in Python systematisch entfernen? [geschlossen]


17

Bisher habe ich kollineare Variablen als Teil des Datenvorbereitungsprozesses entfernt, indem ich Korrelationstabellen betrachtet und Variablen beseitigt habe, die über einem bestimmten Schwellenwert liegen. Gibt es eine akzeptiertere Art, dies zu tun? Außerdem ist mir bewusst, dass es nicht ideal ist, nur die Korrelation zwischen zwei Variablen gleichzeitig zu betrachten. Messungen wie VIF berücksichtigen die potenzielle Korrelation zwischen mehreren Variablen. Wie würde man systematisch Variablenkombinationen auswählen, die keine Multikollinearität aufweisen?

Ich habe meine Daten innerhalb eines Pandadatenrahmens und verwende die Modelle von sklearn.


3
Möglicherweise möchten Sie die Regression der kleinsten Teilquadrate oder die Regression der Hauptkomponenten berücksichtigen. Eine davon wird wahrscheinlich unterstützt.
SPDRNL

Aha. Wenn ich das also richtig verstehe, würde mir das Ausführen von PCA eine Reihe unabhängiger Hauptkomponenten geben, die ich dann als Kovariaten für mein Modell verwenden könnte, da jede der Hauptkomponenten nicht mit den anderen kollinear ist?
Orange1

2
Genau. Einige der Komponenten werden sich wahrscheinlich als irrelevant herausstellen. Dies ist einfacher als das Löschen von Variablen.
spdrnl

Hm, meine Absicht ist es also, das Modell eher zu Erklärungszwecken als zu Prognosezwecken zu verwenden. Wie würde man ein Modell interpretieren, das Hauptkomponenten als Kovariaten verwendet?
Orange1

1
In diesem Fall hilft es nicht weiter, da das Interpretieren von Komponenten eine dunkle Kunst ist.
spdrnl

Antworten:


13

Danke SpanishBoy - Es ist ein gutes Stück Code. @ilanman: Hiermit werden VIF-Werte überprüft und anschließend Variablen gelöscht, deren VIF mehr als 5 beträgt. Mit "Leistung" meine ich die Laufzeit. Der obige Code hat ungefähr 3 Stunden gedauert, um mit ungefähr 300 Variablen und 5000 Zeilen zu arbeiten.

Übrigens habe ich es modifiziert, um einige zusätzliche Schleifen zu entfernen. Außerdem habe ich es ein bisschen sauberer gemacht und den Datenrahmen mit reduzierten Variablen zurückgegeben. Diese Version reduzierte meine Laufzeit um die Hälfte! Mein Code ist unten - Ich hoffe, es hilft.

from statsmodels.stats.outliers_influence import variance_inflation_factor    

def calculate_vif_(X, thresh=5.0):
    variables = list(range(X.shape[1]))
    dropped = True
    while dropped:
        dropped = False
        vif = [variance_inflation_factor(X.iloc[:, variables].values, ix)
               for ix in range(X.iloc[:, variables].shape[1])]

        maxloc = vif.index(max(vif))
        if max(vif) > thresh:
            print('dropping \'' + X.iloc[:, variables].columns[maxloc] +
                  '\' at index: ' + str(maxloc))
            del variables[maxloc]
            dropped = True

    print('Remaining variables:')
    print(X.columns[variables])
    return X.iloc[:, variables]

Vielen Dank. Haben Sie die Ausgänge beider Funktionen verglichen? Ich sah eine R - Funktion (Paket - usdmMethode vifstep) für VIF und Laufzeit war wirklich cool. Wie ich bereits sagte, sind die obige und Ihre (um die Hälfte optimierte) Variante im Vergleich zu der R so langsam. Haben Sie noch andere Ideen, wie Sie noch optimieren können?
SpanishBoy

1
Ich habe eine Frage zu diesem Ansatz. Nehmen wir an, wir haben A-, B- und C-Merkmale. A ist mit C korreliert. Wenn Sie die Features durchlaufen, haben A und C VIF> 5, daher werden sie gelöscht. In der Realität sollten Sie das VIF nicht nach jedem Löschen eines Features neu berechnen. In meinem Beispiel würden Sie sowohl A als auch C fallen lassen, aber wenn Sie VIF (C) berechnen, nachdem A fallen gelassen wurde, wird dies nicht> 5 sein
Titus Pullo

3

Sie können versuchen, den folgenden Code zu verwenden:

from statsmodels.stats.outliers_influence import variance_inflation_factor

def calculate_vif_(X):

    '''X - pandas dataframe'''
    thresh = 5.0
    variables = range(X.shape[1])

    for i in np.arange(0, len(variables)):
        vif = [variance_inflation_factor(X[variables].values, ix) for ix in range(X[variables].shape[1])]
        print(vif)
        maxloc = vif.index(max(vif))
        if max(vif) > thresh:
            print('dropping \'' + X[variables].columns[maxloc] + '\' at index: ' + str(maxloc))
            del variables[maxloc]

    print('Remaining variables:')
    print(X.columns[variables])
    return X

Es funktioniert, aber ich mag die Leistung dieses Ansatzes nicht


Möchten Sie etwas mehr zu dieser Vorgehensweise sagen? Und warum magst du die Aufführung nicht?
Ilanman

2

Ich habe SpanishBoys Antwort ausprobiert und einige Fehler gefunden, als ich sie für einen Datenrahmen ausgeführt habe. Hier ist eine debuggte Lösung.

from statsmodels.stats.outliers_influence import variance_inflation_factor    

def calculate_vif_(X, thresh=100):
cols = X.columns
variables = np.arange(X.shape[1])
dropped=True
while dropped:
    dropped=False
    c = X[cols[variables]].values
    vif = [variance_inflation_factor(c, ix) for ix in np.arange(c.shape[1])]

    maxloc = vif.index(max(vif))
    if max(vif) > thresh:
        print('dropping \'' + X[cols[variables]].columns[maxloc] + '\' at index: ' + str(maxloc))
        variables = np.delete(variables, maxloc)
        dropped=True

print('Remaining variables:')
print(X.columns[variables])
return X[cols[variables]]

Ich hatte auch keine Probleme mit der Leistung, habe es aber nicht ausgiebig getestet.


Das ist schön und funktioniert für mich. außer, es gibt die ominöse Warnung zurück:RuntimeWarning: divide by zero encountered in double_scalars
user2205916
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.