第13章 Python建模库介绍 本章会回顾⼀些pandas的特点,这有利于pandas数据规整和模型拟合和评分。然后简短介绍两个流⾏的建模⼯具,statsmodels和scikit-learn。
13.1 pandas与模型代码的接⼝ 模型开发的通常⼯作流是使⽤pandas进⾏数据加载和清洗,然后切换到建模库进⾏建模。
pandas与其它分析库通常是靠NumPy的数组联系起来的。将DataFrame转换为NumPy数组,可以使⽤.values属性 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 import numpy as npimport pandas as pddata = pd.DataFrame({ 'x0' : [1 , 2 , 3 , 4 , 5 ], 'x1' : [0.01 , -0.01 , 0.25 , -4.1 , 0. ], 'y' : [-1.5 , 0. , 3.6 , 1.3 , -2. ]}) print (data)print (data.columns)print (data.values)print (type (data.values))model_cols = ['x0' , 'x1' ] print (data.loc[:, model_cols].values)print (type (data.loc[:, model_cols].values))df2 = pd.DataFrame(data.values, columns=['one' , 'two' , 'three' ]) print (df2)
虚变量增加与删除:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import numpy as npimport pandas as pddata = pd.DataFrame({ 'x0' : [1 , 2 , 3 , 4 , 5 ], 'x1' : [0.01 , -0.01 , 0.25 , -4.1 , 0. ], 'y' : [-1.5 , 0. , 3.6 , 1.3 , -2. ]}) data['category' ] = pd.Categorical(['a' , 'b' , 'a' , 'a' , 'b' ],categories=['a' , 'b' ]) print (data)dummies = pd.get_dummies(data.category, prefix='category' ) data_with_dummies = data.drop('category' , axis=1 ).join(dummies) print (data_with_dummies)
13.2 ⽤Patsy创建模型描述 Patsy是Python的⼀个库,使⽤简短的字符串“公式语法”描述统计模型(尤其是线性模型)
Patsy适合描述statsmodels的线性模型,Patsy的公式是⼀个特殊的字符串语法
y ~ x0 + x1被称为公式字符串
. a+b不是将a与b相加的意思,⽽是为模型创建的设计矩阵 。
patsy.dmatrices函数接收⼀个公式字符串
和⼀个数据集
(可以是DataFrame或数组的字典),为线性模型创建设计矩阵:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import numpy as npimport pandas as pdimport patsydata = pd.DataFrame({ 'x0' : [1 , 2 , 3 , 4 , 5 ], 'x1' : [0.01 , -0.01 , 0.25 , -4.1 , 0. ], 'y' : [-1.5 , 0. , 3.6 , 1.3 , -2. ]}) print (data)y, X = patsy.dmatrices('y ~ x0 + x1' , data) print (y)print (X)print (type (y))print (type (X))
这些Patsy的DesignMatrix实例实际上是NumPy的ndarray
,带有附加元数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 print (np.asarray(y))print (np.asarray(X))print (type (np.asarray(y)))print (type (np.asarray(X)))
就像刚刚说的,X会出现一个Intercept列: 这是线性模型(⽐如普通最⼩⼆乘法)的惯例⽤法。添加 +0 到模型可以不显示intercept:
1 2 3 4 5 6 7 8 9 10 11 print (patsy.dmatrices('y ~ x0 + x1 + 0' , data)[1 ])
Patsy对象可以直接传递到算法(⽐如numpy.linalg.lstsq)中,它执⾏普通最⼩⼆乘回归. 模型的元数据保留在design_info属性中,因此你可以重新附加列名到拟合系数,以获得⼀个Series
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 coef, resid, _, _ = np.linalg.lstsq(X, y) print (coef)coef = pd.Series(coef.squeeze(), index=X.design_info.column_names) print (coef)
⽤Patsy公式进⾏数据转换
可以将Python代码与patsy公式结合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import numpy as npimport pandas as pdimport patsydata = pd.DataFrame({ 'x0' : [1 , 2 , 3 , 4 , 5 ], 'x1' : [0.01 , -0.01 , 0.25 , -4.1 , 0. ], 'y' : [-1.5 , 0. , 3.6 , 1.3 , -2. ]}) print (data)y, X = patsy.dmatrices('y ~ x0 + np.log(np.abs(x1) + 1)' , data) print (X)y, X = patsy.dmatrices('y ~ standardize(x0) + center(x1)' , data) print (X)
patsy.build_design_matrices函数可以应⽤于转换新数据,使⽤原始样本数据集的保存信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 new_data = pd.DataFrame({ 'x0' : [6 , 7 , 8 , 9 ], 'x1' : [3.1 , -0.5 , 0 , 2.3 ], 'y' : [1 , 2 , 3 , 4 ]}) new_X = patsy.build_design_matrices([X.design_info], new_data) print (new_X)
因为Patsy中的加号不是加法的意义,当你按照名称将数据集的列相加时,你必须⽤特殊I函数将它们封装起来:
1 2 3 4 5 6 7 8 9 10 11 12 y, X = patsy.dmatrices('y ~ I(x0 + x1)' , data) print (X)
分类数据和Patsy
当你在Patsy公式中使⽤⾮数值数据,它们会默认转换为虚变 量。如果有截距,会去掉⼀个,避免共线性 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import pandas as pdimport numpy as npimport patsydata = pd.DataFrame({ 'key1' : ['a' , 'a' , 'b' , 'b' , 'a' , 'b' , 'a' , 'b' ], 'key2' : [0 , 1 , 0 , 1 , 0 , 1 , 0 , 0 ], 'v1' : [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ], 'v2' : [-1 , 0 , 2.5 , -0.5 , 4.0 , -1.2 , 0.2 , -1.7 ] }) y, X = patsy.dmatrices('v2 ~ key1' , data) print (X)
如果从模型中忽略截距,每个分类值的列都会包括在设计矩阵的模型中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import pandas as pdimport numpy as npimport patsydata = pd.DataFrame({ 'key1' : ['a' , 'a' , 'b' , 'b' , 'a' , 'b' , 'a' , 'b' ], 'key2' : [0 , 1 , 0 , 1 , 0 , 1 , 0 , 0 ], 'v1' : [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ], 'v2' : [-1 , 0 , 2.5 , -0.5 , 4.0 , -1.2 , 0.2 , -1.7 ] }) y, X = patsy.dmatrices('v2 ~ key1 + 0' , data) print (X)y, X = patsy.dmatrices('v2 ~ C(key2)' , data) print (X)
当你在模型中使⽤多个分类名,事情就会变复杂,因为会包括key1:key2形式的相交部分,它可以⽤在⽅差(ANOVA)模型分析中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import pandas as pdimport numpy as npimport patsydata = pd.DataFrame({ 'key1' : ['a' , 'a' , 'b' , 'b' , 'a' , 'b' , 'a' , 'b' ], 'key2' : [0 , 1 , 0 , 1 , 0 , 1 , 0 , 0 ], 'v1' : [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ], 'v2' : [-1 , 0 , 2.5 , -0.5 , 4.0 , -1.2 , 0.2 , -1.7 ] }) data['key2' ] = data['key2' ].map ({0 : 'zero' , 1 : 'one' }) print (data)y, X = patsy.dmatrices('v2 ~ key1 + key2' , data) print (X)y, X = patsy.dmatrices('v2 ~ key1 + key2 + key1:key2' , data) print (X)
13.3 statsmodels介绍 statsmodels是Python进⾏拟合多种统计模型、进⾏统计试验和数据探索可视化的库。Statsmodels包含许多经典的统计⽅法,但没有⻉叶斯⽅法和机器学习模型。
statsmodels包含的模型有:
线性模型,⼴义线性模型和健壮线性模型
线性混合效应模型
⽅差(ANOVA)⽅法分析
时间序列过程和状态空间模型
⼴义矩估计
估计线性模型
statsmodels的线性模型有两种不同的接⼝:基于数组,和基于公式。它们可以通过API模块引⼊:
1 2 import statsmodels.api as smimport statsmodels.formula.api as smf
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 import pandas as pdimport numpy as npimport statsmodels.api as smimport statsmodels.formula.api as smfdef dnorm (mean, variance, size=1 ): if isinstance (size, int ): size = size, return mean + np.sqrt(variance) * np.random.randn(*size) np.random.seed(12345 ) N = 100 X = np.c_[dnorm(0 , 0.4 , size=N), dnorm(0 , 0.6 , size=N), dnorm(0 , 0.2 , size=N)] eps = dnorm(0 , 0.1 , size=N) beta = [0.1 , 0.3 , 0.5 ] y = np.dot(X, beta) + eps print (X[:5 ])print (y[:5 ])X_model = sm.add_constant(X) print (X_model[:5 ])model = sm.OLS(y, X) results = model.fit() print (results.params)print (results.summary())data = pd.DataFrame(X, columns=['col0' , 'col1' , 'col2' ]) data['y' ] = y print (data[:5 ])results = smf.ols('y ~ col0 + col1 + col2' , data=data).fit() print (results.params)print (results.tvalues)print (results.predict(data[:5 ]))
估计时间序列过程
statsmodels的另⼀模型类是进⾏时间序列分析,包括⾃回归过程、卡尔曼滤波和其它态空间模型,和多元⾃回归模型。
⽤⾃回归结构和噪声来模拟⼀些时间序列数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import randomimport pandas as pdimport numpy as npimport statsmodels.api as smimport statsmodels.formula.api as smfdef dnorm (mean, variance, size=1 ): if isinstance (size, int ): size = size, return mean + np.sqrt(variance) * np.random.randn(*size) np.random.seed(12345 ) init_x = 4 values = [init_x, init_x] N = 1000 b0 = 0.8 b1 = -0.4 noise = dnorm(0 , 0.1 , N) for i in range (N): new_x = values[-1 ] * b0 + values[-2 ] * b1 + noise[i] values.append(new_x) MAXLAGS = 5 model = sm.tsa.AR(values) results = model.fit(MAXLAGS) print (results.params)
13.4 scikit-learn介绍 scikit-learn是⼀个⼴泛使⽤、⽤途多样的Python机器学习库。 它包含多种标准监督和⾮监督机器学习⽅法和模型选择和评估、数据转换、数据加载和模型持久化⼯具。 这些模型可以⽤于分类、聚合、预测和其它任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 import numpy as npimport pandas as pdtrain = pd.read_csv('datasets/titanic/train.csv' ) test = pd.read_csv('datasets/titanic/test.csv' ) print (train[:4 ])print (train.isnull().sum ())print (test.isnull().sum ())impute_value = train['Age' ].median() train['Age' ] = train['Age' ].fillna(impute_value) test['Age' ] = test['Age' ].fillna(impute_value) train['IsFemale' ] = (train['Sex' ] == 'female' ).astype(int ) test['IsFemale' ] = (test['Sex' ] == 'female' ).astype(int ) predictors = ['Pclass' , 'IsFemale' , 'Age' ] X_train = train[predictors].values X_test = test[predictors].values y_train = train['Survived' ].values print (X_train[:5 ])print (y_train[:5 ])from sklearn.linear_model import LogisticRegressionmodel = LogisticRegression() print (model.fit(X_train, y_train))y_predict = model.predict(X_test) print (y_predict[:10 ])from sklearn.linear_model import LogisticRegressionCVmodel_cv = LogisticRegressionCV(10 ) print (model_cv.fit(X_train, y_train))from sklearn.model_selection import cross_val_scoremodel = LogisticRegression(C=10 ) scores = cross_val_score(model, X_train, y_train, cv=4 ) print (scores)