Trova il valore p (significato) in Scikit-learn LinearRegression


154

Come posso trovare il valore p (significatività) di ciascun coefficiente?

lm = sklearn.linear_model.LinearRegression()
lm.fit(x,y)

2
Non la tua risposta, ma forse una risposta ad altri: scipy fornisce i valori in linregressione: docs.scipy.org/doc/scipy-0.14.0/reference/generated/…
DaveRGP

funziona solo per una dimensione contro una dimensione.
Richard Liang,

Risposte:


162

Questo è un po 'eccessivo, ma proviamolo. Per prima cosa usiamo statsmodel per scoprire quali dovrebbero essere i valori p

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())

e otteniamo

                         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, riproduciamolo. È un po 'eccessivo in quanto stiamo quasi riproducendo un'analisi di regressione lineare usando Matrix Algebra. Ma che diamine.

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)

E questo ci dà.

    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

Quindi possiamo riprodurre i valori da statsmodel.


2
cosa significa che i miei var_b sono tutti Nans? C'è qualche motivo di fondo per cui la parte algebra lineare fallisce?
Famargar

Davvero difficile indovinare il motivo per cui potrebbe essere. Vorrei esaminare la struttura dei tuoi dati e confrontarli con l'esempio. Questo potrebbe fornire un indizio.
JARH

1
Sembra che codenp.linalg.inv possa talvolta restituire un risultato anche quando la matrice non è invertibile. Questo potrebbe essere il problema.
JARH

7
@famargar Ho anche avuto il problema di tutti i nans. Per me è stato perché i miei Xerano un campione dei miei dati, quindi l'indice era spento. Ciò provoca errori durante la chiamata pd.DataFrame.join(). Ho fatto questo cambio di una riga e sembra funzionare ora:newX = pd.DataFrame({"Constant":np.ones(len(X))}).join(pd.DataFrame(X.reset_index(drop=True)))
pault

1
@ mLstudent33 La colonna "probabilità".
skeller88,

52

LinearRegression di scikit-learn non calcola queste informazioni ma puoi facilmente estendere la classe per farlo:

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

Rubato da qui .

Dovresti dare un'occhiata a statsmodels per questo tipo di analisi statistica in Python.


Bene. Questo non funziona perché sse è uno scalare, quindi sse.shape non significa nulla.
Ashu,

15

EDIT: probabilmente non è il modo giusto per farlo, vedi commenti

È possibile utilizzare sklearn.feature_selection.f_regression.

fai clic qui per la pagina di apprendimento di scikit


1
Quindi quelli sono i test F? Pensavo che i valori di p per la regressione lineare fossero in genere per ogni singolo regressore, ed era un test contro il valore nullo del coefficiente essendo 0? Ulteriori spiegazioni della funzione sarebbero necessarie per una buona risposta.
parole per il

La pagina di documentazione di @wordsforthewise dice che il valore restituito è un array di p_values. Quindi è davvero un valore per ogni singolo regressore.
Ashu,

1
Non utilizzare questo metodo in quanto non è corretto! Esegue regressioni univariate, ma probabilmente si desidera una singola regressione multivariata
user357269

1
No, non usare f_regression. Il valore p effettivo di ciascun coefficiente dovrebbe provenire dalla prova t per ciascun coefficiente dopo aver inserito i dati. f_regression in sklearn deriva dalle regressioni univariate. Non ha creato la modalità, è sufficiente calcolare il punteggio f per ogni variabile. Come la funzione chi2 in sklearn Questo è corretto: import statsmodels.api as sm mod = sm.OLS (Y, X)
Richard Liang

@RichardLiang, utilizzare sm.OLS () è il modo corretto di calcolare il valore p (multivariato) per qualsiasi algoritmo? (come albero decisionale, svm, k-mean, regressione logistica, ecc.)? Vorrei un metodo generico per ottenere il valore p. Grazie
Gilian il

11

Il codice nella risposta di elyase https://stackoverflow.com/a/27928411/4240413 in realtà non funziona. Si noti che sse è uno scalare e quindi tenta di scorrere attraverso di esso. Il seguente codice è una versione modificata. Non incredibilmente pulito, ma penso che funzioni più o meno.

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)

8

Un modo semplice per estrarre i valori p è usare la regressione statsmodels:

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

Si ottiene una serie di valori p che è possibile manipolare (ad esempio, scegliere l'ordine che si desidera mantenere valutando ciascun valore p):

inserisci qui la descrizione dell'immagine


Utilizzare sm.OLS () è il modo corretto di calcolare il valore p (multivariato) per qualsiasi algoritmo? (come albero decisionale, svm, k-mean, regressione logistica, ecc.)? Vorrei un metodo generico per ottenere il valore p. Grazie
Gilian il

7

p_value è tra le statistiche f. se vuoi ottenere il valore, usa semplicemente queste poche righe di codice:

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)

3
Questo non risponde alla domanda poiché stai usando una libreria diversa da quella menzionata.
Gented

@gented Quali sono gli scenari in cui un metodo di calcolo sarebbe migliore dell'altro?
Don Chisciotte,

6

Potrebbe esserci un errore nella risposta di @JARH nel caso di una regressione multivariabile. (Non ho abbastanza reputazione per commentare.)

Nella seguente riga:

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

i valori t seguono una distribuzione chi-quadrato di grado len(newX)-1invece di seguire una distribuzione chi-quadrato di grado len(newX)-len(newX.columns)-1.

Quindi questo dovrebbe essere:

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

(Vedi i valori t per la regressione OLS per maggiori dettagli)


5

Puoi usare scipy per il valore p. Questo codice proviene dalla documentazione di 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)

1
Non penso che ciò valga per più vettori utilizzati durante l'adattamento
O.rka,

1

Per un one-liner è possibile utilizzare la funzione pingouin.linear_regression ( dichiarazione di non responsabilità: sono il creatore di Pingouin ), che funziona con la regressione uni / multi-variabile utilizzando array NumPy o Pandas DataFrame, ad esempio:

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)

L'output è un frame di dati con coefficienti beta, errori standard, valori T, valori p e intervalli di confidenza per ciascun predittore, nonché R ^ 2 e R ^ 2 regolati dell'adattamento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.