Finden Sie den p-Wert (Signifikanz) in scikit-learn LinearRegression

Antworten:

162

Das ist eine Art Overkill, aber probieren wir es aus. Lassen Sie uns zuerst statsmodel verwenden, um herauszufinden, wie die p-Werte aussehen sollten

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

und wir bekommen

                         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, lass uns das reproduzieren. Es ist eine Art Overkill, da wir mit Matrix Algebra fast eine lineare Regressionsanalyse reproduzieren. Aber was zum Teufel.

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)

Und das gibt uns.

    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

So können wir die Werte aus dem Statistikmodell reproduzieren.

JARH
quelle
2
Was bedeutet es, dass meine var_b alle Nans sind? Gibt es einen Grund, warum der lineare Algebra-Teil fehlschlägt?
Famargar
Wirklich schwer zu erraten, warum das so sein könnte. Ich würde mir die Struktur Ihrer Daten ansehen und sie mit dem Beispiel vergleichen. Das könnte einen Hinweis geben.
JARH
1
Es sieht so aus, als ob codenp.linalg.inv manchmal ein Ergebnis zurückgeben kann, selbst wenn die Matrix nicht invertierbar ist. Das könnte das Problem sein.
JARH
6
@famargar Ich hatte auch das Problem aller nans. Für mich lag es daran X, dass meine Daten eine Stichprobe meiner Daten waren, sodass der Index deaktiviert war. Dies führt zu Fehlern beim Aufruf pd.DataFrame.join(). Ich habe diese eine Zeile geändert und es scheint jetzt zu funktionieren:newX = pd.DataFrame({"Constant":np.ones(len(X))}).join(pd.DataFrame(X.reset_index(drop=True)))
pault
1
@ mLstudent33 Die Spalte "Wahrscheinlichkeiten".
skeller88
52

Die LinearRegression von scikit-learn berechnet diese Informationen nicht, aber Sie können die Klasse einfach erweitern, um dies zu tun:

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

Von hier gestohlen .

Sie sollten sich die Statistikmodelle für diese Art der statistischen Analyse in Python ansehen .

Elyase
quelle
Gut. Dies funktioniert nicht, da sse ein Skalar ist, sodass sse.shape eigentlich nichts bedeutet.
Ashu
15

EDIT: Wahrscheinlich nicht der richtige Weg, siehe Kommentare

Sie können sklearn.feature_selection.f_regression verwenden.

Klicken Sie hier für die Scikit-Lernseite

Pinna_be
quelle
1
Das sind also F-Tests? Ich dachte, die p-Werte für die lineare Regression wären typischerweise für jeden einzelnen Regressor, und es war ein Test gegen die Null des Koeffizienten 0? Für eine gute Antwort wäre eine weitere Erläuterung der Funktion erforderlich.
Worte für den
Die Dokumentationsseite @wordsforthewise besagt, dass der zurückgegebene Wert ein Array von p_values ​​ist. Es ist also in der Tat ein Wert für jeden einzelnen Regressor.
Ashu
1
Verwenden Sie diese Methode nicht, da sie nicht korrekt ist! Es führt univariate Regressionen durch, aber Sie möchten wahrscheinlich eine einzelne multivariate Regression
user357269
1
Nein, benutze nicht f_regression. Der tatsächliche p-Wert jedes Koeffizienten sollte aus dem t-Test für jeden Koeffizienten nach dem Anpassen der Daten stammen. f_regression in sklearn kommt von den univariaten Regressionen. Der Modus wurde nicht erstellt, sondern nur die f-Punktzahl für jede Variable berechnet. Entspricht der Funktion chi2 in sklearn. Dies ist richtig: importiere statsmodels.api als sm mod = sm.OLS (Y, X)
Richard Liang
@RichardLiang, benutze sm.OLS () ist der richtige Weg, um den p-Wert (multivariat) für jeden Algorithmus zu berechnen? (wie Entscheidungsbaum, SVM, K-Mittel, logistische Regression usw.)? Ich möchte eine generische Methode, um den p-Wert zu erhalten. Danke
Gilian vor
11

Der Code in der Antwort von elyase https://stackoverflow.com/a/27928411/4240413 funktioniert nicht wirklich. Beachten Sie, dass sse ein Skalar ist und dann versucht, ihn zu durchlaufen. Der folgende Code ist eine geänderte Version. Nicht erstaunlich sauber, aber ich denke, es funktioniert mehr oder weniger.

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
quelle
8

Eine einfache Möglichkeit, die p-Werte abzurufen, ist die Verwendung der Statistikmodell-Regression:

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

Sie erhalten eine Reihe von p-Werten, die Sie bearbeiten können (wählen Sie beispielsweise die Reihenfolge, die Sie beibehalten möchten, indem Sie jeden p-Wert auswerten):

Geben Sie hier die Bildbeschreibung ein

Benaou Mouad
quelle
Verwenden Sie sm.OLS (). Ist dies der richtige Weg, um den p-Wert (multivariat) für einen Algorithmus zu berechnen? (wie Entscheidungsbaum, SVM, K-Mittel, logistische Regression usw.)? Ich möchte eine generische Methode, um den p-Wert zu erhalten. Danke
Gilian vor
7

p_value gehört zu den f-Statistiken. Wenn Sie den Wert erhalten möchten, verwenden Sie einfach die folgenden Codezeilen:

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
quelle
3
Dies beantwortet die Frage nicht, da Sie eine andere Bibliothek als die erwähnte verwenden.
Gented
@gented In welchen Szenarien wäre eine Berechnungsmethode besser als die andere?
Don Quijote
6

Bei einer multivariablen Regression könnte die Antwort von @JARH einen Fehler enthalten . (Ich habe nicht genug Ruf, um einen Kommentar abzugeben.)

In der folgenden Zeile:

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

der t-Wert folgt eine Chi-Quadrat - Verteilung von Grad len(newX)-1statt nach einer Chi-Quadrat - Verteilung Grad len(newX)-len(newX.columns)-1.

Das sollte also sein:

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

( Weitere Informationen finden Sie unter t-Werte für die OLS-Regression. )

Jules K.
quelle
5

Sie können scipy für den p-Wert verwenden. Dieser Code stammt aus der Scipy-Dokumentation.

>>> 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
quelle
1
Ich denke nicht, dass dies für mehrere Vektoren gilt, die während der Anpassung verwendet werden
O.rka
1

Für einen Einzeiler können Sie die Funktion pingouin.linear_regression ( Haftungsausschluss: Ich bin der Schöpfer von Pingouin ) verwenden, die mit uni- / multi-variabler Regression unter Verwendung von NumPy-Arrays oder Pandas DataFrame arbeitet, z.

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)

Die Ausgabe ist ein Datenrahmen mit den Beta-Koeffizienten, Standardfehlern, T-Werten, p-Werten und Konfidenzintervallen für jeden Prädiktor sowie dem R ^ 2 und dem angepassten R ^ 2 der Anpassung.

Raphael
quelle