Processing math: 100%

29 votos

¿Cómo eliminar sistemáticamente las variables colineales (columnas pandas) en Python?

Hasta ahora, he eliminado las variables colineales como parte del proceso de preparación de los datos, mirando las tablas de correlación y eliminando las variables que están por encima de un determinado umbral. ¿Existe una forma más aceptada de hacer esto? Además, soy consciente de que sólo mirar la correlación entre 2 variables a la vez no es ideal, las medidas como el VIF tienen en cuenta la correlación potencial a través de varias variables. ¿Cómo se puede elegir sistemáticamente combinaciones de variables que no presenten multicolinealidad?

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

4 votos

Puede que le interese la regresión por mínimos cuadrados parciales o la regresión por componentes principales. Es probable que uno de ellos sea compatible.

0 votos

Ya veo. Entonces, si lo he entendido bien, al ejecutar PCA obtendría un conjunto de componentes principales independientes, que podría utilizar como covariables para mi modelo, ya que cada uno de los componentes principales no es colineal con los demás.

2 votos

Exactamente. Es probable que algunos de los componentes resulten irrelevantes. Esto es más fácil que eliminar variables.

22voto

Ryan Gates Puntos 311

Gracias SpanishBoy - Es un buen trozo de código. @ilanman: Esto comprueba los valores del VIF y luego descarta las variables cuyo VIF es superior a 5. Por "rendimiento", creo que se refiere al tiempo de ejecución. El código anterior me llevó unas 3 horas para ejecutar unas 300 variables, 5000 filas.

Por cierto, lo he modificado para eliminar algunos bucles de más. Además, lo he hecho un poco más limpio y devuelve el dataframe con variables reducidas. Esta versión ha reducido el tiempo de ejecución a la mitad. Mi código está abajo- 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]

0 votos

Gracias, señor. ¿Ha comparado los resultados de ambas funciones? He visto una función en R (paquete usdm método vifstep ) para VIF y tiempo de ejecución era realmente genial. Como he dicho antes, la variante de arriba y su (optimizado a la mitad) son tan lentos en comparación con el R. ¿Alguna otra idea de cómo optimizar todavía?

3 votos

Tengo una pregunta sobre este enfoque. Supongamos que tenemos las características A,B y C. A está correlacionada con C. A está correlacionada con C. Si hacemos un bucle sobre las características, A y C tendrán un VIF > 5, por lo que serán descartadas. En realidad, ¿no debería volver a calcular el VIF cada vez que elimine una característica? En mi ejemplo, usted descartaría A y C, pero si calcula el VIF (C) después de descartar A, no será > 5.

0 votos

Creo que deberías añadir una constante

4voto

MPH Nebraska Puntos 11

Puedes 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

0 votos

¿Quiere comentar un poco más en qué consiste este enfoque? ¿Y por qué no te gusta el rendimiento?

3voto

He probado la respuesta de SpanishBoy y he encontrado varios errores al ejecutarla 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 he tenido problemas con el rendimiento, pero no lo he probado exhaustivamente.

1 votos

Esto es agradable y funciona para mí. excepto, que devuelve la advertencia ominosa: RuntimeWarning: divide by zero encountered in double_scalars

0 votos

Yo añadiría:if len(variables) == 1: break

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X