Comment supprimer systématiquement les variables colinéaires en Python? [fermé]

18

Jusqu'à présent, j'ai supprimé les variables colinéaires dans le cadre du processus de préparation des données en examinant les tableaux de corrélation et en éliminant les variables qui dépassent un certain seuil. Existe-t-il une façon plus acceptée de procéder? De plus, je suis conscient que seule la corrélation entre 2 variables à la fois n'est pas idéale, des mesures comme VIF prennent en compte la corrélation potentielle entre plusieurs variables. Comment procéder pour choisir systématiquement des combinaisons de variables qui ne présentent pas de multicolinéarité?

J'ai mes données dans un cadre de données pandas et j'utilise les modèles de sklearn.

orange1
la source
3
Vous pouvez envisager la régression des moindres carrés partiels ou la régression des composants principaux. L'un d'eux est probablement pris en charge.
spdrnl
Je vois. Donc, si je comprends bien, exécuter PCA me donnerait alors un ensemble de composants principaux indépendants, que je pourrais ensuite utiliser comme covariables pour mon modèle, car chacun des composants principaux n'est pas colinéaire avec les autres?
orange1
2
Exactement. Certains composants risquent de ne pas être pertinents. C'est plus facile que de supprimer des variables.
spdrnl
Hm, mon intention est donc principalement d'exécuter le modèle à des fins explicatives plutôt que prédictives. Comment interpréter un modèle utilisant des composantes principales comme covariables?
orange1
1
Dans ce cas, cela n'aide pas car l'interprétation des composants est un peu un art sombre.
spdrnl

Réponses:

13

Merci SpanishBoy - C'est un bon morceau de code. @ilanman: Cela vérifie les valeurs VIF, puis supprime les variables dont le VIF est supérieur à 5. Par "performances", je pense qu'il veut dire temps d'exécution. Le code ci-dessus m'a pris environ 3 heures pour fonctionner sur environ 300 variables, 5000 lignes.

Soit dit en passant, je l'ai modifié pour supprimer certaines boucles supplémentaires. De plus, je l'ai rendu un peu plus propre et retourne la trame de données avec des variables réduites. Cette version a réduit de moitié mon temps d'exécution! Mon code est ci-dessous - j'espère que ça aide.

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
la source
Je vous remercie. Avez-vous comparé les sorties des deux fonctions? J'ai vu une fonction R ( usdmméthode du package vifstep) pour VIF et le temps d'exécution était vraiment cool. Comme je l'ai déjà dit, la variante ci-dessus et la vôtre (optimisée de moitié) sont si lentes à comparer avec la R. D'autres idées comment optimiser encore?
SpanishBoy
1
J'ai une question sur cette approche. Disons que nous avons des fonctionnalités A, B et C. A est corrélé avec C. Si vous parcourez les fonctionnalités, A et C auront VIF> 5, donc elles seront supprimées. En réalité, ne devriez-vous pas recalculer le VIF après chaque fois que vous supprimez une fonctionnalité. Dans mon exemple, vous baisseriez A et C, mais si vous calculez VIF (C) après la suppression de A, cela ne sera pas> 5
Titus Pullo
3

Vous pouvez essayer d'utiliser le code ci-dessous:

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

Cela fonctionne, mais je n'aime pas les performances de cette approche

SpanishBoy
la source
Voulez-vous commenter un peu plus ce que fait cette approche? Et pourquoi vous n'aimez pas la performance?
ilanman
2

J'ai essayé la réponse de SpanishBoy et trouvé des erreurs de serval lors de son exécution pour une trame de données. Voici une solution déboguée.

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

Je n'ai également eu aucun problème de performance, mais je ne l'ai pas testé de manière approfondie.

Braden Fineberg
la source
c'est sympa et ça marche pour moi. sauf, il renvoie l'avertissement inquiétant:RuntimeWarning: divide by zero encountered in double_scalars
user2205916