数据分析一般分为探索性数据分析、验证型数据分析和预测型数据分析。上一篇讲了如何用Python实现验证型数据分析(假设检验),文章链接:转变:用Python如何实现“假设检验”zhuanlan.zhihu.com
本文对Python进行预测型数据分析用到的基本机器学习算法和流程进行了简要总结。
预测型数据分析用到的基本机器学习算法思维导图:
本文采用鸢尾花数据集进行预测型数据分析算法实践
导入鸢尾花数据集
import pandas as pd
iris = pd.read_csv('iris.csv')
iris.sample(5)
import seaborn as sns
%matplotlib inline
sns.regplot(x='petal_length', y='petal_width',data=iris)
#花瓣长度、花瓣宽度线性回归拟合图
1. 线性回归
目标变量y是一个数值连续型变量
单个特征构造模型
# scikit-learn 常用机器学习库 简写 sklearn
#利用花瓣长度预测花瓣宽度
from sklearn import linear_model
lm=linear_model.LinearRegression()
features=['petal_length']
X = iris[features]
y = iris['petal_width']
print(X.shape,y.shape)
model = lm.fit(X,y)
print(model.intercept_, model.coef_)
# y= -0.36 + 0.416*x
(150, 1) (150,)
-0.36651404521672704 [0.41641913]
model.predict(4)
array([1.29916248])
交叉检验评估模型
from sklearn.model_selection import cross_val_score
#线性回归打分函数(分数越高,性能越好)
#neg_mean_absolute_error 是 y-y'的绝对值和的平均
#neg_mean_squared_error 是 (y-y')平方和的平均
# cv = 5 表示 5次交叉检验 (常用5次)
#误差函数越大,模型越不好,所以Python自动在前面加 - 号, 越大表示误差越小,模型越好
scores = -cross_val_score(lm, X, y, cv=5, scoring='neg_mean_absolute_error')
print(scores)
#得到 5次交叉检验的绝对值误差
[0.08581817 0.09533821 0.13792154 0.2074044 0.29904645]
#一般用5次的平均值作为衡量模型好坏的指标
import numpy as np
print(np.mean(scores))
0.1651057510041143
scores = - cross_val_score(lm, X, y, cv=5, scoring='neg_mean_squared_error')
print(np.mean(scores))
0.04548097902618274
多个特征构造模型
#两个特征构造模型
features=['petal_length','sepal_length']
X = iris[features]
y = iris['petal_width']
# print(X.shape,y.shape)
model = lm.fit(X,y)
print(model.intercept_, model.coef_)
-0.013852011013002707 [ 0.44992999 -0.08190841]
#评估模型
scores = - cross_val_score(lm, X, y, cv=5, scoring='neg_mean_squared_error')
print(np.mean(scores))
0.0460189525758428
加入花萼长度特征预测花瓣宽度,误差为0.0460,比单独用花瓣长度预测花瓣宽度得到模型的误差0.0454还大 说明没有单独用花瓣长度预测花瓣宽度更合适
#三个特征构造模型并评估模型
features=['petal_length','sepal_length','sepal_width']
X = iris[features]
y = iris['petal_width']
model = lm.fit(X,y)
scores = - cross_val_score(lm, X, y, cv=5, scoring='neg_mean_squared_error')
print(np.mean(scores))
0.03950366038609736
加入花萼长度、花萼宽度用三个特征一起预测花瓣宽度,误差为0.039,又比单独用花瓣长度预测花瓣宽度更合适。
利用建立的模型预测
#一组变量预测(变量参数必须横向传入,单组变量默认列存储,所以需要转置)
list = [4,3,2]
pred_x = pd.DataFrame(list).T
print(pred_x)
pred_y = lm.predict(pred_x)
print(pred_y)
print(type(pred_y))
0 1 2
0 4 3 2
[1.68236958]
#多组变量预测
list2=[[4,3,2],[3,1,5]]
pred_x = pd.DataFrame(list2, columns = ['x1','x2','x3'])
print(pred_x)
pred_y = pd.DataFrame(lm.predict(pred_x), columns = ['y'])
print(pred_y)
print(type(pred_y))
x1 x2 x3
0 4 3 2
1 3 1 5
y
0 1.682370
1 2.263156
#合并输入特征和目标变量预测结果
result = pd.concat([pred_x, pred_y], axis = 1)
print(result)
x1 x2 x3 y
0 4 3 2 1.682370
1 3 1 5 2.263156
2. 分类
目标变量y是一个类别离散型变量
分类经典方法:逻辑回归
# scikit-learn 逻辑回归
from sklearn import linear_model
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import LabelEncoder
将类别目标变量 species 由字符串类型转换为数值类型
#第一种方法,采用 LabelEncoder 函数
le = LabelEncoder() #初始化类实例
le.fit(iris['species']) #遍历一遍属性
y = le.transform(iris['species']) #类别转换成非字符串
print(y)
print(type(y))
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
#第二种方法,采用DataFrame的 map 函数, 利用字典映射
species_dict={
'setosa': 0,
'versicolor': 1,
'virginica': 2
}
y1 = iris.species.map(species_dict)
print(y1.sample(10))
52 1
51 1
41 0
2 0
12 0
79 1
21 0
17 0
106 2
20 0
Name: species, dtype: int64
#构造模型
model_log = linear_model.LogisticRegression()
features=['petal_length']
X = iris[features]
model_log.fit(X,y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)
#评估模型
features=['petal_length']
X = iris[features]
scores = cross_val_score(model_log, X, y, cv=5, scoring='accuracy') #accuracy表示属于某个类别的准确率
# scores = cross_val_score(model_log, X, y1, cv=5, scoring='accuracy') #用 y 或者 y1 得到评分一样
print(np.mean(scores))
0.7866666666666667
#增加特征,查看评分
features=['petal_length','petal_width']
X = iris[features]
# X1=iris[['petal_length','petal_width']] # X 和 X1 结果相同
scores = cross_val_score(model_log, X, y, cv=5, scoring='accuracy')
print(np.mean(scores))
0.8666666666666668
#3个特征
features=['petal_length','petal_width','sepal_length','sepal_width']
X = iris[features]
scores = cross_val_score(model_log, X, y, cv=5, scoring='accuracy')
print(np.mean(scores))
0.9600000000000002
属性(特征)参与越多类别识别概率越高
3. k 近邻(分类和回归都可以用)
from sklearn import neighbors
3.1 k近邻分类
features=['petal_length','petal_width']
X = iris[features]
le = LabelEncoder()
le.fit(iris['species'])
y = le.transform(iris['species'])
knn = neighbors.KNeighborsClassifier(5, weights='uniform')
knn.fit(X,y)
scores = cross_val_score(knn, X, y, cv=5, scoring='accuracy')
print(np.mean(scores))
0.9666666666666668
3.2 k近邻回归
features=['petal_length','sepal_length','sepal_width']
X = iris[features]
y = iris['petal_width']
knn = neighbors.KNeighborsRegressor(5, weights='uniform')
knn.fit(X,y)
scores = -cross_val_score(knn, X, y, cv=5, scoring='neg_mean_squared_error')
print(np.mean(scores))
0.04143999999999999
通过参数调整,可以在交叉检验评分上获得更好的结果;那么是不是真的到预测的时候会更好呢也是说不准; 但至少通过参数的调整尽量保证有更大的概率,在未来真正的测试样例上能得到准确的结果。
4. 决策树(分类和回归都可以用)
4.1 决策树分类
from sklearn import tree
features=['petal_length','petal_width']
X = iris[features]
le = LabelEncoder()
le.fit(iris['species'])
y = le.transform(iris['species'])
dt = tree.DecisionTreeClassifier()
dt.fit(X,y)
scores = cross_val_score(dt, X, y, cv=5, scoring='accuracy')
print(np.mean(scores))
0.9533333333333334
4.2 决策树回归
features=['petal_length','sepal_length','sepal_width']
X = iris[features]
y = iris['petal_width']
dt = tree.DecisionTreeRegressor()
dt.fit(X,y)
scores = -cross_val_score(dt, X, y, cv=5, scoring='neg_mean_squared_error')
print(np.mean(scores))
0.06644999999999998
5. 随机森林(分类和回归都可以用)
5.1 随机森林分类
from sklearn import ensemble
features=['petal_length','petal_width']
X = iris[features]
le = LabelEncoder()
le.fit(iris['species'])
y = le.transform(iris['species'])
rf = ensemble.RandomForestClassifier(10)
rf.fit(X,y)
scores = cross_val_score(rf, X, y, cv=5, scoring='accuracy')
print(np.mean(scores))
0.9666666666666666
5.2 随机森林回归
features=['petal_length','sepal_length','sepal_width']
X = iris[features]
y = iris['petal_width']
rf = ensemble.RandomForestRegressor(10)
rf.fit(X,y)
scores = -cross_val_score(rf, X, y, cv=5, scoring='neg_mean_squared_error')
print(np.mean(scores))
0.042581507592592605
随机森林的计算时间和决策树(基分类器)的数量成正比,但是基分类器的数量和结果准确率却不一定成正比; 随机森林一般能得到:一个可能要挑到的特别合适的单个模型的结果差不多能达到的那个程度; 如果挑到的单个模型不合适这个数据集,就会比随机森林差很多,所以随机森林可以作为一开始尝试数据集,查看至少可以达到怎样的分析结果和预测性能, 如果挑的单个模型比随机森林还差,就需要好好分析下,看看能不能找到更合适的一些模型。
6. 聚类
属于非监督学习,不是预测目标变量(聚类问题只有X,没有y);而是在样本中寻找自然集群,比如顾客细分、文章聚类等 分类:事先已知存在哪些类别 聚类:事先不知存在哪些类别
尝试利用鸢尾花花瓣、花萼长度、宽度特征对其进行聚类
#先根据花瓣长度、花瓣宽度,画出实际的鸢尾花类别图
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
g = sns.FacetGrid(iris,hue='species')
g.map(plt.scatter,'petal_width', 'petal_length').add_legend()
从图可以看出,橙黄色和绿色边界上的点利用聚类方法可能不是很好找;
下面利用聚类方法进行分类,看看预测的类别和实际类别差别。
6.1 K均值聚类算法
from sklearn.cluster import KMeans
features=['petal_length','petal_width']
X = iris[features]
首先设置 K值为2,假定是两类
km = KMeans(2) #建立模型
km.fit(X) #训练特征获得模型
iris['cluster_k2'] = km.predict(X) #利用模型对特征向量进行预测
g = sns.FacetGrid(iris,hue='cluster_k2')
g.map(plt.scatter,'petal_width', 'petal_length').add_legend()
可以看出通过KMeans方法,K值为2找出了两类,但是类别1中有一个点还是计算错了;
由于KMeans算法具有随机性,再运行一次0、1标签可能有变化。
设置 K值为3,假定是三类
km = KMeans(3) #建立模型
km.fit(X) #训练特征
iris['cluster_k3'] = km.predict(X) #利用predict()方法获取fit训练那部分点的用KMeans算法分完类的结果
g = sns.FacetGrid(iris,hue='cluster_k3')
g.map(plt.scatter,'petal_width', 'petal_length').add_legend()
可以看出,设定K=3, 0 类别很好找出来了,但是 1 和 2 类别边界的有些点还是没有很好区分出来。 说明即使不知道鸢尾花真实类别情况下,我们有可能得到准确结果,预测鸢尾花类别。
K-Means算法针对“质心”去计算一个距离,最后结果是同类别的点都在一个圆形或者球形范围内; 但是并不是所有的类别表现形式都是球形或者圆形,对这样类别K-Means算法表现就比较差,我们可以用DBSCAN算法。
6.2 DBSCAN聚类算法
#生成数据点
from sklearn import datasets
from pandas import DataFrame
noisy_circles=datasets.make_circles(n_samples=1000,factor=.5,noise=.05)
df=DataFrame()
df['x1']=noisy_circles[0][:,0]
df['x2']=noisy_circles[0][:,1]
df['label']=noisy_circles[1]
df.sample(5)
#画出实际类别图
g = sns.FacetGrid(df,hue='label')
g.map(plt.scatter,'x1', 'x2').add_legend()
#K-Means算法聚类
from sklearn.cluster import KMeans
km = KMeans(2)
X=df[['x1','x2']]
km.fit(X)
df['label_KMeans'] = km.predict(X)
g = sns.FacetGrid(df,hue='label_KMeans')
g.map(plt.scatter,'x1', 'x2').add_legend()
可见KMeans算法对这类问题效果很差。
#DBSCAN算法聚类
from sklearn.cluster import DBSCAN
km = DBSCAN(eps = 0.1, min_samples = 10) # eps=0.1
X=df[['x1','x2']]
km.fit(X)
df['label_DBSCAN'] = km.labels_
# DBSCAN算法用 ‘labels_’方法(KMeans算法用 predict()方法)获取fit训练那部分点的用DBSCAN算法分完类的结果
g = sns.FacetGrid(df,hue='label_DBSCAN')
g.map(plt.scatter,'x1', 'x2').add_legend()
from sklearn.cluster import DBSCAN
km = DBSCAN(eps = 0.2, min_samples = 10) # eps=0.2
X=df[['x1','x2']]
km.fit(X)
df['label_DBSCAN'] = km.labels_
g = sns.FacetGrid(df,hue='label_DBSCAN')
g.map(plt.scatter,'x1', 'x2').add_legend()
from sklearn.cluster import DBSCAN
km = DBSCAN(eps = 0.3, min_samples = 10) # eps=0.3
X=df[['x1','x2']]
km.fit(X)
df['label_DBSCAN'] = km.labels_
g = sns.FacetGrid(df,hue='label_DBSCAN')
g.map(plt.scatter,'x1', 'x2').add_legend()
只有eps取0.2时才能获得满意的聚类结果,所以DBSCAN算法对参数非常敏感,要多次调整参数才可能得到满意的结果。
预测型数据分析需要注意:对于监督学习而言,我们希望模型对于未知数据的泛化能力强,所以就需要模型验证这一过程来体现不同的模型对于未知数据的表现效果;
最先我们用训练准确度(用全部数据进行训练和测试)来衡量模型的表现,这种方法会导致模型过拟合;为了解决这一问题,我们所有数据分成训练集和测试集两部分,我们用训练集进行模型训练,得到的模型再用测试集来衡量模型的预测表现能力;交叉检验通过多次拆分数据集进行训练、测试;
交叉检验可用于调节模型参数,用于模型选择,用于特征选择;
预测型数据分析,特征选择优化占据大部分时间,它是提高预测准确率的重要步骤,本文没有讲述特征选择,特征选择属于“特征工程”,后面有机会再进行总结。