Python Statsmodels 统计包之 OLS 回归

JoinQuant

1 年前

Statsmodels 是 Python 中一个强大的统计分析包,包含了回归分析、时间序列分析、假设检

验等等的功能。Statsmodels 在计量的简便性上是远远不及 Stata 等软件的,但它的优点在于可以与 Python 的其他的任务(如 NumPy、Pandas)有效结合,提高工作效率。在本文中,我们重点介绍最回归分析中最常用的 OLS(ordinary least square)功能。

当你需要在 Python 中进行回归分析时……

import statsmodels.api as sm!!!

在一切开始之前

上帝导入了 NumPy(大家都叫它囊派?我叫它囊辟),

import numpy as np

便有了时间。

上帝导入了 matplotlib,

import matplotlib.pyplot as plt

便有了空间。

上帝导入了 Statsmodels,

import statsmodels.api as sm

世界开始了。

简单 OLS 回归

假设我们有回归模型

Y=β0 β1X1 ⋯ βnXn ε,

并且有 k 组数据

。OLS 回归用于计算回归系数 βi 的估值 b0,b1,…,bn,使误差平方

最小化。

statsmodels.OLS 的输入有 (endog, exog, missing, hasconst) 四个,我们现在只考虑前两个。第一个输入 endog 是回归中的反应变量(也称因变量),是上面模型中的 y(t), 输入是一个长度为 k 的 array。第二个输入 exog 则是回归变量(也称自变量)的值,即模型中的x1(t),…,xn(t)。但是要注意,statsmodels.OLS 不会假设回归模型有常数项,所以我们应该假设模型是

并且在数据中,对于所有 t=1,…,k,设置 x0(t)=1。因此,exog的输入是一个 k×(n 1) 的 array,其中最左一列的数值全为 1。往往输入的数据中,没有专门的数值全为1的一列,Statmodels 有直接解决这个问题的函数:sm.add_constant()。它会在一个 array 左侧加上一列 1。(本文中所有输入 array 的情况也可以使用同等的 list、pd.Series 或 pd.DataFrame。)

确切地说,statsmodels.OLS 是 statsmodels.regression.linear_model 里的一个函数(从这个命名也能看出,statsmodel 有很多很多功能,其中的一项叫回归)。它的输出结果是一个 statsmodels.regression.linear_model.OLS,只是一个类,并没有进行任何运算。在 OLS 的模型之上调用拟合函数 fit(),才进行回归运算,并且得到 statsmodels.regression.linear_model.RegressionResultsWrapper,它包含了这组数据进行回归拟合的结果摘要。调用 params 可以查看计算出的回归系数 b0,b1,…,bn。

简单的线性回归

上面的介绍绕了一个大圈圈,现在我们来看一个例子,假设回归公式是:

我们从最简单的一元模型开始,虚构一组数据。首先设定数据量,也就是上面的 k 值。

nsample = 100

然后创建一个 array,是上面的 x1 的数据。这里,我们想要 x1 的值从 0 到 10 等差排列。

x = np.linspace(0, 10, nsample)

使用 sm.add_constant() 在 array 上加入一列常项1。

X = sm.add_constant(x)

然后设置模型里的 β0,β1,这里要设置成 1,10。

beta = np.array([1, 10])

然后还要在数据中加上误差项,所以生成一个长度为k的正态分布样本。

e = np.random.normal(size=nsample)

由此,我们生成反应项 y(t)。

y = np.dot(X, beta) e

好嘞,在反应变量和回归变量上使用 OLS() 函数。

model = sm.OLS(y,X)

然后获取拟合结果。

results = model.fit()

再调取计算出的回归系数。

print(results.params)

得到

[ 1.04510666, 9.97239799]

和实际的回归系数非常接近。

当然,也可以将回归拟合的摘要全部打印出来。

print(results.summary())

得到

中间偏下的 coef 列就是计算出的回归系数。

我们还可以将拟合结果画出来。先调用拟合结果的 fittedvalues 得到拟合的 y 值。

y_fitted = results.fittedvalues

然后使用 matplotlib.pyploft 画图。首先设定图轴,图片大小为 8×6。

fig, ax = plt.subplots(figsize=(8,6))

画出原数据,图像为圆点,默认颜色为蓝。

ax.plot(x, y, 'o', label='data')

画出拟合数据,图像为红色带点间断线。

ax.plot(x, y_fitted, 'r--.',label='OLS')

放置注解。

ax.legend(loc='best')

得到

在大图中看不清细节,我们在 0 到 2 的区间放大一下,可以见数据和拟合的关系。

加入改变坐标轴区间的指令

ax.axis((-0.05, 2, -1, 25))

高次模型的回归

假设反应变量 Y 和回归变量 X 的关系是高次的多项式,即

我们依然可以使用 OLS 进行线性回归。但前提条件是,我们必须知道 X 在这个关系中的所有次方数;比如,如果这个公式里有一个 (x)^2.5项,但我们对此并不知道,那么用线性回归的方法就不能得到准确的拟合。

虽然 X 和 Y 的关系不是线性的,但是 Y 和

的关系是高元线性的。也就是说,只要我们把高次项当做其他的自变量,即设

那么,对于线性公式

可以进行常规的 OLS 回归,估测出每一个回归系数 βi。可以理解为把一元非线性的问题映射到高元,从而变成一个线性关系。

下面以

为例做一次演示。

首先设定数据量,也就是上面的 k 值。

nsample = 100

然后创建一个 array,是上面的 x1 的数据。这里,我们想要 x1 的值从 0 到 10 等差排列。

x = np.linspace(0, 10, nsample)

再创建一个 k×2 的 array,两列分别为 x1 和 x2。我们需要 x2 为 x1 的平方。

X = np.column_stack((x, x**2))

使用 sm.add_constant() 在 array 上加入一列常项 1。

X = sm.add_constant(X)

然后设置模型里的 β0,β1,β2,我们想设置成 1,0.1,10。

beta = np.array([1, 0.1, 10])

然后还要在数据中加上误差项,所以生成一个长度为k的正态分布样本。

e = np.random.normal(size=nsample)

由此,我们生成反应项 y(t),它与 x1(t) 是二次多项式关系。

y = np.dot(X, beta) e

在反应变量和回归变量上使用 OLS() 函数。

model = sm.OLS(y,X)

然后获取拟合结果。

results = model.fit()

再调取计算出的回归系数。

print(results.params)

得到

[ 0.95119465, 0.10235581, 9.9998477]

获取全部摘要

print(results.summary())

得到

拟合结果图如下

在横轴的 [0,2] 区间放大,可以看到

哑变量

一般而言,有连续取值的变量叫做连续变量,它们的取值可以是任何的实数,或者是某一区间里的任何实数,比如股价、时间、身高。但有些性质不是连续的,只有有限个取值的可能性,一般是用于分辨类别,比如性别、婚姻情况、股票所属行业,表达这些变量叫做分类变量。在回归分析中,我们需要将分类变量转化为哑变量(dummy variable)。

如果我们想表达一个有 d 种取值的分类变量,那么它所对应的哑变量的取值是一个 d 元组(可以看成一个长度为 d 的向量),其中有一个元素为 1,其他都是 0。元素呈现出 1 的位置就是变量所取的类别。比如说,某个分类变量的取值是 {a,b,c,d},那么类别 a 对应的哑变量是(1,0,0,0),b 对应 (0,1,0,0),c 对应 (0,0,1,0),d 对应 (0,0,0,1)。这么做的用处是,假如 a、b、c、d 四种情况分别对应四个系数 β0,β1,β2,β3,设 (x0,x1,x2,x3) 是一个取值所对应的哑变量,那么

可以直接得出相应的系数。可以理解为,分类变量的取值本身只是分类,无法构成任何线性关系,但是若映射到高元的 0,1 点上,便可以用线性关系表达,从而进行回归。

Statsmodels 里有一个函数 categorical() 可以直接把类别 {0,1,…,d-1} 转换成所对应的元组。确切地说,sm.categorical() 的输入有 (data, col, dictnames, drop) 四个。其中,data 是一个 k×1 或 k×2 的 array,其中记录每一个样本的分类变量取值。drop 是一个 Bool值,意义为是否在输出中丢掉样本变量的值。中间两个输入可以不用在意。这个函数的输出是一个k×d 的 array(如果 drop=False,则是k×(d 1)),其中每一行是所对应的样本的哑变量;这里 d 是 data 中分类变量的类别总数。

我们来举一个例子。这里假设一个反应变量 Y 对应连续自变量 X 和一个分类变量 Z。常项系数为 10,XX 的系数为 1;Z 有 {a,b,c}三个种类,其中 a 类有系数 1,b 类有系数 3,c 类有系数 8。也就是说,将 Z 转换为哑变量 (Z1,Z2,Z3),其中 Zi 取值于 0,1,有线性公式

可以用常规的方法进行 OLS 回归。

我们按照这个关系生成一组数据来做一次演示。先定义样本数量为 50。

nsample = 50

设定分类变量的 array。前 20 个样本分类为 a。

groups = np.zeros(nsample, int)

之后的 20 个样本分类为 b。

groups[20:40] = 1

最后 10 个是 c 类。

groups[40:] = 2

转变成哑变量。

dummy = sm.categorical(groups, drop=True)

创建一组连续变量,是 50 个从 0 到 20 递增的值。

x = np.linspace(0, 20, nsample)

将连续变量和哑变量的 array 合并,并加上一列常项。

X = np.column_stack((x, dummy))

X = sm.add_constant(X)

定义回归系数。我们想设定常项系数为 10,唯一的连续变量的系数为 1,并且分类变量的三种分类 a、b、c 的系数分别为 1,3,8。

beta = [10, 1, 1, 3, 8]

再生成一个正态分布的噪音样本。

e = np.random.normal(size=nsample)

最后,生成反映变量。

y = np.dot(X, beta) e

得到了虚构数据后,放入 OLS 模型并进行拟合运算。

result = sm.OLS(y,X).fit()

print(result.summary())

得到

再画图出来

fig, ax = plt.subplots(figsize=(8,6))

ax.plot(x, y, 'o', label='data')

ax.plot(x, result.fittedvalues, 'r--.', label='OLS')

ax.legend(loc='best')

得出

这里要指出,哑变量是和其他自变量并行的影响因素,也就是说,哑变量和原先的 x 同时影响了回归的结果。初学者往往会误解这一点,认为哑变量是一个选择变量:也就是说,上图中给出的回归结果,是在只做了一次回归的情况下完成的,而不是分成3段进行3次回归。哑变量的取值藏在其他的三个维度中。可以理解成:上图其实是将高元的回归结果映射到平面上之后得到的图。

简单应用

我们来做一个非常简单的实际应用。设 x 为上证指数的日收益率,y 为深证成指的日收益率。通过对股票市场的认知,我们认为 x 和 y 有很强的线性关系。因此可以假设模型

并使用 Statsmodels 包进行 OLS 回归分析。

我们取上证指数和深证成指一年中的收盘价。

data = get_price(['000001.XSHG', '399001.XSHE'], start_date='2015-01-01', end_date='2016-01-01', frequency='daily', fields=['close'])['close']

x_price = data['000001.XSHG'].values

y_price = data['399001.XSHE'].values

计算两个指数一年内的日收益率,记载于 x_pct 和 y_pct 两个 list 中。

x_pct, y_pct = [], []

for i in range(1, len(x_price)):

x_pct.append(x_price[i]/x_price[i-1]-1)

for i in range(1, len(y_price)):

y_pct.append(y_price[i]/y_price[i-1]-1)

将数据转化为 array 的形式;不要忘记添加常数项。

x = np.array(x_pct)

X = sm.add_constant(x)

y = np.array(y_pct)

上吧,λu.λv.(sm.OLS(u,v).fit())!全靠你了!

results = sm.OLS(y, X).fit()

print(results.summary())

得到

恩,y=0.002 0.9991x,合情合理,或者干脆直接四舍五入到 y=x。最后,画出数据和拟合线。

fig, ax = plt.subplots(figsize=(8,6))

ax.plot(x, y, 'o', label='data')

ax.plot(x, results.fittedvalues, 'r--', label='OLS')

ax.legend(loc='best')

结语

本篇文章中,我们介绍了 Statsmodels 中很常用 OLS 回归功能,并展示了一些使用方法。线性回归的应用场景非常广泛。在我们量化课堂应用类的内容中,也有相当多的策略内容采用线性回归的内容。我们会将应用类文章中涉及线性回归的部分加上链接,链接到本篇文章中来,形成体系。量化课堂在未来还会介绍 Statsmodel 包其他的一些功能,敬请期待。

python 量化投资 长期横盘_python量化投资才是最正确的方式,只教方法,不股荐!...相关推荐

  1. python量化策略源码_Python量化交易进阶讲堂-创建自定义量化回测框架

    欢迎大家订阅<Python实战-构建基于股票的量化交易系统>小册子,小册子会陆续推出与小册内容相关的专栏文章,对涉及到的知识点进行更全面的扩展介绍,并且会有选择地收录至小册中,更便于广大读 ...

  2. python不同时间周期k线_Python量化交易基础讲堂-股票分笔数据跨周期处理

    <Python实战-构建基于股票的量化交易系统>小册子主要侧重于 Python 实战讲解,但在内容设计上提供了前置基础章节帮助读者快速掌握基础工具的使用.同时我们会持续更新一些关于Pyth ...

  3. python存储对象的数组_Python:在2d数组中存储对象并调用其方法

    我正在尝试制作一个象棋应用程序.代码如下:#file containing pieces classes class Piece(object):` name = "piece" ...

  4. python数据类型怎么定义_Python的五大数据类型的作用、定义方式、使用方法

    一.简述Python的五大数据类型的作用.定义方式.使用方法: 1. 数字类型int: 1.整形 作用:可以表示人的年龄,身份证号码,身高和体重等 定义方式: weight = 130 print(w ...

  5. python时间序列平稳性检验_Python量化投资基础:时间序列的平稳性检验

    主要内容: 1. 自相关性和自相关系数 2. 强平稳和弱平稳 3. Python平稳性检验实战 重要性:10分 (1-10). 时间序列数据的平稳性对于我们采用什么样的分析方式.选择什么样的模型有着至 ...

  6. python量化交易策略实例_python量化交易策略入门(一):MACD的威力

    最近刚开始学习量化交易,在聚宽网上看了几篇教程,对操作流程有了大致的了解,接下来打算好好研究一下交易策略. 据大奖章基金的Simons透露,他那个每年收益率20%以上的系统,一点都不费解(compli ...

  7. python量化交易的框架_python量化交易框架easyquant试用体会

    在github上发现一个python写的,看上去简单实用的量化交易框架easyquant,作者是在他写的easytrader上实现了自动读取行情和交易登入,初步试验了雪球登入,效果还不错. 安装这个框 ...

  8. python量化交易策略实例_Python量化实例 – 基于股票的金融数据量化分析

    说明:本文只是通过自己的已学知识对 一.分析目的 利用预先设定的策略,通过对股票交易的历史数据进行回测,验证该策略是否能指导股票交易. 二.数据处理 1.数据集描述 数据集简介:此数据集来源于Nasd ...

  9. python量化交易学习笔记_Python量化交易学习笔记(45)——深度学习挖短线股5

    前4篇文章分别记录了利用深度学习挖短线股的数据预处理.模型训练.结果预测及策略回测过程,本文记录根据筛选短线股票的过程. 选股流程 1.股票数据下载更新 例如现在是2020年11月23日19:00,我 ...

  10. python在坐标轴上画矩形_Python使用matplotlib实现在坐标系中画一个矩形的方法

    本文实例讲述了Python使用matplotlib实现在坐标系中画一个矩形的方法.分享给大家供大家参考.具体实现方法如下: import matplotlib.pyplot as plt from m ...

最新文章

  1. Caffe中对cifar10执行train操作
  2. 谈一谈算法工程师的落地能力
  3. 个性化推荐系统研究热点之用户画像
  4. nodejs模块加载分析(1).md
  5. 9款Android经常使用的高速开发框架
  6. svn自动同步更新脚本(windows)
  7. 杭电oj2043密码
  8. php5.4与php5.2,升级php 5.2.14 到5.4.11版本报错问题
  9. Django上传文件,制作文件上传按钮,form上传文件
  10. 电脑剪贴板在哪里打开_如何把在公司电脑上复制的内容,粘贴到家里的电脑?超好用!...
  11. access数据库窗体设计实验报告_来自窗体控件的数值条件(VBA)
  12. Code First 下自动更新数据库结构(Automatic Migrations)
  13. struts.xml头文件
  14. 2008R2Win7管理八DNS新功能及常规管理
  15. m6000查看端口状态_M6000日常维护命令
  16. Excel VBA 学习总结 - 基础知识
  17. 如何在WPS里添加字体?
  18. XTU 1236 Fibonacci
  19. 已知一/27网络中有一个地址是167.199.170.82,问这个网络的网络掩码、网络前缀长度和网络后缀长度是多少,求这个地址块的地址数、首地址以及末地址是多少
  20. 基金经理学量化(Python+AI)

热门文章

  1. Linux查找文件内容的常用命令方法
  2. 微软半日游,和CSDN同学们走进名企
  3. 不差钱!华为,给学生开百万年薪
  4. JN5169 ZigBee 3.0 开发环境搭建
  5. 我用Python分析1585家电商车厘子销售数据,发现这些秘密
  6. Python基础之文件操作
  7. 图灵机器人官网 java_图灵机器人-Java/Android
  8. c++数组、结构体数组和对象数组的初始化方式
  9. 关于Microsoft office深色模式设置
  10. QGIS3.20 制作栅格动画