LoginSignup
7
12

More than 5 years have passed since last update.

Pythonでの線形回帰(statmodels, scikit-learn, PyMC3)

Last updated at Posted at 2017-03-12

はじめに

Pythonではいくつか線形回帰をするために使えるライブラリがあります。個人的に線形回帰をする必要にせまられ、そのための方法を調べたのでメモを兼ねてシェアしたいと思います。使ったライブラリは以下:
- statmodels
- scikit-learn
- PyMC3

データ準備

まず適当なデータを用意します。今回は以下の式にノイズ $\epsilon$ を加えた人工データを使います。

$$y = \beta_0 + \beta_1 x_1 + \beta_2 x2 + \epsilon$$

ここで推定するのは、$\beta_0, \beta_1, \beta_2$の値。

import numpy as np
import pandas as pd
import random
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Generate random data
beta = [1.2, 0.5]
# prep data
x1 = np.random.random(size=1000)*5 
x2 = np.random.random(size=1000)*10 
x = np.transpose([x1, x2])
y = np.dot(x, beta)  + np.random.normal(scale=0.5, size=1000)
#data = dict(x=x, y=y)
data = dict(x1=x1, x2=x2, y=y)
df = pd.DataFrame(data)
# visualisation
plt.scatter(x1, y, color='b')
plt.scatter(x2, y, color='orange')
# 3D
fig = plt.figure()
ax = Axes3D(fig)
ax.scatter(x1, x2, y)
plt.show()

output_2_0.png
output_2_1.png

Statmodelsを使う場合

import statsmodels.api as sm

x = sm.add_constant(x)
results = sm.OLS(endog=y, exog=x).fit()
results.summary()
OLS Regression Results
Dep. Variable: y R-squared: 0.951
Model: OLS Adj. R-squared: 0.951
Method: Least Squares F-statistic: 9745.
Date: Fri, 10 Mar 2017 Prob (F-statistic): 0.00
Time: 09:58:59 Log-Likelihood: -724.14
No. Observations: 1000 AIC: 1454.
Df Residuals: 997 BIC: 1469.
Df Model: 2
Covariance Type: nonrobust
coef std err t P>|t| [95.0% Conf. Int.]
const 0.0499 0.042 1.181 0.238 -0.033 0.133
x1 1.1823 0.011 107.081 0.000 1.161 1.204
x2 0.4983 0.005 91.004 0.000 0.488 0.509
Omnibus: 0.654 Durbin-Watson: 2.079
Prob(Omnibus): 0.721 Jarque-Bera (JB): 0.599
Skew: -0.059 Prob(JB): 0.741
Kurtosis: 3.023 Cond. No. 17.2

scikit-learnを使う場合

from sklearn import linear_model
# compare different regressions
lreg = linear_model.LinearRegression()
lreg.fit(x, y)
print("Linear regression: \t", lreg.coef_)
breg = linear_model.BayesianRidge()
breg.fit(x, y)
print("Bayesian regression: \t", breg.coef_)
ereg = linear_model.ElasticNetCV()
ereg.fit(x, y)
print("Elastic net:  \t\t", ereg.coef_)
print("true parameter values: \t", beta)
Linear regression:   [ 1.18232244  0.49832431]
Bayesian regression:     [ 1.18214701  0.49830501]
Elastic net:         [ 1.17795855  0.49756084]
true parameter values:   [1.2, 0.5]

PyMC3を使う場合

上2つの場合では最尤推定を使ってパラメータを推定していましたが、PyMC3ではベイズ定理に基づいてマルコフ連鎖モンテカルロ法(MCMC)でパラメータの確率分布を求めます。

import pymc3 as pm

with pm.Model() as model_robust:
    family = pm.glm.families.Normal()
    #pm.glm.glm('y ~ x', data, family=family)
    pm.glm.glm('y ~ x1+x2', df, family=family)
    start = pm.find_MAP()
    step = pm.NUTS(scaling=start)
    #step = pm.Metropolis()
    trace_robust = pm.sample(25000, step)
Optimization terminated successfully.
         Current function value: 764.008811
         Iterations: 19
         Function evaluations: 30
         Gradient evaluations: 30


100%|██████████| 25000/25000 [00:32<00:00, 761.52it/s]
# show results
pm.traceplot(trace_robust[2000:])
#pm.summary(trace_robust[2000:])
plt.show()
pm.df_summary(trace_robust[2000:])

output_9_0.png
 

mean sd mc_error hpd_2.5 hpd_97.5
Intercept -0.022296 0.040946 0.000564 -0.100767 0.057460
x1 1.199371 0.011235 0.000126 1.177191 1.221067
x2 0.500502 0.005346 0.000057 0.490258 0.511003
sd 0.490271 0.010949 0.000072 0.469599 0.512102

もうちょっと複雑な場合でやってみる

PyMC3がどこまでできるのか見るために、もうちょっと複雑な場合でやってみます。元になる式は以下:

$y = \beta_0 + \beta_1 x_1 + \beta_2 x_2+ \beta_3 x_3 + \beta_4 x_4 + \beta_5 x_5 + \epsilon$

# Generate random data
beta = [1.2, 0.5, 9.8, 0.2, 1.08]
# prep data
x1 = np.random.random(size=1000)*5 
x2 = np.random.random(size=1000)*10 
x3 = np.random.random(size=1000)
x4 = np.random.normal(size=1000)*2 
x5 = np.random.random(size=1000)*60 
x = np.transpose([x1, x2, x3, x4, x5])
y = np.dot(x, beta)  + np.random.normal(scale=0.5, size=1000)
#data = dict(x=x, y=y)
data = dict(x1=x1, x2=x2, x3=x3, x4=x4, x5=x5, y=y)
df = pd.DataFrame(data)

with pm.Model() as model_robust:
    family = pm.glm.families.Normal()
    pm.glm.glm('y ~ x1+x2+x3+x4+x5', df, family=family)
    start = pm.find_MAP()
    step = pm.NUTS(scaling=start)
    #step = pm.Metropolis()
    trace_robust = pm.sample(25000, step)
# show results
pm.traceplot(trace_robust[2000:])
plt.show()
print("true parameter values are:", beta)
pm.df_summary(trace_robust[2000:])

output_13_0.png

true parameter values are: [1.2, 0.5, 9.8, 0.2, 1.08]
mean sd mc_error hpd_2.5 hpd_97.5
Intercept 0.041924 0.059770 0.000737 -0.080421 0.154130
x1 1.199973 0.011466 0.000106 1.177061 1.222395
x2 0.494488 0.005656 0.000053 0.483624 0.505661
x3 9.699889 0.056527 0.000484 9.587273 9.809352
x4 0.197271 0.008424 0.000052 0.181196 0.214320
x5 1.081120 0.000922 0.000008 1.079339 1.082917
sd 0.514316 0.011438 0.000067 0.492296 0.536947

おわりに

短いコードで書けるので特に解説はしませんでしたが、どれもうまくパラメータを推定できていることがわかります。
もし何か質問があれば遠慮なくコメントください。

(追記) 出力画像が間違っていたので修正。

7
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
12