Jak systematycznie usuwać zmienne współliniowe w Pythonie? [Zamknięte]

18

Do tej pory usunąłem zmienne współliniowe w ramach procesu przygotowania danych, patrząc na tabele korelacji i eliminując zmienne, które przekraczają pewien próg. Czy istnieje bardziej akceptowany sposób na zrobienie tego? Ponadto jestem świadomy, że patrzenie tylko na korelację między 2 zmiennymi jednocześnie nie jest idealne, pomiary takie jak VIF uwzględniają potencjalną korelację między kilkoma zmiennymi. Jak można systematycznie wybierać kombinacje zmiennych, które nie wykazują wielokoliniowości?

Mam swoje dane w ramce danych pand i używam modeli sklearn.

pomarańczowy 1
źródło
3
Warto rozważyć regresję częściowych najmniejszych kwadratów lub regresję głównych składników. Jeden z nich jest prawdopodobnie obsługiwany.
spdrnl
Widzę. Więc jeśli dobrze rozumiem, uruchomienie PCA dałoby mi wtedy zestaw niezależnych głównych komponentów, których mógłbym następnie użyć jako zmiennych towarzyszących dla mojego modelu, ponieważ każdy z głównych komponentów nie jest współliniowy z innymi?
orange1
2
Dokładnie. Niektóre elementy mogą okazać się nieistotne. Jest to łatwiejsze niż usuwanie zmiennych.
spdrnl
Hm, więc moim zamiarem jest przede wszystkim uruchomienie modelu w celach wyjaśniających niż przewidywalnych. Jak zająłby się interpretacja modelu, w którym główne komponenty byłyby zmiennymi towarzyszącymi?
orange1
1
W takim przypadku nie pomaga, ponieważ interpretacja komponentów jest poniekąd czarną sztuką.
spdrnl

Odpowiedzi:

13

Dzięki SpanishBoy - To dobry kawałek kodu. @ilanman: Sprawdza wartości VIF, a następnie upuszcza zmienne, których VIF jest większy niż 5. Przez „wydajność” myślę, że ma na myśli czas działania. Powyższy kod zajął mi około 3 godzin, aby uruchomić na około 300 zmiennych, 5000 wierszy.

Nawiasem mówiąc, zmodyfikowałem go, aby usunąć dodatkowe pętle. Poza tym sprawiłem, że jest trochę czystszy i zwracam ramkę danych ze zredukowanymi zmiennymi. Ta wersja skróciła mój czas działania o połowę! Mój kod jest poniżej - Mam nadzieję, że to pomaga.

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]
Prashant
źródło
Dziękuję Ci. Czy porównałeś wyniki obu funkcji? Widziałem funkcję R ( usdmmetoda pakietowa vifstep) dla VIF, a czas działania był naprawdę fajny. Jak powiedziałem wcześniej, powyższy wariant i twój (zoptymalizowany o połowę) są tak powolni w porównaniu z R. Czy są jeszcze inne pomysły na optymalizację?
SpanishBoy
1
Mam pytanie dotyczące tego podejścia. Powiedzmy, że mamy funkcje A, B i C. A jest skorelowane z C. Jeśli przejdziesz przez funkcje, A i C będą miały VIF> 5, a zatem zostaną upuszczone. W rzeczywistości nie powinieneś ponownie obliczać VIF po każdym upuszczeniu funkcji. W moim przykładzie spadłbyś zarówno A, jak i C, ale jeśli obliczysz VIF (C) po upuszczeniu A, to nie będzie> 5
Titus Pullo
3

Możesz spróbować użyć poniższego kodu:

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

Działa, ale nie podoba mi się wydajność tego podejścia

SpanishBoy
źródło
Czy chcesz komentować nieco więcej na temat tego, co robi to podejście? A dlaczego nie lubisz występu?
ilanman
2

Próbowałem odpowiedzi SpanishBoy i znalazłem serwal błędy, uruchamiając ją dla ramki danych. Oto debugowane rozwiązanie.

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

Nie miałem również problemów z wydajnością, ale nie przetestowałem go dokładnie.

Braden Fineberg
źródło
to jest miłe i działa dla mnie. z wyjątkiem, że zwraca złowieszcze ostrzeżenie:RuntimeWarning: divide by zero encountered in double_scalars
user2205916