¿Cómo eliminar sistemáticamente las variables colineales en Python? [cerrado]

17

Hasta ahora, he eliminado las variables colineales como parte del proceso de preparación de datos al observar las tablas de correlación y eliminar las variables que están por encima de un cierto umbral. ¿Hay una forma más aceptada de hacer esto? Además, soy consciente de que solo mirar la correlación entre 2 variables a la vez no es lo ideal, las mediciones como VIF tienen en cuenta la correlación potencial entre varias variables. ¿Cómo se elegiría sistemáticamente combinaciones de variables que no exhiban multicolinealidad?

Tengo mis datos dentro de un marco de datos de pandas y estoy usando los modelos de sklearn.

naranja1
fuente
3
Es posible que desee considerar la regresión de mínimos cuadrados parciales o la regresión de componentes principales. Uno de estos es probablemente compatible.
spdrnl
Veo. Entonces, si entiendo correctamente, ejecutar PCA me daría un conjunto de componentes principales independientes, que luego podría usar como covariables para mi modelo, ya que cada uno de los componentes principales no es colineal con los demás.
naranja1
2
Exactamente. Es probable que algunos de los componentes resulten irrelevantes. Esto es más fácil que soltar variables.
spdrnl
Hm, entonces mi intención es principalmente ejecutar el modelo con fines explicativos más que predictivos. ¿Cómo se podría interpretar un modelo que utilizara componentes principales como covariables?
naranja1
1
En ese caso, no ayuda, ya que interpretar componentes es algo así como un arte oscuro.
spdrnl

Respuestas:

13

Gracias SpanishBoy - Es un buen código. @ilanman: Esto verifica los valores de VIF y luego elimina las variables cuyo VIF es más de 5. Por "rendimiento", creo que se refiere al tiempo de ejecución. El código anterior me tomó alrededor de 3 horas para ejecutar en aproximadamente 300 variables, 5000 filas.

Por cierto, lo he modificado para eliminar algunos bucles adicionales. Además, lo hice un poco más limpio y devolví el marco de datos con variables reducidas. ¡Esta versión redujo mi tiempo de ejecución a la mitad! Mi código está debajo. Espero que ayude.

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]
Prasante
fuente
Gracias. ¿Has comparado las salidas de ambas funciones? Vi una función R ( usdmmétodo de paquete vifstep) para VIF y el tiempo de ejecución fue realmente genial. Como dije antes, la variante anterior y su (optimizado a la mitad) son tan lentas en comparación con la R. ¿Alguna otra idea de cómo optimizar aún?
SpanishBoy
1
Tengo una pregunta sobre este enfoque. Digamos que tenemos características A, B y C. A está correlacionado con C. Si recorre las características, A y C tendrán VIF> 5, por lo tanto, se eliminarán. En realidad, no debería volver a calcular el VIF después de cada vez que suelta una función. En mi ejemplo, soltarías tanto A como C, pero si calculas VIF (C) después de que se elimine A, no será> 5
Titus Pullo
3

Puede intentar usar el siguiente código:

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

Funciona, pero no me gusta el rendimiento de ese enfoque.

SpanishBoy
fuente
¿Quieres comentar un poco más sobre lo que hace este enfoque? ¿Y por qué no te gusta la actuación?
ilanman
2

Intenté la respuesta de SpanishBoy y encontré errores de servicio al ejecutarlo para un marco de datos. Aquí hay una solución depurada.

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

Tampoco tuve problemas con el rendimiento, pero no lo he probado exhaustivamente.

Braden Fineberg
fuente
Esto es bueno y funciona para mí. excepto, devuelve la advertencia ominiosa:RuntimeWarning: divide by zero encountered in double_scalars
user2205916