Trouver la valeur p (signification) dans scikit-learn LinearRegression

154

Comment puis-je trouver la valeur p (signification) de chaque coefficient?

lm = sklearn.linear_model.LinearRegression()
lm.fit(x,y)
elplatt
la source
2
Pas votre réponse, mais peut-être une réponse à d'autres: scipy fournit des pvalues ​​dans linregression: docs.scipy.org/doc/scipy-0.14.0/reference/generated/…
DaveRGP
cela ne fonctionne que pour une dimension vs une dimension.
Richard Liang le

Réponses:

162

C'est un peu exagéré, mais essayons-le. Utilisons d'abord statsmodel pour savoir quelles devraient être les p-values

import pandas as pd
import numpy as np
from sklearn import datasets, linear_model
from sklearn.linear_model import LinearRegression
import statsmodels.api as sm
from scipy import stats

diabetes = datasets.load_diabetes()
X = diabetes.data
y = diabetes.target

X2 = sm.add_constant(X)
est = sm.OLS(y, X2)
est2 = est.fit()
print(est2.summary())

et nous obtenons

                         OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.518
Model:                            OLS   Adj. R-squared:                  0.507
Method:                 Least Squares   F-statistic:                     46.27
Date:                Wed, 08 Mar 2017   Prob (F-statistic):           3.83e-62
Time:                        10:08:24   Log-Likelihood:                -2386.0
No. Observations:                 442   AIC:                             4794.
Df Residuals:                     431   BIC:                             4839.
Df Model:                          10                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const        152.1335      2.576     59.061      0.000     147.071     157.196
x1           -10.0122     59.749     -0.168      0.867    -127.448     107.424
x2          -239.8191     61.222     -3.917      0.000    -360.151    -119.488
x3           519.8398     66.534      7.813      0.000     389.069     650.610
x4           324.3904     65.422      4.958      0.000     195.805     452.976
x5          -792.1842    416.684     -1.901      0.058   -1611.169      26.801
x6           476.7458    339.035      1.406      0.160    -189.621    1143.113
x7           101.0446    212.533      0.475      0.635    -316.685     518.774
x8           177.0642    161.476      1.097      0.273    -140.313     494.442
x9           751.2793    171.902      4.370      0.000     413.409    1089.150
x10           67.6254     65.984      1.025      0.306     -62.065     197.316
==============================================================================
Omnibus:                        1.506   Durbin-Watson:                   2.029
Prob(Omnibus):                  0.471   Jarque-Bera (JB):                1.404
Skew:                           0.017   Prob(JB):                        0.496
Kurtosis:                       2.726   Cond. No.                         227.
==============================================================================

Ok, reproduisons ceci. C'est un peu exagéré car nous reproduisons presque une analyse de régression linéaire en utilisant l'algèbre matricielle. Mais que diable.

lm = LinearRegression()
lm.fit(X,y)
params = np.append(lm.intercept_,lm.coef_)
predictions = lm.predict(X)

newX = pd.DataFrame({"Constant":np.ones(len(X))}).join(pd.DataFrame(X))
MSE = (sum((y-predictions)**2))/(len(newX)-len(newX.columns))

# Note if you don't want to use a DataFrame replace the two lines above with
# newX = np.append(np.ones((len(X),1)), X, axis=1)
# MSE = (sum((y-predictions)**2))/(len(newX)-len(newX[0]))

var_b = MSE*(np.linalg.inv(np.dot(newX.T,newX)).diagonal())
sd_b = np.sqrt(var_b)
ts_b = params/ sd_b

p_values =[2*(1-stats.t.cdf(np.abs(i),(len(newX)-len(newX[0])))) for i in ts_b]

sd_b = np.round(sd_b,3)
ts_b = np.round(ts_b,3)
p_values = np.round(p_values,3)
params = np.round(params,4)

myDF3 = pd.DataFrame()
myDF3["Coefficients"],myDF3["Standard Errors"],myDF3["t values"],myDF3["Probabilities"] = [params,sd_b,ts_b,p_values]
print(myDF3)

Et cela nous donne.

    Coefficients  Standard Errors  t values  Probabilities
0       152.1335            2.576    59.061         0.000
1       -10.0122           59.749    -0.168         0.867
2      -239.8191           61.222    -3.917         0.000
3       519.8398           66.534     7.813         0.000
4       324.3904           65.422     4.958         0.000
5      -792.1842          416.684    -1.901         0.058
6       476.7458          339.035     1.406         0.160
7       101.0446          212.533     0.475         0.635
8       177.0642          161.476     1.097         0.273
9       751.2793          171.902     4.370         0.000
10       67.6254           65.984     1.025         0.306

Nous pouvons donc reproduire les valeurs de statsmodel.

JARH
la source
2
qu'est-ce que cela signifie que mes var_b sont tous des Nans? Y a-t-il une raison sous-jacente pour laquelle la partie d'algèbre linéaire échoue?
famargar
Vraiment difficile de deviner pourquoi cela pourrait être. Je regarderais la structure de vos données et la comparerais à l'exemple. Cela pourrait fournir un indice.
JARH
1
Il semble que codenp.linalg.inv peut parfois renvoyer un résultat même lorsque la matrice n'est pas inversible. Cela pourrait être le problème.
JARH
7
@famargar J'ai aussi eu le problème de tous les nans. Pour moi, c'était parce que mes données Xétaient un échantillon de mes données, donc l 'index était désactivé. Cela provoque des erreurs lors de l'appel pd.DataFrame.join(). J'ai fait ce changement d'une ligne et cela semble fonctionner maintenant:newX = pd.DataFrame({"Constant":np.ones(len(X))}).join(pd.DataFrame(X.reset_index(drop=True)))
pault
1
@ mLstudent33 La colonne "probabilités".
skeller88 le
52

LinearRegression de scikit-learn ne calcule pas ces informations, mais vous pouvez facilement étendre la classe pour le faire:

from sklearn import linear_model
from scipy import stats
import numpy as np


class LinearRegression(linear_model.LinearRegression):
    """
    LinearRegression class after sklearn's, but calculate t-statistics
    and p-values for model coefficients (betas).
    Additional attributes available after .fit()
    are `t` and `p` which are of the shape (y.shape[1], X.shape[1])
    which is (n_features, n_coefs)
    This class sets the intercept to 0 by default, since usually we include it
    in X.
    """

    def __init__(self, *args, **kwargs):
        if not "fit_intercept" in kwargs:
            kwargs['fit_intercept'] = False
        super(LinearRegression, self)\
                .__init__(*args, **kwargs)

    def fit(self, X, y, n_jobs=1):
        self = super(LinearRegression, self).fit(X, y, n_jobs)

        sse = np.sum((self.predict(X) - y) ** 2, axis=0) / float(X.shape[0] - X.shape[1])
        se = np.array([
            np.sqrt(np.diagonal(sse[i] * np.linalg.inv(np.dot(X.T, X))))
                                                    for i in range(sse.shape[0])
                    ])

        self.t = self.coef_ / se
        self.p = 2 * (1 - stats.t.cdf(np.abs(self.t), y.shape[0] - X.shape[1]))
        return self

Volé d' ici .

Vous devriez jeter un œil aux statsmodels pour ce type d'analyse statistique en Python.

elyase
la source
Bien. Cela ne fonctionne pas car sse est un scalaire donc sse.shape ne veut vraiment rien dire.
ashu
15

EDIT: Probablement pas la bonne façon de le faire, voir les commentaires

Vous pouvez utiliser sklearn.feature_selection.f_regression.

cliquez ici pour la page scikit-learn

Pinna_be
la source
1
Ce sont donc des tests F? Je pensais que les valeurs p pour la régression linéaire étaient généralement pour chaque régresseur individuel, et c'était un test par rapport à la valeur nulle du coefficient étant 0? Une explication plus détaillée de la fonction serait nécessaire pour une bonne réponse.
wordsforthewise
La page de documentation @wordsforthewise indique que la valeur renvoyée est un tableau de p_values. Il s'agit donc bien d'une valeur pour chaque régresseur individuel.
ashu
1
N'utilisez pas cette méthode car elle n'est pas correcte! Il effectue des régressions univariées, mais vous voulez probablement une régression multivariée unique
user357269
1
Non, n'utilisez pas f_regression. La valeur p réelle de chaque coefficient doit provenir du test t pour chaque coefficient après ajustement des données. f_regression dans sklearn provient des régressions univariées. Il n'a pas construit le mode, calcule simplement le score f pour chaque variable. Identique à la fonction chi2 dans sklearn C'est correct: importez statsmodels.api en tant que sm mod = sm.OLS (Y, X)
Richard Liang
@RichardLiang, utilisez sm.OLS () est la bonne façon de calculer la valeur p (multivariée) pour n'importe quel algorithme? (comme l'arbre de décision, svm, k-means, régression logistique, etc.)? Je voudrais une méthode générique pour obtenir la valeur p. Merci
Gilian
11

Le code dans la réponse d'elyase https://stackoverflow.com/a/27928411/4240413 ne fonctionne pas réellement. Notez que sse est un scalaire, puis il essaie de l'itérer. Le code suivant est une version modifiée. Pas incroyablement propre, mais je pense que cela fonctionne plus ou moins.

class LinearRegression(linear_model.LinearRegression):

    def __init__(self,*args,**kwargs):
        # *args is the list of arguments that might go into the LinearRegression object
        # that we don't know about and don't want to have to deal with. Similarly, **kwargs
        # is a dictionary of key words and values that might also need to go into the orginal
        # LinearRegression object. We put *args and **kwargs so that we don't have to look
        # these up and write them down explicitly here. Nice and easy.

        if not "fit_intercept" in kwargs:
            kwargs['fit_intercept'] = False

        super(LinearRegression,self).__init__(*args,**kwargs)

    # Adding in t-statistics for the coefficients.
    def fit(self,x,y):
        # This takes in numpy arrays (not matrices). Also assumes you are leaving out the column
        # of constants.

        # Not totally sure what 'super' does here and why you redefine self...
        self = super(LinearRegression, self).fit(x,y)
        n, k = x.shape
        yHat = np.matrix(self.predict(x)).T

        # Change X and Y into numpy matricies. x also has a column of ones added to it.
        x = np.hstack((np.ones((n,1)),np.matrix(x)))
        y = np.matrix(y).T

        # Degrees of freedom.
        df = float(n-k-1)

        # Sample variance.     
        sse = np.sum(np.square(yHat - y),axis=0)
        self.sampleVariance = sse/df

        # Sample variance for x.
        self.sampleVarianceX = x.T*x

        # Covariance Matrix = [(s^2)(X'X)^-1]^0.5. (sqrtm = matrix square root.  ugly)
        self.covarianceMatrix = sc.linalg.sqrtm(self.sampleVariance[0,0]*self.sampleVarianceX.I)

        # Standard erros for the difference coefficients: the diagonal elements of the covariance matrix.
        self.se = self.covarianceMatrix.diagonal()[1:]

        # T statistic for each beta.
        self.betasTStat = np.zeros(len(self.se))
        for i in xrange(len(self.se)):
            self.betasTStat[i] = self.coef_[0,i]/self.se[i]

        # P-value for each beta. This is a two sided t-test, since the betas can be 
        # positive or negative.
        self.betasPValue = 1 - t.cdf(abs(self.betasTStat),df)
Alex
la source
8

Un moyen facile d'extraire les p-values ​​est d'utiliser la régression statsmodels:

import statsmodels.api as sm
mod = sm.OLS(Y,X)
fii = mod.fit()
p_values = fii.summary2().tables[1]['P>|t|']

Vous obtenez une série de p-values ​​que vous pouvez manipuler (par exemple, choisissez l'ordre que vous souhaitez conserver en évaluant chaque p-value):

entrez la description de l'image ici

benaou mouad
la source
Utilisez sm.OLS () est la bonne façon de calculer la valeur p (multivariée) pour n'importe quel algorithme? (comme l'arbre de décision, svm, k-means, régression logistique, etc.)? Je voudrais une méthode générique pour obtenir la valeur p. Merci
Gilian
7

p_value fait partie des statistiques f. si vous voulez obtenir la valeur, utilisez simplement ces quelques lignes de code:

import statsmodels.api as sm
from scipy import stats

diabetes = datasets.load_diabetes()
X = diabetes.data
y = diabetes.target

X2 = sm.add_constant(X)
est = sm.OLS(y, X2)
print(est.fit().f_pvalue)
Afshin Amiri
la source
3
Cela ne répond pas à la question car vous utilisez une bibliothèque différente de celle mentionnée.
gented le
@gented Quels sont les scénarios où une méthode de calcul serait meilleure que l'autre?
Don Quixote
6

Il pourrait y avoir une erreur dans la réponse de @JARH dans le cas d'une régression multivariée. (Je n'ai pas assez de réputation pour commenter.)

Dans la ligne suivante:

p_values =[2*(1-stats.t.cdf(np.abs(i),(len(newX)-1))) for i in ts_b],

les valeurs t suivent une distribution chi-carré du degré len(newX)-1au lieu de suivre une distribution chi carré du degré len(newX)-len(newX.columns)-1.

Cela devrait donc être:

p_values =[2*(1-stats.t.cdf(np.abs(i),(len(newX)-len(newX.columns)-1))) for i in ts_b]

(Voir les valeurs t pour la régression OLS pour plus de détails)

Jules K
la source
5

Vous pouvez utiliser scipy pour la valeur p. Ce code est issu de la documentation scipy.

>>> from scipy import stats
>>> import numpy as np
>>> x = np.random.random(10)
>>> y = np.random.random(10)
>>> slope, intercept, r_value, p_value, std_err = stats.linregress(x,y)
Ali Mirzaei
la source
1
Je ne pense pas que cela s'applique à plusieurs vecteurs utilisés pendant l'ajustement
O.rka
1

Pour un one-liner, vous pouvez utiliser la fonction pingouin.linear_regression ( avertissement: je suis le créateur de Pingouin ), qui fonctionne avec une régression uni / multi-variée en utilisant des tableaux NumPy ou Pandas DataFrame, par exemple:

import pingouin as pg
# Using a Pandas DataFrame `df`:
lm = pg.linear_regression(df[['x', 'z']], df['y'])
# Using a NumPy array:
lm = pg.linear_regression(X, y)

La sortie est une trame de données avec les coefficients bêta, les erreurs standard, les valeurs T, les valeurs p et les intervalles de confiance pour chaque prédicteur, ainsi que le R ^ 2 et le R ^ 2 ajusté de l'ajustement.

Raphael
la source