Как систематически удалять коллинеарные переменные в Python? [закрыто]

18

До сих пор я удалял коллинеарные переменные как часть процесса подготовки данных, просматривая корреляционные таблицы и удаляя переменные, которые превышают определенный порог. Есть ли более приемлемый способ сделать это? Кроме того, я знаю, что рассмотрение только двух переменных одновременно не является идеальным, измерения, такие как VIF, учитывают потенциальную корреляцию между несколькими переменными. Как можно было бы систематически выбирать переменные комбинации, которые не проявляют мультиколлинеарности?

У меня есть данные внутри фрейма данных панд, и я использую модели Склеарна.

oRange1
источник
3
Возможно, вы захотите рассмотреть регрессию частично наименьших квадратов или регрессию главных компонентов. Один из них, вероятно, поддерживается.
spdrnl
Понимаю. Поэтому, если я правильно понимаю, запуск PCA даст мне набор независимых главных компонентов, которые я затем смогу использовать в качестве ковариат для моей модели, поскольку каждый из основных компонентов не является коллинеарным с остальными?
orange1
2
Точно. Некоторые компоненты могут оказаться неактуальными. Это проще, чем сбросить переменные.
spdrnl
Хм, поэтому я намерен в первую очередь использовать модель для объяснения, а не для прогнозирования. Как можно было бы интерпретировать модель, в которой главные компоненты использовались как ковариаты?
orange1
1
В этом случае это не помогает, поскольку интерпретация компонентов является чем-то вроде темного искусства.
spdrnl

Ответы:

13

Спасибо SpanishBoy - Это хороший кусок кода. @ilanman: это проверяет значения VIF, а затем отбрасывает переменные, чей VIF больше 5. Под «производительностью» я думаю, что он подразумевает время выполнения. Приведенный выше код занял у меня около 3 часов, чтобы запустить около 300 переменных, 5000 строк.

Кстати, я изменил его, чтобы удалить некоторые дополнительные петли. Кроме того, я сделал его немного чище и возвращаю фрейм данных с уменьшенными переменными. Эта версия сократила мое время выполнения наполовину! Мой код ниже - надеюсь, это поможет.

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 ( usdmметод пакета vifstep) для VIF, и время выполнения было действительно круто. Как я уже говорил, вариант выше и ваш (оптимизированный вдвое) настолько медленны по сравнению с R. Есть еще идеи, как оптимизировать?
SpanishBoy
1
У меня есть вопрос об этом подходе. Допустим, у нас есть функции A, B и C. A коррелирует с C. Если вы перебираете объекты, у A и C будет VIF> 5, следовательно, они будут отброшены. На самом деле, вы не должны пересчитывать VIF после каждого падения функции. В моем примере вы отбросили бы и A, и C, но если вы вычислите VIF (C) после того, как A будет сброшено, это не будет> 5
Titus
3

Вы можете попробовать использовать следующий код:

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

Это работает, но мне не нравится производительность этого подхода

SpanishBoy
источник
Хотите еще немного прокомментировать, что делает этот подход? А почему вам не нравится представление?
Иланман
2

Я попробовал ответ SpanishBoy и обнаружил ошибки в работе сервера при работе с фреймом данных. Вот отлаженное решение.

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

У меня также не было проблем с производительностью, но я не проверял это всесторонне.

Браден Файнберг
источник
это хорошо и работает для меня. кроме того, он возвращает зловещее предупреждение:RuntimeWarning: divide by zero encountered in double_scalars
user2205916