Bootstrap

00_00 python机器学习_各章实例代码汇总(随学习进度更新)

文章目录


代码整理

以下例子均来自于本人博客,如果有内容看不懂的请转自具体章节自行查看.

第一章 入门

001 散点图 + K邻近算法 + 模型训练

# 多维数组基础包
import numpy as np
# 数据表格
import pandas as pd
# 颜色方案
import mglearn
# 绘图
import matplotlib.pyplot as pt
# 机器学习 数据模型
from sklearn.datasets import load_iris
# 机器学习 数据分离
from sklearn.model_selection import train_test_split
# 机器学习 K临近算法
from sklearn.neighbors import KNeighborsClassifier

# 使用数据训练库里的实例数据
iris_dataset = load_iris()

# 将训练数据打乱,分成两份 75% 25%, 
# X_train y_train 为训练数据和训练结果
# Y_train y_test 为测试数据和测试结果
X_train, X_test, y_train, y_test = \
    train_test_split(iris_dataset['data'],
                     iris_dataset['target'], random_state=0)
# 创建数据表单
iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)

# 使用数据表单创建散点图
# c
#		color的意思, 这里用的是纯数字数组, 每个数字是一种颜色
#   y_train是想面数据拆分后 75%的训练数据[X_train]的结果部分, 因为我们传入的iris_dataframe是
#   用X_train做成的,所以必须用y_train来表示每个训练数据的结果
# cmap
#		color map配色方案, 就像是各种主题都有一种配色方案,选哪一种就在这里指定
#    	>>> import mglearn
#		>>> mglearn.cm3
#		<matplotlib.colors.ListedColormap object at 0x0000021B59E339A0>
# marker
#		散点图中用什么图标来表示结果
# s
#		图标的大小 Size
# hist_kwds={'bins': 20}
#		bins表示柱状图, 20表示柱状图的个数
# 	生成的图片中,对角线上的图都是柱状图,其他的为散点图
grr = pd.plotting.scatter_matrix(iris_dataframe, c=y_train, figsize=(15, 15), marker='0',
                                 hist_kwds={'bins': 20}, s=60, alpha=.8, cmap=mglearn.cm3)

# 需要看散点图就把这行去掉这行注释
# 显示散点图
# pt.show()

# 初始化K邻近算法类
knn = KNeighborsClassifier(n_neighbors=1)
# 使用训练数据设置模型
knn.fit(X_train, y_train)

# 通过录入方式简单测试一下模型
X_new = np.array([[6.3, 2.5, 5., 1.9]])
# 使用模型创建模型输出的训练数据
prediction = knn.predict(X_new)
print(prediction)
print(iris_dataset['target_names'][prediction])

# 使用模型创建模型输出的训练数据
y_pred = knn.predict(X_test)
# 比对模型输出的训练数据和实际分离训练数据之间的相似度
result = np.mean(y_pred == y_test)
print(result)

第二章 监督学习

001 创建两个画布, 分别设置画布大小,然后绘图

import matplotlib.pyplot as plt

# 设置好画布大小
plt.figure(figsize=(6, 3))
# 创建画布
fig = plt.figure(1)
# 添加图形 指定位置 x:2 Y:1 index:1
sp1 = fig.add_subplot(211)
# 填充图形数据
sp1.plot([1, 2, 3])

# 同上
plt.figure(figsize=(6, 3))
fig2 = plt.figure(2)
sp2 = fig2.add_subplot(211)
sp2.plot([4, 5, 6])

plt.show()

002 K邻近算法分类 绘制散点图的决策边界

# 绘图包
import matplotlib.pyplot as plt
# 配色方案包,这里有一些简单的数据样本可以直接使用
# 我理解: 因为重点是图形表示,所以使用图形包中的数据样本能来的快些,因此没有使用sklearn.datasets的数据
import mglearn
# K邻近算法
from sklearn.neighbors import KNeighborsClassifier

# 实例话数据源
x, y = mglearn.datasets.make_forge()

# 创建画布 fig
# 创建多个绘画 axes
fig, axes = plt.subplots(1, 3, figsize=(10, 3))

for n_neighbors, ax in zip([1, 3, 9], axes):
    # 创建K邻近算法,喂好数据
    clf = KNeighborsClassifier(n_neighbors=n_neighbors).fit(x, y)
    # 创建图层,喂好数据,在图层上显示决策边界
    mglearn.plots.plot_2d_separator(clf, x, fill=True, eps=0.5, ax=ax, alpha=.4)
    # 绘制散点图
    mglearn.discrete_scatter(x[:, 0],x[:, 1], y, ax=ax)
    # 设置单个绘图的标题
    ax.set_title("{} neighbors".format(n_neighbors))
    # 设置单个绘图的横轴的表示内容
    ax.set_xlabel("feature 0")
    # 设置单个绘图的纵轴的表示内容
    ax.set_ylabel("feature 1")

# 设置实例说明的位置  ['lower left'      3]
axes[0].legend(loc=3)

# 显示画布
plt.show()

002 K邻近算法回归 绘制趋势图

import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsRegressor

# 通过绘图保重中的波形函数创建简单的数据集
X, y = mglearn.datasets.make_wave(n_samples=40)
# 使用机器学习中函数将数据分离成训练数据和测试数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 创建一个画布, 1*3个绘图, 设置画布大小 1500*400 像素
fig, axes = plt.subplots(1, 3, figsize=(15, 4))
# 用多维数组函数包创建正宗的多维数组, 设置好取值范围和项目个数,然后将数组格式化一下
line = np.linspace(-3, 3, 1000).reshape(-1, 1)
# 绑定各个绘画所持有的neighbor数
for n_neighbor, ax in zip([1,3,9], axes):
    # 创建基于波形的K邻近算法对象
    reg = KNeighborsRegressor(n_neighbors=n_neighbor)
    # 喂训练数据
    reg.fit(X_train, y_train)
    # 在当前绘图上绘制第一个图像, 使用创建的1000个新数据测试
    ax.plot(line, reg.predict(line))
    # 在当前绘图上绘制第一个图像, 使用的是原始建模用的数据
    ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8)
    # 在当前绘图上绘制第一个图像, 使用的是原始测试用数据
    ax.plot(X_test, y_test, '^', c=mglearn.cm2(1), markersize=8)
    # 设置当绘图的标题及横纵坐标
    ax.set_title("Nighbors:{}".format(n_neighbor))
    ax.set_xlabel('Feature')
    ax.set_ylabel('Target')

# 指定在第一个绘图上添加图标说明, 使用best来让程序自己找合适的位置来显示说明内容
axes[0].legend(["predict", "train", "test"], loc="best")
# 显示画布
plt.show()

003 线性模型 线性回归[LinearRegression]处理多特征数据

# 线性模型
from sklearn.linear_model import LinearRegression
# 波士顿房价数据 [多特征]
from mglearn.datasets import load_extended_boston
# 机器学习 数据分离
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 获得样本数据
X, y = load_extended_boston()
# 分离数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 喂数据
lr = LinearRegression().fit(X_train, y_train)
# 用训练数据查看结果
plt.plot(X_train, lr.predict(X_train))
# 显示画布
plt.show()

003 线性模型 岭回归[Ridge L2]处理多特征数据

# 线性模型
from sklearn.linear_model import Ridge
# 波士顿房价数据 [多特征]
from mglearn.datasets import load_extended_boston
# 机器学习 数据分离
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 获得样本数据
X, y = load_extended_boston()
# 分离数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
# 喂数据
# alpha为损失系数默认值就是1, 损失系数越大训练模型精度越低,泛化能力越强
# 损失系数越小就相当于约接近 LinearRegression模型
lr = Ridge(alpha=1.0).fit(X_train, y_train)
# 用训练数据查看结果
plt.plot(X_train, lr.predict(X_train))
# 显示画布
plt.show()

003 线性模型 岭回归[Ridge L2] 参数alpha如何影响模型

# 线性模型
from sklearn.linear_model import Ridge
from sklearn.linear_model import LinearRegression
# 波士顿房价数据 [多特征]
from mglearn.datasets import load_extended_boston
# 机器学习 数据分离
from sklearn.model_selection import train_test_split
# 绘图包
import matplotlib.pyplot as plt

# 获得样本数据
X, y = load_extended_boston()
# 分离数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 创建4组模型
ridge_01 = Ridge(alpha=0.1).fit(X_train, y_train)
ridge_1 = Ridge(alpha=1).fit(X_train, y_train)
ridge_10 = Ridge(alpha=10).fit(X_train, y_train)
lr = LinearRegression().fit(X_train, y_train)

# 开始作画 分别打印4个模型中对各个特征的参数的计算值
# 之前的例子一直是分别设置横轴和纵轴的数据
# 而这里只有一个数组,原因是后台会用数组的下标位置作为横轴,值作为纵轴自动画图
plt.plot(ridge_01.coef_, 's', label='alpha 0.1')
plt.plot(ridge_1.coef_, '^', label='alpha 1')
plt.plot(ridge_10.coef_, 'v', label='alpha 10')
plt.plot(lr.coef_, 'o', label='LinearRegression')

# 设置绘图的横纵坐标显示内容
plt.xlabel = 'coefficient index'
plt.ylabel = 'coefficient magnitude'

# 设置一条横线 第一参数是横线的纵轴位置, 第二参数表示横线的开始位置, 第三参数表示横线的结束位置
plt.hlines(0, 0, len(lr.coef_))
# 设置画布y轴能表示的数据区间
plt.ylim(-25, 25)

plt.legend()
# 绘图
plt.show()

004 线性模型 [Ridge L1] 处理回归多特征数据

# 绘图包
import matplotlib.pyplot as plt
# 采用L1正则化算法的线性模型
from sklearn.linear_model import Lasso
# 数据分离
from sklearn.model_selection import train_test_split
# 数据样本
from mglearn.datasets import load_extended_boston

# 取数据
X, y = load_extended_boston()
# 分离训练数据和测试数据
X_train, X_test, y_train, y_test = train_test_split(X, y)
# 实例化Lasso模型并喂数据
lasso = Lasso(alpha=0.01).fit(X_train, y_train)
# 绘制训练数据的训练得分
plt.plot(X_test, lasso.predict(X_test))
# 显示绘图
plt.show()

005 线性模型[LogisticRegression, LinearSVC]处理分类问题

# 数据源
import mglearn
# 绘图
import matplotlib.pyplot as plt
# 线性模型
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC

# 取得数据
# make_forge 产生俩特征数据样本
X, y = mglearn.datasets.make_forge()
# 创建一个画布, 连个绘画
flg, axes = plt.subplots(1,2, figsize=(10, 3))

for model, ax in zip([LinearSVC(), LogisticRegression()], axes):
    # 填充数据给当前的绘画对象
    clf = model.fit(X, y)
    # 为了便于表示,绘出决策边界,这个在前面系说过参数,这里就不再复述
    mglearn.plots.plot_2d_classification(clf, X, fill=False, eps=0.5, ax=ax, alpha=0.7)
    # 在图层上绘制散点图
    # 第一参数为横坐标,第二参数为纵坐标,
    # 第三参数去重后决定marker图标的个数 及不同种类图标的默认表示
    mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
    # 设置绘画
    ax.set_title("{}".format(clf.__class__.__name__))
    ax.set_xlabel("feature 0")
    ax.set_ylabel("feature 1")

# 在绘图1上绘制图例
axes[0].legend()

# 显示画布
plt.show()

005 线性模型处理分类问题时C对模型的影响

# 绘图
import matplotlib.pyplot as plt
# 线性模型
from sklearn.linear_model import LogisticRegression
# 数据样本
from sklearn.datasets import load_breast_cancer
# 数据样本分离
from sklearn.model_selection import train_test_split

#############################
# 数据准备
#############################
# 获得数据样本对象
cancer = load_breast_cancer()
# 分离训练数据和测试数据,
# 分离后数据各种类比例与原始数据样本相同
# 加入随机种子保证每次的执行结果都不同
X_train, X_test, y_train, y_test = \
    train_test_split(cancer.data, cancer.target,
                     stratify=cancer.target, random_state=42)

#############################
# 创建模型
#############################
# 创建3组线性模型,分别指定不懂的C用于观察C的影响 默认使用l2正则
lr1 = LogisticRegression(C=1).fit(X_train, y_train)
lr100 = LogisticRegression(C=100).fit(X_train, y_train)
lr001 = LogisticRegression(C=0.01).fit(X_train, y_train)

# 创建3组线性模型,分别指定不懂的C用于观察C的影响 使用l1正则
# penalty
#   正则模式  默认使用l2正则, 可以指定l1正则
# solver
#   当指定L2正则的时候, 默认的分离是不支持L2正则的,需要改变分类器的类型
lr1_1 = LogisticRegression(
    C=1, penalty='l1', solver='liblinear').fit(X_train, y_train)
lr100_1 = LogisticRegression(
    C=100, penalty='l1', solver='liblinear').fit(X_train, y_train)
lr001_1 = LogisticRegression(
    C=0.01, penalty='l1', solver='liblinear').fit(X_train, y_train)

#############################
# 绘图
#############################
# 绘制线性模型图片,使用默认图标
# T是方法表示反转数组  (1,3) -> (3,1)
# l2正则化
plt.plot(lr1.coef_.T, label="C=1")
plt.plot(lr100.coef_.T, label="C=100")
plt.plot(lr001.coef_.T, label="C=0.01")

# l1正则化
plt.plot(lr1_1.coef_.T, label="C=1 l1")
plt.plot(lr100_1.coef_.T, label="C=100 l1")
plt.plot(lr001_1.coef_.T, label="C=0.01 l1")

# 横轴标记函数
# 第一参数表示横轴标记的位置
# 第二参数表示横轴各标记位置需要显示的label名
# 第三参数表示90旋转后显示label
plt.xticks(range(X_train.data.shape[1]), cancer.feature_names, rotation=90)
# 绘制一条水平线,用于观察各C模型结果的浮动情况
# 横线在纵轴的0,横轴的0开始到数据特征个数长度终了
plt.hlines(0, 0, X_train.shape[1])

# 下面的是图层的通用设置
plt.ylabel("coefficient magnitude")
plt.xlabel("coefficient index")
plt.legend(loc='best')

#############################
# 显示
#############################
# 显示画布
plt.show()

005 线性模型处理多分类

import mglearn
import numpy as np
import matplotlib.pyplot as plt
# 聚类数据样本
from sklearn.datasets import make_blobs
# 线性模型中处理分类问题的包
from sklearn.svm import LinearSVC

#############################
# 数据准备
#############################
# 生成数据样本
X, y = make_blobs(random_state=42)

#############################
# 生成算法模型
#############################
svm_liner = LinearSVC().fit(X, y)

#############################
# 绘制不同分类的决策边界
#############################
mglearn.plots.plot_2d_classification(svm_liner, X, fill=True, alpha=.7)

#############################
# 绘制散点图
#############################
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)

#############################
# 绘制分类线
#############################
# 生成一个含有50个元素且有效至大的一维数组
# 做成元素个数50是默认值, 需要改变的话设置num参数就行.
line = np.linspace(-15, 15)
# svm_liner.coef_: 特征系数
#       在回归模型中这个是每个特征的系数, 而且是一维数组
#       在分类模型中由于是每个特征分别于其它特征去做"一对其余"的比较方式,
#           因此有几个分类就会有几组系数
#               In[24]: set(y)
#               Out[24]: {0, 1, 2}
#               In[25]: X.shape
#               Out[25]: (100, 2)
#           从上述结果中可以看出来结果一共分为3部分,一次coef_因该是shape(3,2)
#               In [26]: svm_liner.coef_.shape
#               Out[26]: (3, 2)
#
# svm_liner.intercept_: 截距, 叫做偏移值能更好理解
#       In [28]: svm_liner.intercept_
#       Out[28]: array([-1.07745804,  0.13140619, -0.08605017])
#       因为有3组系数,因此也有针对3个特征系数的截距n
#       下面公式的分解分析:
#       正则化公式是从讲线性模型开始就接触的
#           特征1 * 特征系数1 + .... 特征n * 特征系数n + 截距
#       例子中line的数据格式是 (50,)说明有50个样本,且1个特征
#           50个样本: line
#           当前类别特征为: coef[0]
#       带入公式为 line * coef[0] + intercept 用于表示当前类别得分
#       由于处理的是分类问题, coef[1]表示其他类别,为了求出当前类别与其它类别的比率因此
#       当前类别/其他类别 即: (line * coef[0] + intercept) / coef[1]
#       至于减号"-"嘛,  斜率公式 k=-a/b, 公式要求
for coef, intercept in zip(svm_liner.coef_, svm_liner.intercept_):
    plt.plot(line, -(line * coef[0] + intercept) / coef[1])


plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.legend(loc='best')

plt.show()

006 决策树 决策树得分

# 查看决策树得分

# 绘图
import matplotlib.pyplot as plt
# 数据样本
from sklearn.datasets import load_breast_cancer
# 数据分离
from sklearn.model_selection import train_test_split
# 决策树模型 预剪枝
from sklearn.tree import DecisionTreeClassifier

# 获得数据
cancer = load_breast_cancer()
# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=42)

# 使用默认决策树
tree_default = DecisionTreeClassifier()
# 使用深度为4的决策树
tree_deep4 = DecisionTreeClassifier(max_depth=4, random_state=0)

# 喂数据
tree_default.fit(X_train, y_train)
tree_deep4.fit(X_train, y_train)

# 记录各模型得分
tree_default_train_score = tree_default.score(X_train, y_train)
tree_default_test_score = tree_default.score(X_test, y_test)
tree_deep4_train_score = tree_deep4.score(X_train, y_train)
tree_deep4_test_score = tree_deep4.score(X_test, y_test)

# 绘制图片
plt.plot(0, tree_default_train_score, marker='^', label="default train")
plt.plot(0, tree_default_test_score, marker='^', label="default test")
plt.plot(0, tree_deep4_train_score, marker='^', label="deep4 train")
plt.plot(0, tree_deep4_test_score, marker='^', label="deep4 test")

# 绘制图标
plt.legend(loc='best')

# 显示画布
plt.show()

006 分析决策树

# 分析决策树

# 绘图
import matplotlib.pyplot as plt
# 数据样本
from sklearn.datasets import load_breast_cancer
# 数据分离
from sklearn.model_selection import train_test_split
# 决策树模型 预剪枝
from sklearn.tree import DecisionTreeClassifier
from sklearn.tree import export_graphviz

dot_file = r"D:\999_Temp\999_Tmp\tree.dot"
pdf_file = r"D:\999_Temp\999_Tmp\tree"


# 获得数据
cancer = load_breast_cancer()
# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=42)

# 使用默认决策树
tree_default = DecisionTreeClassifier()

# 喂数据
tree_default.fit(X_train, y_train)

# 生成分析报告
# out_file
#       做成的分析报告名
# class_names
#       决策各分支的名, 决策分支个数应当与数据样本的个数一样
# feature_names
#       特征名
# impurity
#       True表示显示各个决策含有的杂志率
# filled
#       填充颜色,用于演示的话建议打开能好看点.
export_graphviz(tree_default, out_file=dot_file, class_names=["malignat", "begin"],
                feature_names=cancer.feature_names, impurity=True, filled=True)


#############################
# 用python查看dot文件方法
#############################
# 思路: dot文件转存成PDf文件
import graphviz
with open(dot_file) as f:
    dot_grap = f.read()
# 读取dot并转换
# 因为这个不是重点,所以不深入研究,会用就行
gh = graphviz.Source(dot_grap)
gh.render(pdf_file)

006 决策树如何处理多分类问题

# 分析决策树 处理多分类

#############
# 数据样本 
#############
# In[30]: lr.data.shape
# Out[30]: (150, 4)
# In[31]: lr.target_names
# Out[31]: array(['setosa', 'versicolor', 'virginica'], dtype='<U10')
from sklearn.datasets import load_iris

#############
# 决策树算法模型 预剪枝
#############
from sklearn.tree import DecisionTreeClassifier

#############
# 数据转换
#############
# 将算法数据导出绘图数据
from sklearn.tree import export_graphviz
# 绘图数据编辑
import graphviz

#############
# 内存
#############
# 使用内存来完成算法与绘图的转换
from io import StringIO

#############
# 图像处理
#############
# 读入图片
from imageio import imread

#############
# 绘图
#############
# 显示图片
import matplotlib.pyplot as plt

# 转话后数据的保存位置
fout = r"D:\999_Temp\999_Tmp\tree_iris"

# 获得数据
lr = load_iris()

# 使用默认决策树
tree_default = DecisionTreeClassifier()

# 喂数据
tree_default.fit(lr.data, lr.target)

# 生成分析报告
# 算法数据写入到out_file接口中
# out_file
#       做成的分析报告名
# class_names
#       决策各分支的名, 决策分支是boolean类型的因此最多只有俩
# feature_names
#       特征名
# impurity
#       True表示显示各个决策含有的杂志率
# filled
#       填充颜色,用于演示的话建议打开能好看点.
dot_data = StringIO()
export_graphviz(tree_default, out_file=dot_data, class_names=["1", "2", '3'],
                feature_names=lr.feature_names, impurity=True, filled=True)
data = dot_data.getvalue()

# 格式话算法数据
graph = graphviz.Source(data, format="png")
# 导出格式化后的值
graph.render(fout)

# 读入图片数据,设置到当前画布中
plt.imshow(imread(fout + ".png"))

# 显示画布
plt.show()


# 画布中有多个绘图的时候像下面这样设置就可以
# plt.figure()   # 画布初始化
# ax = plt.gca()  # GCF: Get Current Figure"   GCA: "Get Current Axes"
# ax.imshow(imread(fout + ".png"))
# plt.show()

006 决策树对测试数据的预测能力

# 决策树对测试数据的预测能力如何
# 思路:
# 1.将数据样以2000年为界限划分成训练数据和测试数据
# 2.用训练数据分别训练决策树模型和线性回归模型
# 3.为确认模型泛化能力,将含有训练数据和测试数据的完整数据样本应用于数据模型
# 4.用直接计算而不是模型计算的结果先绘制出预想图形
# 5.分别将两个模型计算结果绘制成图形
# 6.分别比较两个模型的预测结果和实际图形的差别
# 即:
# module(X_train, 10^y_train).predit(X).exp()  == y


# 数据表单处理库
import pandas as pd
# 多维数组处理库
import numpy as np
# 绘图库
import matplotlib.pyplot as plt
# 机器学习线性回归模型
from sklearn.linear_model import LinearRegression
# 机器学习决策树模型
from sklearn.tree import DecisionTreeRegressor

###################
# 数据准备
###################

# 数据样本位置
csv_data = r'D:\001_Work\002_DevelopSource\Python_Project\python_numpy\.venv\Lib\site-packages\mglearn\data\ram_price.csv'

# csv -> array
# In [5]: ram_prices
#    Out[5]:
#        Unnamed: 0     date         price
#    0             0  1957.00  4.110418e+08
#    1             1  1959.00  6.794772e+07
#    ......
ram_prices = pd.read_csv(csv_data)

# 数据拆分
data_train = ram_prices[ram_prices.date < 2000]
data_test = ram_prices[ram_prices.date >= 2000]

# 取得数据样本种关于日期的那一列的值
dates = data_train.date

# 将数组新追加一个维度, 使一维数组变成二维数组
# 将原有数据的任何一个数据都放到新维度数组的一维位置
#   In [19]: data_train.date[:3]
#   Out[19]:
#   0    1957.0
#   1    1959.0
#   2    1960.0
#   In[20]: data_train.date[:, np.newaxis][:3]
#   Out[20]:
#   array([[1957.],
#          [1959.],
#          [1960.]])
X_train = dates[:, np.newaxis]

# np.log  幂运算 底数为10
# 求 a^b 的运算叫乘方运算,运算的结果叫幂  a为底数, b为真数
y_train = np.log(data_train.price)

###################
# 建模
###################
# 决策树建模
tree = DecisionTreeRegressor().fit(X_train, y_train)
# 线性回归模型建模
linear = LinearRegression().fit(X_train, y_train)

###################
# 模型验证
###################
# 将数据样本中的所有时间数据取出,重新格式化成二维数组,
# 将时间数据的每个数据重置在新数据的一维位置
X_all = ram_prices.date[:, np.newaxis]

# 记录通过训练数据做成的决策树模型对真个数据样本的预测得分
pred_tree = tree.predict(X_all)
# 同理,纪律线性模型的预测得分
pred_lr = linear.predict(X_all)

# 将模型预测出来的得分做开方运算, 为了确认能否得到数据样本中相同的价格
# a^b 中,求 a 的逆运算叫开方运算 ,求 b 的逆运算叫对数运算。
price_tree = np.exp(pred_tree)
price_lr = np.exp(pred_lr)

# semilogy函数是对y坐标点取常用对数(底为10)后生成的对数坐标函数。
# 因为模型预测值都是幂运算结果,因此绘图时也是用幂运算结果表示才更能看清结果
# semilogy 帮助文档:
# https://ww2.mathworks.cn/help/matlab/ref/semilogx.html?s_tid=srchtitle_semilogx_1
# 使用原始数据样本2000年以前的数据不通过决策树模型直接直接算出结果并绘制线的前半段
plt.semilogy(data_train.date, data_train.price, label='Train data')
# 使用原始数据样本2000年以后的数据不通过决策树模型直接直接算出结果并绘制线的后半段
plt.semilogy(data_test.date, data_test.price, label='Train test')
# 用2000年以前数据训练的决策树模型尝试分析数据全体并绘制完整线
plt.semilogy(ram_prices.date, price_tree, label='Tree prediction')
# 同上,使用线性模型来绘制
plt.semilogy(ram_prices.date, price_lr, label='Linear prediction')

# 绘制图标
plt.legend()
# 显示画布
plt.show()

007 决策树集合 随机森林


# 绘图相关
import mglearn
import matplotlib.pyplot as plt
# 决策树集合
from sklearn.ensemble import RandomForestClassifier
# 2分类数据集
from sklearn.datasets import make_moons
# 数据分离
from sklearn.model_selection import train_test_split

#############
# 数据样本
#############
# 创建数据样本
# noise
#   增加噪点, 增加数据样本的难度
X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
# 差分数据
X_train, X_test, y_train, y_test = \
    train_test_split(X,y,stratify=y,random_state=42)

#############
# 数据模型
#############
# 创建决策树集合
# n_estimators
#   决策树中树的个数
forest = RandomForestClassifier(n_estimators=5, random_state=2)
# 向决策树集合模型喂数据
forest.fit(X_train, y_train)

#############
# 绘图
#############
# 在一张画布上创建一个 2 * 3个 绘图, 并限定每个绘图的大小
fig, axes = plt.subplots(2, 3, figsize=(20, 30))
# 使用enumerate为每一次循环计数
# forest.estimators_ 带到一个arraylist里面包含5个实例化的树模型
# axes是 2 * 3 二维数组  axes.ravel()可以把多维数组转变成一维数组
# 结合上面信息,循环的目的就是每次得到一个绘图对象和它对应的树模型
for i, (ax, tree) in enumerate(zip(axes.ravel(), forest.estimators_)):
    # 设置每一个绘图对象的标题
    ax.set_title("Tree {}".format(i))
    # 在每个模型上用训练数据测试每个模型成果,同时画出决策边界
    mglearn.plots.plot_tree_partition(X_train, y_train, tree, ax=ax)

# 我们创建了6个绘图,据册数集合只使用了5个
# 在最后一个绘图上绘制决策树集合计算的最终的决策边界
mglearn.plots.plot_2d_separator(forest, X_train, fill=True, ax=axes[-1, -1], alpha=0.4)
# 在最后一个绘图上设置标题
axes[-1, -1].set_title("Random Forest")
# 在最后有一个绘图上绘制绘制散点图
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train)

# 显示画布
plt.show()

007 决策树集合 梯度提升决策树

# 决策树集合 梯度提升决策树

# 多维数据基础库
import numpy as np
# 绘图相关
import matplotlib.pyplot as plt
# 机器学习 数据分离
from sklearn.model_selection import train_test_split
# 机器学习 肿瘤数据样本
from sklearn.datasets import load_breast_cancer
# 梯度提升决策树
from sklearn.ensemble import GradientBoostingClassifier
# 随机森林
from sklearn.ensemble import RandomForestClassifier
# 决策树
from sklearn.tree import DecisionTreeClassifier

def plots_feature_importances_cancer(model, ax, str_x_label):
    """ 使用算法模型绘制标尺图

    Args:
        model (_type_): 算法模型
        ax (_type_): 单个绘图
    """
    # 获得特征个数
    n_features = cancer.data.shape[1]
    # 绘制标尺
    ax.barh(range(n_features), model.feature_importances_, align='center')
    # 设置Y轴标尺的表示内容
    ax.set_yticks(np.arange(n_features), cancer.feature_names)
    ax.set_xlabel(str_x_label)
    ax.set_ylabel("Feature")

# 获得肿瘤数据
cancer = load_breast_cancer()
# 数据分离
X_train, X_test, y_train, y_test = \
    train_test_split(cancer.data, cancer.target, random_state=0)

# 实例化决策树
tree = DecisionTreeClassifier(random_state=0)
# 实例化随机森林
random_trees = RandomForestClassifier(random_state=0)
# 实例化梯度提升决策树
gradient_trees = GradientBoostingClassifier(random_state=0, max_depth=1)

# 训练模型
tree.fit(X_train, y_train)
random_trees.fit(X_train, y_train)
gradient_trees.fit(X_train, y_train)

# 为了对比模型间差异,创建了3个绘图
fig, axes = plt.subplots(1, 3, figsize=(20, 30))

# 分别绘图
plots_feature_importances_cancer(tree, axes[0], 'tree')
plots_feature_importances_cancer(random_trees, axes[1], 'random tree')
plots_feature_importances_cancer(gradient_trees, axes[2], 'gradient boost')

# 显示画布
plt.show()


008 核支持向量机 简单了解

# 核支持向量机
# 共4个例子:
# 例子1: 线性模型&平面&分类
#   用于显示线性模型在平面上处理分类问题的不足.
# 例子2: 线性模型&3D空间&分类
#   在3D环境中观察线性模型处理多特征的效果
# 例子3: 线性模型&3D空间&分类&决策边界
#   演示在3D环境中如何正确施画决策边界
# 例子4: 线性模型&3D空间&分类&决策边界&超平面
#   演示如何在超平面中描述空间上的决策边界
import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.svm import LinearSVC
from mpl_toolkits.mplot3d import Axes3D

# 创建4分类聚类数据
X, y = make_blobs(centers=4, random_state=0)
# 将4分类强行转变成2分类,(数据样本不变只y是改变了最终分类)
y = y % 2

##################################
# 在第一个画布上用线性模型构建平面图像
##################################
# 2特征4分类数据样本散点图和决策边界
# 获得新画布
fig1 = plt.figure()
# 取得当前画布的绘图对象
ax1 = plt.gca()
# 创建线性模型
linear_svc = LinearSVC().fit(X, y)
# 散点图
mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax1, labels='LinearSVC')
# 平面决策边界
mglearn.plots.plot_2d_separator(linear_svc, X)


##################################
# 第二个画布上用线性模型构建3D图像
##################################
# 获得新画布
fig2 = plt.figure()
# 在新画布上创建3d绘图对象
# 参数: (这俩参数是控制表示角度的,不用记,用时自己调一调就行)
#   elev: 方位角
#   azim: 仰角
ax2 = Axes3D(fig2, azim=-158, elev=-176)
# 现在的数据不是只有两分类了吗,这里用了一个投机取巧的办法来区分这两类数据
# mask -> True 代表第一类数据
# ~mask -> False 代表第二类数据
mask = y == 0
# 为了填充3D模型定义Z轴, 在元数据样本上扩张了新的非线性特征
# 新特征使用第二特征的平方
X_new = np.hstack([X, X[:, 1:] ** 2])
# 在3D绘图对象上绘制第一分类数据的散点图
# 前三个参数位置分别代表 X轴, Y轴, Z轴
# c: color  b:blue, r:red
# cmap: color function   mglearn.cm2(绘图包提供的绘图解决方案)
# s: size
# marker: 图标
ax2.scatter(X_new[mask, 0], X_new[mask, 1],
            X_new[mask, 2], c='b', cmap=mglearn.cm2, s=60)
# 在3D绘图对象上绘制第二分类数据的散点图
ax2.scatter(X_new[~mask, 0], X_new[~mask, 1],
            X_new[~mask, 2], c='r', marker='^', cmap=mglearn.cm2, s=60)
ax2.set_xlabel('feature0')
ax2.set_ylabel('feature1')
ax2.set_zlabel('feature1 ** 2')

##################################
# 第三个画布上画出3D图像的决策边界
##################################
# 用3特征数据样本创建线性模型
linear_svm_3d = LinearSVC().fit(X_new, y)
# g(x) = w1x1 + w2x2 + w3x3 + w4x4 + w0
# coef 各个特征系数 w1~wn
# intercept: 模型偏移量 w0
coef, intercept = linear_svm_3d.coef_.ravel(), linear_svm_3d.intercept_
# 获得新画布
fig3 = plt.figure()
# 在新画布上创建3d绘图对象
ax3 = Axes3D(fig3, elev=-176, azim=-158)
# 创建50个连续点, 点的取值范围为: 数据样本第一特征最小值-2 ~ 最大值+2
# 这个2的偏移就是想让线把特征都包住
xx = np.linspace(X_new[:, 0].min()-2, X_new[:, 0].max()+2, 50)
yy = np.linspace(X_new[:, 1].min()-2, X_new[:, 1].max()+2, 50)
# 使用两个坐标轴上的点在平⾯上画格
# 一般用于3d图像的网格采样的数据源
XX, YY = np.meshgrid(xx, yy)

# 分子部分:
#   是线性方程计算公式即: 各特征*相应的特征系数之和 + 偏移量
# 分母部分:
#   Z轴系数
# 合在一起:
#   使用斜率公式求出网格线中每个点的空间高度即Z轴的位置
ZZ = (coef[0] * XX + coef[1] * YY + intercept) / -coef[2]
# 在3D绘图对象上绘制平面
# rstride: row stride 行间距
# cstride: column stride 列间距
# 含义查了半天都不如看一下代码来的直接
# def plot_surface(....)
#   row_inds = list(range(0, rows-1, rstride)) + [rows-1]
#   col_inds = list(range(0, cols-1, cstride)) + [cols-1]
# 看代码含义因该是在row或column范围内每个一定的步长抽一个点
# 因为抽取的范围是刨除掉边界点的,因此不影响大小,仅仅是为了少点计算量
ax3.plot_surface(XX, YY, ZZ, rstride=10, cstride=10, alpha=0.3)
# 在3D绘图对象上绘制第一分类数据的散点图
ax3.scatter(X_new[mask, 0], X_new[mask, 1], X_new[mask, 2], c='b',
            marker='^', cmap=mglearn.cm2, s=60)
# 在3D绘图对象上绘制第二分类数据的散点图
ax3.scatter(X_new[~mask, 0], X_new[~mask, 1], X_new[~mask, 2], c='r',
            marker='^', cmap=mglearn.cm2, s=60)
ax3.set_xlabel('feature0')
ax3.set_ylabel('feature1')
ax3.set_zlabel('feature1 ** 2')

##################################
# 第四个画布, 在超平面上绘制线性模型在3d空间中表示的决策边界
##################################
# 获得新画布
fig4 = plt.figure()
# 取得当前画布的绘图对象
ax4 = plt.gca()
# (1)新做成3D空间上的网格线 用新型模型进行分类,
# (2)分类产生的决策边界(3D空间)转换成各个超平面绘图数据
#   参数的shape必须与线性模型的shape一致因此需要在XX,YY基础上扩张出ZZ
#   ZZ的计算规则应与线性模型建模时的规则一致
# 什么是超平面?
#   https://baike.baidu.com/item/%E8%B6%85%E5%B9%B3%E9%9D%A2
ZZ_1 = YY ** 2
dec = linear_svm_3d.decision_function(
    np.c_[XX.ravel(), YY.ravel(), ZZ_1.ravel()])
# 绘制等高线图
#   采样上面产生的3D各个超平面的绘图数据,根据数据画出超平面轮廓
# 什么是等高线图?
#   https://baike.baidu.com/item/%E7%AD%89%E9%AB%98%E7%BA%BF/1697067?fr=aladdin
# contourf函数帮助:
#   https://ww2.mathworks.cn/help/matlab/ref/contourf.html#mw_e930b555-4fc5-4db1-8907-5d10f7aac1c0
# 前3个参数含义:  contourf(X,Y,Z) 指定 Z 中各值的 x 和 y 坐标。 等高线图的数据源
# level: 采样数据源那些位置用于绘制
ax4.contourf(XX, YY, dec.reshape(XX.shape), levels=[dec.min(), 0, dec.max()],
             cmap=mglearn.cm2, s=0.5)
# 绘制散点图
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
ax4.set_xlabel('feature0')
ax4.set_ylabel('feature1')

# 显示画布
plt.show()

008 核支持向量机 标记支持向量

import mglearn
from sklearn.svm import SVC
from matplotlib import pyplot as plt

# 生成快速演示数据 2分类2特征
X, y = mglearn.tools.make_handcrafted_dataset()
# kernel: 核函数
# kernel='rbf' 不设置默认也是这个, 高斯核,
# C 精确系数, 越大越精确, 泛化越弱
# gamma 高斯核宽度, gamma越小核半径越大,gamma越大核半径越小
#   核半径越大模型越简单,核半径越小模型越复杂
svm = SVC(kernel='rbf', C=10, gamma=0.1).fit(X, y)
mglearn.plots.plot_2d_separator(svm, X, eps=.5)
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
# 取得对决策边界有决定性作用的支持向量
sv = svm.support_vectors_
# 取得支持向量的参数重要度得分
sv_label = svm.dual_coef_.ravel() > 0
# 用散点图表示支持向量 size设置成15用于区分默认图标
mglearn.discrete_scatter(sv[:, 0], sv[:, 1], sv_label, s=15, markeredgewidth=3)
plt.xlabel('feature 0')
plt.ylabel('feature 1')
plt.show()

008 核支持向量机_参数C&gamma对模型影响

# 核支持向量机
# 快速演示 模型参数C, gamma对于模型的影响

import mglearn
from sklearn.svm import SVC
from matplotlib import pyplot as plt

# 生成 3 * 3 绘图对象
fig, axes = plt.subplots(3, 3, figsize=(15, 10))

# 设定每组绘图对象中C的取值
for ax, C in zip(axes, [-1, 0, 0]):
    # 设定每个绘图中gamma的取值
    for a, gamma in zip(ax, range(-1, 2)):
        # 这个就是为了快速演示用的模型
        # 传入的参数都被设定为 10为底,参数为指数来求幂
        # 另外数据源是自动做成的,不用另外做成
        mglearn.plots.plot_svm(log_C=C, log_gamma=gamma, ax=a)

# 设定说明
axes[0, 0].legend(['class 0', 'class 1', 'sv class 0', 'sv class 1'],
ncol=4, loc=(.9, 1.2))

# 显示画布
plt.show()

008 核支持向量机_数据缩放 归一化

from sklearn.svm import SVC
# 数据样本
from sklearn.datasets import load_breast_cancer
# 数据样本分离
from sklearn.model_selection import train_test_split

#############################
# 数据准备
#############################
# 获得数据样本对象
cancer = load_breast_cancer()
# 分离训练数据和测试数据,
# 分离后数据各种类比例与原始数据样本相同
# 加入随机种子保证每次的执行结果都不同
X_train, X_test, y_train, y_test = \
    train_test_split(cancer.data, cancer.target,
                     stratify=cancer.target, random_state=42)
#############################
# 模型1
#############################
svc1 = SVC().fit(X_train, y_train)
print("模型1训练得分: %s" % svc1.score(X_train, y_train))
print("模型1测试得分: %s" % svc1.score(X_test, y_test))

#############################
# 模型2 数据缩放
#############################
# 原理
# 1.求出各特征的最小值 array.min()
# 2.求出范围, 范围 = (用各特征 - (1)).max() 即各特征的范围
# 3.计算:  (数据各特征-(1)) / (2)   所以值一定在0~1之间

train_min = X_train.min(axis=0)
train_range = (X_train - train_min).max(axis=0)
train_compute = (X_train - train_min) / train_range
test_compute = (X_test - train_min) / train_range

svc2 = SVC().fit(train_compute, y_train)
# 这里需要注意下,因为已经改变了训练方式,因此在测试得分的时候应当使用同样rule的数据.
print("模型2训练得分: %s" % svc2.score(train_compute, y_train))
print("模型2测试得分: %s" % svc2.score(test_compute, y_test))

009 神经网络 快速演示

# 神经网络模型快速演示

import mglearn

# 快速演示返回的内容是dot文件
# 以后看到graphviz包直接联想dot就可以了
# 线性模型
gh1 = mglearn.plots.plot_logistic_regression_graph()
# 神经网络,多层感知机
gh2 = mglearn.plots.plot_single_hidden_layer_graph()

pdf1_file = r"D:\999_Temp\999_Tmp\line.dot"
pdf2_file = r"D:\999_Temp\999_Tmp\neural.dot"

# 需要将dot文件转换一下可以读的格式,默认是转成pdf
gh1.render(pdf1_file)
gh2.render(pdf2_file)

009 神经网络 重要参数

# 神经网络模型: 重要参数说明
# 概要:
#   数据源采用2分类数据源, 100各数据样本
#   创建两个画布
#       第一画布: 一个隐层 观察 10/100隐单元  [0.0001, 0.01, 0.1, 1]alpha 对于模型的影响
#       第二画布: 二个隐层 观察 10/100隐单元  [0.0001, 0.01, 0.1, 1]alpha 对于模型的影响

import mglearn
import matplotlib.pyplot as plt
from sklearn.neural_network import MLPClassifier
# 2分类数据集
from sklearn.datasets import make_moons
# 数据分离
from sklearn.model_selection import train_test_split

# 一个虚拟层
fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for axx, n_hidden_node in zip(axes, [10, 100]):
    for ax, alpha in zip(axx, [0.0001, 0.01, 0.1, 1]):
        mlp = MLPClassifier(solver='lbfgs', random_state=0,
                            hidden_layer_sizes=[n_hidden_node], alpha=alpha)

        #############
        # 数据样本
        #############
        # 创建数据样本
        # noise
        #   增加噪点, 增加数据样本的难度
        X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
        # 差分数据
        X_train, X_test, y_train, y_test = \
            train_test_split(X, y, stratify=y, random_state=42)

        mlp.fit(X_train, y_train)
        mglearn.plots.plot_2d_separator(
            mlp, X_train, fill=True, alpha=.3, ax=ax)
        mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train, ax=ax)
        ax.set_title("n_hidden=[{}] \n alpha={}".format(n_hidden_node, alpha))

# 3个虚拟层
fig, axes = plt.subplots(2, 4, figsize=(20, 8))
for axx, n_hidden_node in zip(axes, [10, 100]):
    for ax, alpha in zip(axx, [0.0001, 0.01, 0.1, 1]):
        mlp = MLPClassifier(solver='lbfgs', random_state=0,
                            hidden_layer_sizes=[n_hidden_node, n_hidden_node], alpha=alpha)

        #############
        # 数据样本
        #############
        # 创建数据样本
        # noise
        #   增加噪点, 增加数据样本的难度
        X, y = make_moons(n_samples=100, noise=0.25, random_state=3)
        # 差分数据
        X_train, X_test, y_train, y_test = \
            train_test_split(X, y, stratify=y, random_state=42)

        mlp.fit(X_train, y_train)
        mglearn.plots.plot_2d_separator(
            mlp, X_train, fill=True, alpha=.3, ax=ax)
        mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], y_train, ax=ax)
        ax.set_title("n_hidden=[{},{}] \n alpha={}".format(
            n_hidden_node, n_hidden_node, alpha))

plt.show()

009 神经网络 模型得分

# 神经网络模型使用
# 概要:
#   数据源: 肿瘤数据
#   (1) 使用默认设置观察 神经网络的模型得分情况
#   (2) 对数据进行缩放后观察 神经网络的模型得分情况
#   (3) 查看数据缩放训练出来的模型,各特征的热力图

import mglearn
import matplotlib.pyplot as plt
# 神经网络
from sklearn.neural_network import MLPClassifier
# 机器学习 肿瘤数据样本
from sklearn.datasets import load_breast_cancer
# 机器学习 数据分离
from sklearn.model_selection import train_test_split

####################
# 默认
####################
# 获得肿瘤数据
cancer = load_breast_cancer()
# 数据分离
X_train, X_test, y_train, y_test = \
    train_test_split(cancer.data, cancer.target, random_state=0)

mlp = MLPClassifier(random_state=0)
mlp.fit(X_train, y_train)
score_train_1 = mlp.score(X_train, y_train)
score_test_1 = mlp.score(X_test, y_test)

print("default train score: {}".format(score_train_1))
print("default test score: {}".format(score_test_1))


####################
# 数据缩放
####################
# 数据缩放
# (特征 - 特征平均值) / 特征范围
# 将特征缩放到 -1~1之间
mean_train = X_train.mean(axis=0)
std_train = X_train.std(axis=0)
X_train_scaled = (X_train - mean_train)/std_train
X_test_scaled = (X_test - mean_train)/std_train
# max_iter: 训练次数 之前贝叶斯分类器讲过
# 神经网络模型内部有最大迭代数限制,
# 如果不指定这个参数,缩放后的数据在应用模型时会报如下错误:
# terations (200) reached and the optimization hasn't converged yet.
# 这个参数需要调试确定,也可以一次性设置大一些.
mlp_scaled = MLPClassifier(random_state=0, max_iter=400)
mlp_scaled.fit(X_train_scaled, y_train)
score_train_scaled = mlp_scaled.score(X_train_scaled, y_train)
score_test_scaled = mlp_scaled.score(X_test_scaled, y_test)

print("scaled train score: {}".format(score_train_scaled))
print("scaled test score: {}".format(score_test_scaled))

###########################
# 训练后模型的各特征的热力图
###########################
plt.figure(figsize=(20,5))
# mlp_scaled.coefs_[0] 的值是各特征得分
plt.imshow(mlp_scaled.coefs_[0])
plt.yticks(range(30), cancer.feature_names)
plt.xlabel("columns in weight matrix")
plt.ylabel("input feature")
# 显示标尺
plt.colorbar()
plt.show()

010 分类器 利用模型得分&模型的预测概率绘制决策边界

# 使用标准模型, 模型得分, 模型的预测率绘制决策边界
import matplotlib.pyplot as plt
import mglearn
# 梯度提升决策树
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.datasets import make_circles
from sklearn.model_selection import train_test_split

# 数据准备
X, y = make_circles(noise=0.25, factor=0.5, random_state=1)
X_train, X_test, y_train, y_test = \
    train_test_split(X, y, random_state=0)

# 模型初始化
gbrt = GradientBoostingClassifier(random_state=0)
gbrt.fit(X_train, y_train)

# 创建用于显示的画布及2图层
fig, axes = plt.subplots(1, 3, figsize=(13, 5))

# 第一个绘图通过函数plot_2d_separator,直接使用模型来完成决策边界的绘画
mglearn.tools.plot_2d_separator(
    gbrt, X, ax=axes[0], alpha=.4, fill=True, cm=mglearn.cm2)

# 第二个绘图通过plot_2d_scores,直接使用模型来完成决策边界的绘画
scores_image = \
    mglearn.tools.plot_2d_scores(
        gbrt, X, ax=axes[1], alpha=.4, cm=mglearn.ReBl)

# 第二个绘图通过plot_2d_scores,同过指定绘图函数来完成决策边界的绘画
# function: 仅有两个参数 predict_proba 和 decision_function
#   predict_proba: 通过概率决定
#   decision_function(default): 通过得分决定
scores_image = \
    mglearn.tools.plot_2d_scores(
        gbrt, X, ax=axes[2], alpha=.4, cm=mglearn.ReBl, function='predict_proba')


# 将训练数据,测试数据 分别填充进每个图层
for ax in axes:
    mglearn.discrete_scatter(
        X_test[:, 0], X_test[:, 1], y_test, markers='^', ax=ax)
    mglearn.discrete_scatter(
        X_train[:, 0], X_train[:, 1], y_train, markers='o', ax=ax)
    ax.set_xlabel("Featrue 0")
    ax.set_ylabel("Featrue 1")

# 添加说明
# 说明内容如果超过图层宽度,那么就会为了显示说明内容而放大图层宽度
# axes[0].legend(["Test class 0", "Test class 1",
#                "Train class 0", "Train class 1"], ncol=4, loc=(.1, 1.1))

# 将热力图分布生成温度计形式
plt.colorbar(scores_image, ax=axes.tolist())

plt.show()

010 分类器 多分类时的处理方法

# 多分类器不确定估计

import numpy as np
# 鸢尾花数据
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import GradientBoostingClassifier

# 加载数据
iris = load_iris()
# 分割数据
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, random_state=42)
# 创建模型
gbrt = GradientBoostingClassifier(learning_rate=0.01, random_state=0)
# 填充数据
gbrt.fit(X_train, y_train)

# 查看样本数据的特征得分
# 3分类数据样式
print(gbrt.decision_function(X_train[:6]))
# [[0.06146394 - 1.90755736 - 1.92793758]
#  [0.06146394 - 1.90755736 - 1.92793758]
#  [-1.99677434 - 1.8434627 - 0.01541921]
#  [-1.995715    0.04758267 - 1.92720695]
#  [-1.995715    0.04758267 - 1.92720695]
#  [0.06146394 - 1.90755736 - 1.92793758]]

# 查看模型对于数据样本的预测分类
print(gbrt.predict(X_train[:6]))
# [0 0 2 1 1 0]

# 模拟模型所作出的判断.
# np.argmax返回列表数据中,数值最大的元素的位置
print(np.argmax(gbrt.decision_function(X_train[:6]), axis=1))
# [0 0 2 1 1 0]

# 不管是几分类数据, 对于预测概率来说永远都是1
print(gbrt.predict_proba(X_train[:6]).sum(axis=1))
# In[11]: gbrt.predict_proba(X_train[:6])
# Out[11]:
# array([[0.78347147, 0.10936745, 0.10716108],
#        [0.78347147, 0.10936745, 0.10716108],
#        [0.10617681, 0.12376905, 0.77005414],
#        [0.10217718, 0.78840034, 0.10942248],
#        [0.10217718, 0.78840034, 0.10942248],
#        [0.78347147, 0.10936745, 0.10716108]])
# [1. 1. 1. 1. 1. 1.]

第三章 无监督学习与预处理

001 无监督学习 数据预处理_数据缩放种类快速演示

# 数据预处理_数据缩放种类快速演示

# 1. 左图为原始数据样本  数据范围 X: 0~15, Y: 0~10.
# 2. 右图4张为缩放后样本
#     1. 上左(StandardScaler): 每个特征的平均值为0,方差调整为1.由于强调的是整体,因此对于最大最小值可能不会太友好.
#     2. 上右(MinMaxScaler): 将特征都移动到X,Y都是0~1的矩阵范围内(想象一下离远点看数据,或将XY轴的测量单位扩大几倍).
#     3. 下左(RobustSclaer): 跟1差不多, 但是它用的是 中位数或四分位数.
#     4. 下右(Normalizer): 调整欧式长度为1(想象一下半径为1的球体)

# 中位数: 指有那么一个数可以让整体的数据一半比它大一半比它小.
# 四分位数: 跟中位数类似, 不过它不是一半而是1/4.

import mglearn
import matplotlib.pyplot as plt

mglearn.plots.plot_scaling()

plt.show()

002 无监督学习 数据预处理_MinMaxScaler缩放原理

# 数据预处理_MinMaxScaler缩放原理

# 数据样本源
from re import M
from sklearn.datasets import load_breast_cancer
# 数据拆分
from sklearn.model_selection import train_test_split
# 用于将数据缩放到 X,Y 为1的矩阵中
from sklearn.preprocessing import MinMaxScaler

# 数据准备
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=1)

# 缩放类准备
# 原理:
#     The transformation is given by::
#     X_std = (X - X.min(axis=0)) / (X.max(axis=0) - X.min(axis=0))
#     X_scaled = X_std * (max - min) + min
scaler = MinMaxScaler()
# 不同与模型的fit函数, 缩放函数之填充训练数据就行.
# 因为缩放只关心源,不关心目标类别
scaler.fit(X_train)
# 这个有点奇怪,直接实例化然后变换就行了呗,为啥中间还要喂一遍数据?
# 看函数帮助也是这么用的.
# fit函数可能的作用是先预读数据样本,确定样本中一些内容,不如个数,最大,最小值等等,
#   以此来确定数据的轮廓,在转换时会参照这个轮廓来变换数据.理论上喂的数据不用跟变换数据必须一致.
X_train_scaler = scaler.transform(X_train)

# 缩放不会改变结构,只会改变大小
# In [19]: X_train_scaler.shape
# Out[19]: (426, 30)

# In [20]: X_train.shape
# Out[20]: (426, 30)

# In [41]: X_train.min(axis=0)
# Out[41]: 
# array([6.981e+00, 9.710e+00, 4.379e+01, 1.435e+02, 5.263e-02, 1.938e-02,
#        0.000e+00, 0.000e+00, 1.060e-01, 5.024e-02, 1.153e-01, 3.602e-01,
#        7.570e-01, 6.802e+00, 1.713e-03, 2.252e-03, 0.000e+00, 0.000e+00,
#        9.539e-03, 8.948e-04, 7.930e+00, 1.202e+01, 5.041e+01, 1.852e+02,
#        7.117e-02, 2.729e-02, 0.000e+00, 0.000e+00, 1.566e-01, 5.521e-02])

# In [42]: X_train_scaler.min(axis=0)
# Out[42]: 
# array([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.])

# In [43]: X_train.max(axis=0)
# Out[43]: 
# array([2.811e+01, 3.928e+01, 1.885e+02, 2.501e+03, 1.634e-01, 2.867e-01,
#        4.268e-01, 2.012e-01, 3.040e-01, 9.575e-02, 2.873e+00, 4.885e+00,
#        2.198e+01, 5.422e+02, 3.113e-02, 1.354e-01, 3.960e-01, 5.279e-02,
#        6.146e-02, 2.984e-02, 3.604e+01, 4.954e+01, 2.512e+02, 4.254e+03,
#        2.226e-01, 9.379e-01, 1.170e+00, 2.910e-01, 5.774e-01, 1.486e-01])

# In [44]: X_train_scaler.max(axis=0)
# Out[44]: 
# array([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.])

X_test_scaler = scaler.transform(X_test)

# 对于测试数据达不到完全缩放. 结果可以看出有的小于0,大于1,不如训练数据缩放的结果完美.
# In [46]: X_test_scaler.min(axis=0)
# Out[46]: 
# array([ 0.0336031 ,  0.0226581 ,  0.03144219,  0.01141039,  0.14128374,
#         0.04406704,  0.        ,  0.        ,  0.1540404 , -0.00615249,
#        -0.00137796,  0.00594501,  0.00430665,  0.00079567,  0.03919502,
#         0.0112206 ,  0.        ,  0.        , -0.03191387,  0.00664013,
#         0.02660975,  0.05810235,  0.02031974,  0.00943767,  0.1094235 ,
#         0.02637792,  0.        ,  0.        , -0.00023764, -0.00182032])

# In [47]: X_test_scaler.max(axis=0)
# Out[47]: 
# array([0.9578778 , 0.81501522, 0.95577362, 0.89353128, 0.81132075,
#        1.21958701, 0.87956888, 0.9333996 , 0.93232323, 1.0371347 ,
#        0.42669616, 0.49765736, 0.44117231, 0.28371044, 0.48703131,
#        0.73863671, 0.76717172, 0.62928585, 1.33685792, 0.39057253,
#        0.89612238, 0.79317697, 0.84859804, 0.74488793, 0.9154725 ,
#        1.13188961, 1.07008547, 0.92371134, 1.20532319, 1.63068851])

002 无监督学习 数据预处理_MinMaxScaler缩放前后对比

# 数据预处理_MinMaxScaler缩放前后对比

import mglearn
import matplotlib.pyplot as plt
# 生成聚类数据
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
# 缩放类
from sklearn.preprocessing import MinMaxScaler

# 讲解数据缩放并不关心种类目标,因此没有y
# make_blobs 用于生成聚类数据
# n_samples 创建的样本个数
# centers 目标种类个数 本例将生成5各类别的数据
# random_state 随机种子用于打乱数据
# cluster_std 标准偏差 默认是1
X, _ = make_blobs(n_samples=50, centers=5, random_state=4, cluster_std=2)
X_train, X_test = train_test_split(X, random_state=5, test_size=.1)

#######################
# 第一个画布,使用原始数据绘制散点图
#######################
# 绘制训练集和测试集
fig, axes = plt.subplots(1, 3, figsize=(13, 4))
axes[0].scatter(X_train[:, 0], X_train[:, 1],
                c=mglearn.cm2(0), label="Training set", s=60)
axes[0].scatter(X_test[:, 0], X_test[:, 1], marker='^',
                c=mglearn.cm2(1), label="Test set", s=60)
axes[0].legend(loc='upper left')
axes[0].set_title("Original Data")

#######################
# 第二个画布,同时缩放训练数据和测试数据
#######################
# 因为使用了同时缩放,因此即使标尺改变也不影响预测结果
# 从图中可以看出,散点图与原始图接近
# 利用MinMaxScaler缩放数据
scaler = MinMaxScaler()

# 缩放都是先fit在transform,其实有个方法是fit_transform可以整合这俩个方法,而且运行效率较高
# 例如: scaler.fit_transform(X_train) 来简化步骤
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 将正确缩放的数据可视化
axes[1].scatter(X_train_scaled[:, 0], X_train_scaled[:, 1],
                c=mglearn.cm2(0), label="Training set", s=60)
axes[1].scatter(X_test_scaled[:, 0], X_test_scaled[:, 1], marker='^',
                c=mglearn.cm2(1), label="Test set", s=60)
axes[1].set_title("Scaled Data")

#######################
# 第三个画布,只对测试数据进行缩放
#######################
# 要么就全缩放,要么就全不缩放,只缩放一部分相当于改变了衡量标准,
# 因此模型会认为其中一部分数据有重大偏差进而影响预测结果.
test_scaler = MinMaxScaler()
test_scaler.fit(X_test)
X_test_scaled_badly = test_scaler.transform(X_test)
# 将错误缩放的数据可视化
axes[2].scatter(X_train_scaled[:, 0], X_train_scaled[:, 1],
                c=mglearn.cm2(0), label="training set", s=60)
axes[2].scatter(X_test_scaled_badly[:, 0], X_test_scaled_badly[:, 1],
                marker='^', c=mglearn.cm2(1), label="test set", s=60)
axes[2].set_title("Improperly Scaled Data")


for ax in axes:
    ax.set_xlabel("Feature 0")
    ax.set_ylabel("Feature 1")

plt.show()

002 无监督学习 数据变换_缩放对模型的影响

# 数据变换_缩放对模型的影响

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_breast_cancer
from sklearn.svm import SVC
# 将数据缩放到 x,y为1的矩阵中
from sklearn.preprocessing import MinMaxScaler
# 特征平均为0, 方差为1
from sklearn.preprocessing import StandardScaler

# 数据准备
cancer = load_breast_cancer()
X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target,
                                                    random_state=0)
svm = SVC(C=100)
svm.fit(X_train, y_train)
print("Test set accuracy: {:.2f}".format(svm.score(X_test, y_test)))
# Test set accuracy: 0.94

#########################
# 使用MinMaxScaler缩放模型
#########################
scaler = MinMaxScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
svm.fit(X_train_scaled, y_train)
print("Scaled test set accuracy: {:.2f}".format(
    svm.score(X_test_scaled, y_test)))
# Scaled test set accuracy: 0.97

#########################
# 使用StandardScaler缩放模型
#########################
scaler = StandardScaler()
scaler.fit(X_train)
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 在缩放后的训练数据上学习SVM
svm.fit(X_train_scaled, y_train)
# 在缩放后的测试集上计算分数
print("SVM test accuracy: {:.2f}".format(svm.score(X_test_scaled, y_test)))
# SVM test accuracy: 0.96

003 无监督学习 数据变换_PCA 特征提取快速演示

# 数据变换_PCA 特征提取快速演示

# 特征提取的代码虽然很少但是信息量很大
# 特征提取用的最多的就是 主成分分析(principal component analysis 简称PCA)
# PCA算法的步骤是:
# 1. 寻找主要数据,标记主数据的方向.
#   同过计算方差,找到方差最大的方向,将其标记成主数据.
#   主数据的方向应当是包含数据最多且特征之间最为相关.
# 2. 沿着主数据的垂直方向寻找信息最多的方向.
#   二维空间对于x轴的垂直方向就是y轴,但是在空间上与x轴垂直的方向可是有很多.
#   模型负责在这些方向上找出信息最多的那个.
# 3. 旋转主数据方向,使主数据与x轴重合
#   我猜测模型的预测计算等都是使用标准化的z轴为基础的,因此需要将数据像标准化.
#   找到主数据方向后,整体旋转数据,使主数据方向平行与x轴重合.
#   重合后的主数据,其平局值可能与x轴平行也可能与X轴重合,为了不影响模型判断因此先减去这部分平均值
#   最终得到右上图的效果,主数据方向延x轴延申,且全部以0(Y轴)为中心.
# 4. 降维
#   仅保留一部分主数据,去掉其它非主要数据来达到降维的目的.
#   得到坐下图中一条直线. (是不是线性模型就是这么找方向的呢?)
# 5. 还原数据方向
#   再次旋转数据,使整理后的数据方向与元数据主数据的方向一致.
#   将第三步中临时去掉的平均值再加回来,保持主数据的完整性.最终得到右下图

# 以上使PCA算法的思路,虽然会损失一部分的数据特征(噪点),但是可以找到真正有用的主数据方向
# 需要注意的是主数据不是主特征.主数据表示最能代表数据含义的一组或多组数据特征集合.

import mglearn
from matplotlib import pyplot as plt

mglearn.plots.plot_pca_illustration()
plt.show()

003 无监督学习 数据变换_多特征&无缩放如何表示

# 数据变换_多特征&无缩放如何表示

import mglearn
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import load_breast_cancer

cancer = load_breast_cancer()
fig, axes = plt.subplots(15, 2, figsize=(10, 20))
malignant = cancer.data[cancer.target == 0]
benign = cancer.data[cancer.target == 1]
# 将绘图结构变成线性结构
ax = axes.ravel()

for i in range(30):
    # histogram 在numpy下用于表示数据的直方图
    # 函数帮助参照: https://www.cjavapy.com/article/1103/
    # 其它的参数函数说明介绍的很清楚了,这里单独讲一下bins参数
    # bins: 直方图中柱状图的个数
    # 有三种入力模式, 数字,序列,字符串.这里只讲前两种
    # 数字: 柱状图个数,首先会取得数组集合中的虽大值最小值作为边界,然后通过linspace来自动填充
    #       使得均匀分布的柱状个数=bins值
    # 序列: 跟上面的差不多,区别在于柱状图的范围完全由序列指定.
    # 例子(数字):
    #   bin为数字时,个数小于类别个数的时候,会在最小,最大范围内,自己寻找均等位置
    #
    #   In [21]: np.histogram([1, 2, 1], bins=1)
    #   Out[21]: (array([3], dtype=int64), array([1., 2.]))
    #
    #   In [23]: np.histogram([1, 2, 1,8], bins=2)
    #   Out[23]: (array([3, 1], dtype=int64), array([1. , 4.5, 8. ]))
    #
    # bin的值在histogram中计算时是 bin+1.
    #   In [50]: np.linspace(1,8, 3)
    #   Out[50]: array([1. , 4.5, 8. ])

    # 例子(序列):
    #   In [46]: np.histogram([1, 2, 1,8,4], bins=[2,3,6,8])
    #   Out[46]: (array([1, 1, 1], dtype=int64), array([2, 3, 6, 8]))
    # 因为指定了返回值范围,因此只能返回范围内找到的值
    # 范围2~3, 找到2, 返回个数1
    # 范围3~6, 找到4, 返回个数1
    # 范围6~8, 找到8, 返回个数1
    # 因此返回的是array([1, 1, 1]

    # 把范围最小值1设置上,看看结果
    # In [51]: np.histogram([1, 2, 1,8,4], bins=[1,2,3,6,8])
    # Out[51]: (array([2, 1, 1, 1], dtype=int64), array([1, 2, 3, 6, 8]))
    # 这个值已经能够看懂了吧.

    # 通过上面的介绍,基本了解了函数是做什么用的,接下来会到这个例子
    # 这个函数的第一个返回值是 概率或者个数对接下来的画图没有用因此用"_"占位,直接舍弃
    # 第二个参数返回的是柱状图每个柱的范围,是画图中需要用到的.
    _, bins = np.histogram(cancer.data[:, i], bins=50)
    # 上面np中的hitogram是为了造柱子的轮廓数据,绘图类ax中的hist是实际的画图
    # 分别用良性/恶性样本中的每个特征数据来画柱状图
    ax[i].hist(malignant[:, i], bins=bins, color=mglearn.cm3(0), alpha=.5)
    ax[i].hist(benign[:, i], bins=bins, color=mglearn.cm3(2), alpha=.5)
    ax[i].set_title(cancer.feature_names[i])
    ax[i].set_yticks(())

ax[0].set_xlabel("Feature magnitude")
ax[0].set_ylabel("Frequency")
ax[0].legend(["malignant", "benign"], loc="best")

# 紧凑显示画布上的绘图
fig.tight_layout()

plt.show()

003 无监督学习 数据变换_PCA 分析多特征

# 数据变换_PCA 分析多特征

import mglearn
import matplotlib.pyplot as plt
# 主成分分析库
from sklearn.decomposition import PCA
from sklearn.datasets import load_breast_cancer
from sklearn.preprocessing import StandardScaler

#####################
# 准备数据,并进行数据的缩放
#####################
cancer = load_breast_cancer()
scaler = StandardScaler()
scaler.fit(cancer.data)
X_scaled = scaler.transform(cancer.data)

#####################
# 实例化PCA,指定保留2个重要主成分
#####################
# 跟快速演示中的不太一样,看样子PCA只是用于提取,并没有缩放功能,缩放需要自己单独写
pca = PCA(n_components=2)
# 初始化数据分析轮廓
pca.fit(X_scaled)

# compents_ 属性中存放有序主成分,越重要的主成分越靠前
# In [4]: pca.components_.shape
# Out[4]: (2, 30)

# 提取主成分
X_pca = pca.transform(X_scaled)
print("Original shape: {}".format(str(X_scaled.shape)))
print("Reduced shape: {}".format(str(X_pca.shape)))
# Original shape: (569, 30)
# Reduced shape: (569, 2)
# 成功提取出来俩个特征

#####################
# 画图 主成分的散点图
#####################
plt.figure(figsize=(8, 8))
mglearn.discrete_scatter(X_pca[:, 0], X_pca[:, 1], cancer.target)
plt.legend(cancer.target_names, loc="best")
plt.gca().set_aspect("equal")
plt.xlabel("First principal component")
plt.ylabel("Second principal component")

plt.show()

003 无监督学习 数据变换_PCA 提取特征脸

# 数据变换_PCA 提取特征脸

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_lfw_people
from sklearn.model_selection import train_test_split
from sklearn.decomposition import PCA

# min_faces_per_person=20 每人最少20张人脸图片
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape

##################
# 第一画布 显示人脸图像
##################
fix, axes = plt.subplots(2, 5, figsize=(15, 8),
                         subplot_kw={'xticks': (), 'yticks': ()})
for target, image, ax in zip(people.target, people.images, axes.ravel()):
    ax.imshow(image)
    ax.set_title(people.target_names[target])


##################
# 处理人脸图像
##################
# 每人人脸图像采样最多50个
mask = np.zeros(people.target.shape, dtype=np.bool)
for target in np.unique(people.target):
    # np.where(condition) 这个是numpy中的方法不能直接作用在list上
    # a = np.array([1,2,3,4])
    # 通过np.array做成的都是高维数组,即使只有1维数据也表示成(xx,)的形式
    # a.shape -> (4,)
    # 取得满足条件的数据的位置
    # np.where(a > 2) -> (array([2, 3], dtype=int64),)
    # 应当用这种方式取得一维的数据
    # np.where(a > 2)[0] -> array([2, 3], dtype=int64)

    # 通过上面的例子分析一下np.where(people.target == target)[0][:50]
    # 1.np.where(people.target == target)[0] 就是直接找到匹配条件的数据位置
    # 2.[:50] 满足条件的同一结果的所有位置数据中只要前50个位置
    # 3.mask[XXX] = 1 将符合条件的前50个位置标记成True,用于以后的取数据
    mask[np.where(people.target == target)[0][:50]] = 1
X_people = people.data[mask]
y_people = people.target[mask]


##################
# 第二画布 使用PCA抽取主成分成像
##################
# stratify 以指定参数的成分比例拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    X_people, y_people, stratify=y_people, random_state=0)
# 抽取前100主要成分
# whiten: 白化处理,可以将主成分缩放到相同的比例
pca = PCA(n_components=100, whiten=True, random_state=0).fit(X_train)
fix, axes = plt.subplots(3, 5, figsize=(15, 12), subplot_kw={
                         'xticks': (), 'yticks': ()})
# 将主成分成像
# zip函数可以将两个数组通过位置1对1匹配,凑不成1对时,停止
# In [73]: list(zip([3,2], [1,2,3]))
# Out[73]: [(3, 1), (2, 2)]
for i, (component, ax) in enumerate(zip(pca.components_, axes.ravel())):
    # 将主成分格式化成图像一致的横纵比
    ax.imshow(component.reshape(image_shape), cmap='viridis')
    ax.set_title("{}. component".format((i + 1)))

plt.show()

004 无监督学习 数据变换_NMF 快速演示

# 数据变换_NMF 快速演示

# NMP与PCA相比图要简单一些,没有PCA的旋转,去成分,再旋转等操作
# 但是NMP很明显可以看到数据分类的方向
# 第一图是二分类数据, 两个箭头分别指向分类的方向
# 第二图是一分类数据, 箭头正好均分数据
# 要是多分类的话理论上会有多个箭头指向各个分类的方向

import mglearn
import matplotlib.pyplot as plt

mglearn.plots.plot_nmf_illustration()
plt.show()

004 无监督学习 数据变换_NMF 处理人脸数据

# 数据变换_NMF 处理人脸数据
# 概要:
# 1.使用NMF模型降维,并提取人脸数据种重要的15种主要成分
# 2.将这15种主要成分绘图
# 3.以2种第三种主要成分抽取人脸数据并绘图 (向右)
# 4.以10种第三种主要成分抽取人脸数据并绘图 (向左)
# 通过以上例子可以看出NMF不仅可以降维提取主成分,还可以汇集方向一致的数据

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import NMF
from sklearn.datasets import fetch_lfw_people
from sklearn.model_selection import train_test_split

# min_faces_per_person=20 每人最少20张人脸图片
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
# In[4]: image_shape
# Out[4]: (87, 65)

# stratify 以指定参数的成分比例拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    people.data, people.target, stratify=people.target, random_state=0)

# 创建NMF模型并填充数据
# 设定n_components, 使模型选择最重要的15种主要成分用于显示
nmf = NMF(n_components=15, random_state=0)
nmf.fit(X_train)

# 绘图
#######################
# 使用NMF像是15种主要成分 (先总览一下都找出了哪些重要成分)
#######################
fix, axes = plt.subplots(3, 5, figsize=(15, 12),
                         subplot_kw={'xticks': (), 'yticks': ()})
# In[3]: nmf.components_.shape
# Out[3]: (15, 5655)
for i, (component, ax) in enumerate(zip(nmf.components_, axes.ravel())):
    ax.imshow(component.reshape(image_shape))
    ax.set_title("{}. component".format(i))

#######################
# 使用NMF仅显示第符合第三种主要成分的人脸数据(向左)
#######################
# 先用数据转换一下原始数据
# 目的时降维和汇总主成分数据
X_train_nmf = nmf.transform(X_train)
X_test_nmf = nmf.transform(X_test)

# In[8]: X_train.shape
# Out[8]: (1658, 5655)
# In[9]: X_train_nmf.shape
# Out[9]: (1658, 15)

compn = 3
# argsort使用方法
# argsort(start_index, end_index, step)
# In[13]: a = np.array([4, 3, 5, 6])
# In[14]: np.argsort(a)
# Out[14]: array([1, 0, 2, 3], dtype=int64)
# In[15]: np.argsort(a)[:]
# Out[15]: array([1, 0, 2, 3], dtype=int64)
# In[16]: np.argsort(a)[::-1]
# Out[16]: array([3, 2, 0, 1], dtype=int64)
inds = np.argsort(X_train_nmf[:, compn])[::-1]
fig, axes = plt.subplots(2, 5, figsize=(15, 8),
subplot_kw={'xticks': (), 'yticks': ()})
for i, (ind, ax) in enumerate(zip(inds, axes.ravel())):
    ax.imshow(X_train[ind].reshape(image_shape))

#######################
# 使用NMF仅显示第符合第十种主要成分的人脸数据 (向右)
#######################
compn = 10
inds = np.argsort(X_train_nmf[:, compn])[::-1]
fig, axes = plt.subplots(2, 5, figsize=(15, 8),
                         subplot_kw={'xticks': (), 'yticks': ()})
for i, (ind, ax) in enumerate(zip(inds, axes.ravel())):
    ax.imshow(X_train[ind].reshape(image_shape))

plt.show()

004 无监督学习 数据变换_PCA & NMF 处理信号

# 数据变换_PCA & NMF 处理信号
# 处理概要:
# 1.创建信号源
# 2.提高信号源的复杂度
# 3.使用NMF解析2的信号,取出前三个主要成分
# 4.使用PCA解析2的信号,取出前三个主要成分
# 5.将1,2,3,4分别绘图

# 结论:
# step1:原始信号为3个,波形较接近,有重叠的部分
# step2:提高数组维数后,不同信号波形之间出现明显分离,较容易区分每个信号
# step3:NMF可以降维取出主要成分,并能较准确的还原step2种的信号波形
# step4:PCA也可以降维取出主要成分,但是绘制波形出现了明显的误差与step1,2的波形完全不匹配

# 尝试:
# step3: 将n_components的值设置>3, 结果相同
# step4: 将n_components的值设置>3, 结果相同
# 因此PCA,NMF在寻找主成分上还是没有问题的

# 导致step4出现误差的原因:
# PCA不关心方向,通过旋转 -> 去成分 -> 旋转 的方式寻找主要成分, 旋转一定会带来一部分的损失
# 我理解PCA就是想方设法"找出",而NMF是想法设法"找对"
# 也就是:  PCA(find: "a*")  NMF(find: "abc")

import numpy as np
import matplotlib.pyplot as plt
import mglearn
from sklearn.decomposition import PCA, NMF

################################
# 创建一组原始信号
################################
S = mglearn.datasets.make_signals()
# In[3]: S.shape
# Out[3]: (2000, 3)

################################
# 提高数据的维数, 就是把数据搞复杂些
################################
# RandomState(0)
# 需要提供一个随机种子用于计算, 相同的种子会得到相同的结果
# np.random.RandomState.uniform(low=0.0, high=1.0, size=None)
# In[32]: np.random.RandomState(1).uniform()
# Out[32]: 0.417022004702574
# In [44]: np.random.RandomState(0).uniform()
# Out[44]: 0.5488135039273248
# In [45]: np.random.RandomState(0).uniform()
# Out[45]: 0.5488135039273248

# size: 用于指定生成的结构
# In[13]: np.random.RandomState(0).uniform(size=(3, 100)).shape
# Out[13]: (3, 100)
# In[15]: np.random.RandomState(0).uniform(size=(100, 3)).shape
# Out[15]: (100, 3)

A = np.random.RandomState(0).uniform(size=(100, 3))
# In[2]: A.T.shape
# Out[2]: (3, 100)

# dot help: https://www.icode9.com/content-1-1198894.html
X = np.dot(S, A.T)
# In[4]: X.shape
# Out[4]: (2000, 100)
print("Shape of measurements: {}".format(X.shape))

################################
# 使用NMF还原信号
################################
nmf = NMF(n_components=3, random_state=42)
S_ = nmf.fit_transform(X)

################################
# 使用PCA还原信号
################################
pca = PCA(n_components=3)
H = pca.fit_transform(X)

################################
# 绘制上面数据的图片:
#   [原始信号, 混合后高维数组的信号, NMF还原的信号, PCA还原的信号]
################################
models = [X, S, S_, H]
names = ['Observations (first three measurements)',
         'True sources',
         'NMF recovered signals',
         'PCA recovered signals']

# hspace 控制两个绘图之间的间距(上下间隔高度)
fig, axes = plt.subplots(4, figsize=(8, 4), gridspec_kw={'hspace': .5},
                         subplot_kw={'xticks': (), 'yticks': ()})
for model, name, ax in zip(models, names, axes):
    ax.set_title(name)
    ax.plot(model[:, :3], '-')

plt.show()

005 无监督学习 数据变换_流式学习t-SNE 可视化数据

# 数据变换_流式学习t-SNE

# 概要:
# 1. 创建手写数据, 表示手写图片
# 2. 使用PCA尝试区分手写数据,使用散点图查看PCA分类状况
# 3. 使用t-SNE尝试step2的内容

# 结论:
# PCA可以找出分类,但是各分类间的决策边界不明显,分析数据不直观
# t-SNE可以找出分类,并且会会将同类数据尽量靠近表示,决策边界明显,图像只管.    
    
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE

#################
# 绘制手写数据
#################
digits = load_digits()
fig, axes = plt.subplots(2, 5, figsize=(10, 5),
                         subplot_kw={'xticks': (), 'yticks': ()})
for ax, img in zip(axes.ravel(), digits.images):
    ax.imshow(img)

#################
# PCA分析手写数据
#################
pca = PCA(n_components=2)
pca.fit(digits.data)

digits_pca = pca.transform(digits.data)
colors = ["#476A2A", "#7851B8", "#BD3430", "#4A2D4E", "#875525",
          "#A83683", "#4E655E", "#853541", "#3A3120", "#535D8E"]
plt.figure(figsize=(10, 10))
plt.xlim(digits_pca[:, 0].min(), digits_pca[:, 0].max())
plt.ylim(digits_pca[:, 1].min(), digits_pca[:, 1].max())
for i in range(len(digits.data)):
    # 将数据实际绘制成文本,而不是散点
    plt.text(digits_pca[i, 0], digits_pca[i, 1], str(digits.target[i]),
             color=colors[digits.target[i]],
             fontdict={'weight': 'bold', 'size': 9})
    # 散点图 想看散点图的去掉下面注释
    # plt.scatter(digits_pca[i, 0], digits_pca[i, 1])
plt.xlabel("First principal component")
plt.ylabel("Second principal component")

#################
# t-SNE分析手写数据
#################
tsne = TSNE(random_state=42)
# 流式学习一般不应用在测试数据上,因此没有先fit(不需要准备,之后不会再用)再transform的过程
# 都是直接一步完成,前面的例子有用到过fit_transform,这里直接用就行.
digits_tsne = tsne.fit_transform(digits.data)

plt.figure(figsize=(10, 10))
plt.xlim(digits_tsne[:, 0].min(), digits_tsne[:, 0].max() + 1)
plt.ylim(digits_tsne[:, 1].min(), digits_tsne[:, 1].max() + 1)
for i in range(len(digits.data)):
    # 将数据实际绘制成文本,而不是散点
    plt.text(digits_tsne[i, 0], digits_tsne[i, 1], str(digits.target[i]),
         color=colors[digits.target[i]],
         fontdict={'weight': 'bold', 'size': 9})
plt.xlabel("t-SNE feature 0")
plt.xlabel("t-SNE feature 1")

plt.show()

006 无监督学习 数据聚类_K均值聚类 快速演示

# 数据聚类_K均值聚类 快速演示

# 原理:
# 1. 根据设置参数N创建N个cluster
# 2. 将各个数据点分配给最近的cluster
# 3. 计算各个cluster的平均值,并标记成cluster中心点
# 4. 迭代执行step2,3, 直到cluster的分配不再发生变化,算法结束.

# 理解:
# 算法迭代过程中,cluster的中心会发生偏移,也就会导致数据点的分配也会发生变化,
# cluster中数组一直都在变化,但是每次都会保留上一次的结果,通过数据元素匹配结束循环应当是最有效的方式.

# 说明:
# 图中的三角形为对应cluster的中心

import mglearn
import matplotlib.pyplot as plt

# 算法演示
mglearn.plots.plot_kmeans_algorithm()

# 决策边界
mglearn.plots.plot_kmeans_boundaries()

plt.show()

006 无监督学习 数据聚类_K均值聚类使用

# 数据聚类_K均值聚类使用

import mglearn
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

# 生成模拟的二维数据
X, y = make_blobs(random_state=1)

# 构建聚类模型
# n_clusters 设置预先想定的簇个数
kmeans = KMeans(n_clusters=3)
kmeans.fit(X)

# labels_ 标记簇的种类,  有点像分类种的target属性, 
# 不同的是labels_只负责标记簇,并不知道簇的实际种类.
# In[2]: kmeans.labels_
# Out[2]:
# array([1, 0, 0, 0, 2, 2, 2, 0, 1, 1, 0, 0, 2, 1, 2, 2, 2, 1, 0, 0, 2, 0,
#        2, 1, 0, 2, 2, 1, 1, 2, 1, 1, 2, 1, 0, 2, 0, 0, 0, 2, 2, 0, 1, 0,
#        0, 2, 1, 1, 1, 1, 0, 2, 2, 2, 1, 2, 0, 0, 1, 1, 0, 2, 2, 0, 0, 2,
#        1, 2, 1, 0, 0, 0, 2, 1, 1, 0, 2, 2, 1, 0, 1, 0, 0, 2, 1, 1, 1, 1,
#        0, 1, 2, 1, 1, 0, 0, 2, 2, 1, 2, 1])

# predict
# 可以用于测试数据上,但是测试数据会使用训练数据的簇中心,
# 也就是说新数据不会改变既有模型,而是顺应既有模型
# 如果predict使用的是训练数据,那么输出结果与labels_一致
# In [3]: kmeans.predict(X)
# Out[3]: 
# array([1, 0, 0, 0, 2, 2, 2, 0, 1, 1, 0, 0, 2, 1, 2, 2, 2, 1, 0, 0, 2, 0,
#        2, 1, 0, 2, 2, 1, 1, 2, 1, 1, 2, 1, 0, 2, 0, 0, 0, 2, 2, 0, 1, 0,
#        0, 2, 1, 1, 1, 1, 0, 2, 2, 2, 1, 2, 0, 0, 1, 1, 0, 2, 2, 0, 0, 2,
#        1, 2, 1, 0, 0, 0, 2, 1, 1, 0, 2, 2, 1, 0, 1, 0, 0, 2, 1, 1, 1, 1,
#        0, 1, 2, 1, 1, 0, 0, 2, 2, 1, 2, 1])

# cluster_centers_ 存放簇中心
# In [2]: 
#    ...: kmeans.cluster_centers_
# Out[2]: 
# array([[ -1.4710815 ,   4.33721882],
#        [-10.04935243,  -3.85954095],
#        [ -6.58196786,  -8.17239339]])

########################
# 绘制散点图 簇个数 = 分类数
########################
# 使用K均值聚类算法生成的聚类绘制散点图
mglearn.discrete_scatter(X[:, 0], X[:, 1], kmeans.labels_, markers='o')

# 使用簇中心点绘制散点图
# 结合上面[cluster_centers_]的输出结果理解下面.
# [0, 1, 2] 是因为在初始化算法的时候就已经假定好了有3个簇
mglearn.discrete_scatter(
    kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1], [0, 1, 2],
    markers='^', markeredgewidth=2)

########################
# 绘制散点图 簇个数 != 分类数
########################
fig, axes = plt.subplots(1, 2, figsize=(10, 5))
# 使用2个簇中心:
kmeans = KMeans(n_clusters=2)
kmeans.fit(X)
mglearn.discrete_scatter(X[:, 0], X[:, 1], kmeans.labels_, ax=axes[0])

# 使用5个簇中心:
kmeans = KMeans(n_clusters=5)
kmeans.fit(X)
mglearn.discrete_scatter(X[:, 0], X[:, 1], kmeans.labels_, ax=axes[1])

plt.show()

006 无监督学习 数据聚类_K均值聚类 失败案例

# 数据聚类_K均值聚类 失败案例
# 3种失败案例

import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

##############################
# 失败案例 环形数据
##############################
# 失败内容: 无法找到环形的中心点, 而是找到相邻数据的中心点

X, y = make_moons(n_samples=200, noise=0.05, random_state=0)
# 将数据聚类成2个簇
kmeans = KMeans(n_clusters=2)
kmeans.fit(X)

# 画出簇分配和簇中心
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels_, cmap=mglearn.cm2, s=60)
plt.scatter(kmeans.cluster_centers_[:, 0], kmeans.cluster_centers_[:, 1],
            marker='^', c=[mglearn.cm2(0), mglearn.cm2(1)], s=100, linewidth=2)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")


##############################
# 失败案例 不同分类点距离很接近的数据
##############################
# 失败内容: 簇不准确, 会包含相邻簇中距离当前簇距离很近的点.

# cluster_std用于控制不同簇的范围
# 这里使用了 1和0.5 相对于2.5 都要远一些.

# 创建新画布
plt.figure()
X_varied, y_varied = make_blobs(n_samples=200,
                                cluster_std=[1.0, 2.5, 0.5],
                                random_state=170)
kmeans = KMeans(n_clusters=3, random_state=0)
kmeans_fit = kmeans.fit(X_varied)
y_pred = kmeans.fit_predict(X_varied)
mglearn.discrete_scatter(X_varied[:, 0], X_varied[:, 1], y_pred)
mglearn.discrete_scatter(kmeans_fit.cluster_centers_[
                         :, 0], kmeans_fit.cluster_centers_[:, 1], [0, 1, 2], markers='D')
plt.legend(["cluster 0", "cluster 1", "cluster 2"], loc='best')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")


##############################
# 失败案例 带有方向的数据源
##############################
# 失败原因: k均值聚类不考虑数据方向,只关心距离是否相近.
#         无法准确带有方向数据的中心点,出错原因类似环形数据

# 创建新画布
plt.figure()

# 生成一些随机分组数据 创建3分类测试数据
X, y = make_blobs(random_state=170, n_samples=600)

# In[6]: set(y)
# Out[6]: {0, 1, 2}

# 生成随机数
rng = np.random.RandomState(74)

# 简单说一下RandomState
# 生成伪随机数函数, 取值范围 0~1
# rand函数用于取随机数,无参情况下返回1个随机数,有参情况下,返回指定个数的随机数
# 由此可见RandomState内部是应当是维护一个随机数组的,保留numpy的特性
# In [22]: rng
# Out[22]: RandomState(MT19937) at 0x275C9C5C940

# In [23]: rng.rand()
# Out[23]: 0.42384573800413594

# In [24]: rng.rand(4)
# Out[24]: array([0.80487959, 0.02949612, 0.52576831, 0.78733602])

# 生成 2 * 2 结构的随机数
transformation = rng.normal(size=(2, 2))

# 变换数据使其拉长
# 结合下面的例子理解如何做到数据放大的
X = np.dot(X, transformation)
# In [26]: x
# Out[26]: 
# array([[1, 2],
#        [1, 2]])
# In [27]: y
# Out[27]: 
# array([[0.5, 0.8],
#        [0.5, 0.9]])
# In [24]: np.dot(x,y)
# Out[24]: 
# array([[1.5, 2.6],
#        [1.5, 2.6]])
# In [25]: np.dot(y,x)
# Out[25]: 
# array([[1.3, 2.6],
#        [1.4, 2.8]])

# 将数据聚类成3个簇
kmeans = KMeans(n_clusters=3)
kmeans_fit = kmeans.fit(X)
y_pred = kmeans.predict(X)

# # 画出簇分配和簇中心
plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=mglearn.cm3)
plt.scatter(kmeans_fit.cluster_centers_[:, 0], kmeans_fit.cluster_centers_[:, 1],
            marker='D', c=[0, 1, 2], s=100, linewidth=2, cmap='Dark2')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")


plt.show()

006 无监督学习 数据聚类_PCA NMF K均值聚类 对比

# 数据聚类_PCA NMF K均值聚类 对比
# 处理概要:
#     1. 准备数据
#     2. 分别使用3种算法计算人脸数据
#     3. 使用step2的数据直接绘图
#     4. 将step2的数据根据各个算法进行重建
#     5. 将step4种重建好的数据进行绘图
#     6. 观察各个算法直接出图与重建后的图,以及原始图之间的关系

# 观察结论:
#     1. NMF 与算法介绍的一样,它的初衷是保留数据方向并提取主成分
#         直接出图:
#             从图中可以看出,NMF的直接计算结果 大概描述出了数据轮廓,因此能判定数据方向
#         重建出图:
#             轮廓更多了一些,对于计算机而言,细节更多了些,但对于人眼观察缺失细节
#     2. PCA 算法目的是不考虑方向只关心主成分
#         直接出图:
#             对于人来说也就是有个人影, 对于计算机来说可以使用大概的轮廓进行模糊匹配
#         重建出图:
#             人脸大部分可以识别出来,但是受限与算法,更NMF一样缺失了不少细节.
#             就有点像素描,虽然细节少但可以很直观确认出是谁.
#     3. K均值聚类
#         直接出图:
#             没想到可以识别出人脸的大部分数据,从底色来看,基本上都识别出来了.
#             想想也是,毕竟它没删数据,只是聚隆数据,并没有删细节
#         重建出图:
#             人脸细节保留了很多,但是图片多多少少出现了些重影,原因可能是聚拢算法是数据产生了位移.

# 每个算法都针对特有的需求,没有通用算法,使用时需要留意.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.decomposition import NMF
from sklearn.datasets import fetch_lfw_people

###################
# 准备人脸数据
###################
# min_faces_per_person=20 每人最少20张人脸图片
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape

# 每人人脸图像采样最多50个
mask = np.zeros(people.target.shape, dtype=np.bool)

# 标记可以取得的人脸数据位置为true
for target in np.unique(people.target):
    mask[np.where(people.target == target)[0][:50]] = 1

# 通过标记取得可用数据
X_people = people.data[mask]
y_people = people.target[mask]

# 数据拆分
X_train, X_test, y_train, y_test = train_test_split(
    X_people, y_people, stratify=y_people, random_state=0)

###################
# NMF, PCA, KMEANS算法
###################
# 非负矩阵拆分 NMF
nmf = NMF(n_components=100, random_state=0)
nmf.fit(X_train)

# 主成分提取 PCA
pca = PCA(n_components=100, random_state=0)
pca.fit(X_train)

# K均值聚类 KMEANS
kmeans = KMeans(n_clusters=100, random_state=0)
kmeans.fit(X_train)

###################
# 通过各模型的计算结果重建数据
###################

# 主成分提取 PCA
# 重建原理: 先筛选出主要成分,然后通过主要成分反向补全人脸数据
X_reconstructed_pca = pca.inverse_transform(pca.transform(X_test))

# K均值聚类
# 重建原理: 为了重建,需要找到每个类别的簇中心点
# 提示:
#   对于训练数据  labels_ 与 predict 的结果一致
#   对于测试数据只能使用 predictzhua
X_reconstructed_kmeans = kmeans.cluster_centers_[kmeans.predict(X_test)]

# 非负矩阵拆分 NMF
# 重建原理: 找出带有方向部分的主成分,然后根据成分个数拉伸放大
X_reconstructed_nmf = np.dot(nmf.transform(X_test), nmf.components_)

###################
# 画图  各算法直接计算结果出图
###################
fig, axes = plt.subplots(3, 5, figsize=(8, 8),
                         subplot_kw={'xticks': (), 'yticks': ()})
fig.suptitle("Extracted Components")
# In [9]: lable1
# Out[9]: 
# array([[1, 2],
#        [3, 4]])

# In [10]: values
# Out[10]: 
# array([[ 7963.92759169, -2931.3518914 ,  3360.79428745],
#        [ 7964.28495515, -2930.99452794,  3361.15165092],
#        [ 7965.60367246, -2929.67581063,  3362.47036823]])

# In [8]: for label, score in zip(lable1,values):
#    ...:     print("{}:{}".format(label,score))
#    ...: 
# [1 2]:[ 7963.92759169 -2931.3518914   3360.79428745]
# [3 4]:[ 7964.28495515 -2930.99452794  3361.15165092]
# 思路: 一次设置一列, 1列包含3个算法
for ax, comp_kmeans, comp_pca, comp_nmf in zip(
        axes.T, kmeans.cluster_centers_, pca.components_, nmf.components_):
    ax[0].imshow(comp_kmeans.reshape(image_shape), cmap='PRGn_r')
    ax[1].imshow(comp_pca.reshape(image_shape), cmap='viridis')
    ax[2].imshow(comp_nmf.reshape(image_shape), cmap='Dark2')
axes[0, 0].set_ylabel("kmeans")
axes[1, 0].set_ylabel("pca")
axes[2, 0].set_ylabel("nmf")

###################
# 画图  将各算法计算结果反向重建后出图
###################
fig, axes = plt.subplots(4, 5, subplot_kw={'xticks': (), 'yticks': ()},
                         figsize=(8, 8))
fig.suptitle("Reconstructions")
for ax, orig, rec_kmeans, rec_pca, rec_nmf in zip(
        axes.T, X_test, X_reconstructed_kmeans, X_reconstructed_pca,
        X_reconstructed_nmf):
    ax[0].imshow(orig.reshape(image_shape))
    ax[1].imshow(rec_kmeans.reshape(image_shape), cmap='PRGn_r')
    ax[2].imshow(rec_pca.reshape(image_shape), cmap='viridis')
    ax[3].imshow(rec_nmf.reshape(image_shape), cmap='Dark2')
axes[0, 0].set_ylabel("original")
axes[1, 0].set_ylabel("kmeans")
axes[2, 0].set_ylabel("pca")
axes[3, 0].set_ylabel("nmf")

plt.show()

007 无监督学习 数据聚类_凝聚聚类 快速演示

# 数据聚类_凝聚聚类 快速演示

# 凝聚聚类原理:
#     1. 将每个数据点都看成簇
#     2. 递归合并两个相似的簇,知道簇的个数缩减到指定的个数为止

# 判断相似簇的参数有:
#     ward: 
#         ward 挑选两个簇来合并,使得所有簇中的方差增加最小。这通常会得到大小差不多相等的簇
#     average:
#         average 链接将簇中所有点之间平均距离最小的两个簇合并。
#     complete:
#         complete 链接(也称为最大链接)将簇中点之间最大距离最小的两个簇合并。

# 其中 ward 适合分类数据中各类个数差别不大的数据源.

import mglearn
import matplotlib.pylab as plt

######################
# 凝聚聚类分层过程
######################
mglearn.plots.plot_agglomerative_algorithm()

plt.figure()
######################
# 表示每次迭代产生的层
######################
# 只看每层的合并点是否对就行, 上面的编号是算法分析的过程不用关心
# 这个图看起来有点像地图的海拔图
mglearn.plots.plot_agglomerative()

plt.show()

007 无监督学习 数据聚类_凝聚聚类 使用方法

# 数据聚类_凝聚聚类 使用方法

# 概要: 
#     1. 使用同样的数据源(参照前面的例子,使用的是K均值聚类无法完全区分的数据源)
#     2. 先画出凝聚聚类
#     3. 再画出K均值聚类

# 比较结果:
#     凝聚聚类的算法好像能比较好的区分数据, 各个类别划分的很清晰,没有噪点.

import mglearn
import matplotlib.pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

#######################
# 凝聚聚类
#######################
X, y = make_blobs(n_samples=200,
                  cluster_std=[1.0, 2.5, 0.5],
                  random_state=170)

agg = AgglomerativeClustering(n_clusters=3)

# 凝聚聚类由于算法限制,只能用于训练数据,因此没有predict方法, 但可以使用fit_predict
assignment = agg.fit_predict(X)
mglearn.discrete_scatter(X[:, 0], X[:, 1], assignment)
# 凝聚聚类形成的簇好像没有簇的中心点坐标Agglomerative,属性里没查到.

plt.xlabel(" Feature 0")
plt.ylabel("Agglomerative Feature 1")


#######################
# k均值聚类 之前的例子,方便对比
#######################
plt.figure()
X_varied, y_varied = make_blobs(n_samples=200,
                                cluster_std=[1.0, 2.5, 0.5],
                                random_state=170)
kmeans = KMeans(n_clusters=3, random_state=0)
kmeans_fit = kmeans.fit(X_varied)
y_pred = kmeans.fit_predict(X_varied)
mglearn.discrete_scatter(X_varied[:, 0], X_varied[:, 1], y_pred)
mglearn.discrete_scatter(kmeans_fit.cluster_centers_[
                         :, 0], kmeans_fit.cluster_centers_[:, 1], [0, 1, 2], markers='D')
plt.legend(["cluster 0", "cluster 1", "cluster 2"], loc='best')
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

plt.show()

008 无监督学习 数据聚类_DBSCAN 快速演示

# 数据聚类_DBSCAN 快速演示

# DBSCAN 本质上也是凝聚, 只是相对于凝聚聚类它的方法稍显不同,它是通过拥挤程度来区分簇的.
# 影响DBSCAN判断拥挤程度的两个重要参数:
#     1. min_samples 用于描述数据点个数
#     2. eps 用于描述距离
# 原理大概: 
#   1.确定一个簇标记 (eps范围内的数据点个数大于等于min_samples)
#   2.用标记好的簇去影响eps范文内还未标记的点,
#   3.搜寻这个簇,标记这个簇能影响到的所有点,直到找不出为止
#   4. 重复1~3步骤,直到所有点都被标记完为止.

# 详细步骤:
#   1. 首先选取数据中的一个随机点
#   2. 找出在eps范围内的所有数据点
#   3. 如果找出的数据点个数小于min_samples那么(1)中的点就被标记成噪声,说明该点不属于任何簇
#   4. 反之,(1)中的点就会被标记成核心样本,并会分配给一个新的簇标检
#   5. 重新看一下(2)中的数据点, 对还没有分配簇标签的数据点,统一分配成(1)的簇标签
#   6. 依次访问(2)中被标记的数据点,然后再以eps为范围搜寻邻近点,再标记
#   7. 重复执行1~6的步骤,知道无法再找到新的核心样本为止
#   8. 再随机抽取一个数据点(既不是噪声,也没标记簇) 重复1~7的动作 直到标记完所有的点为止.

# 图怎么看:
#   1. 实心大图标为 "核心数据"
#   2. 实心小图标为 "边界点" (卡着eps距离的点)
#   3. 空心图标为 "噪声"
#   4. 图中距离说明了, min_sample和eps是如何影响模型判断的

# 参数影响:
#  1. eps设置的太小,那么每个数据点将变成噪点,反之所有的数据点都可能会变成一个簇
#  2. min_samples设置的越大,核心点越少,噪点越多.

import mglearn
import matplotlib.pylab as plt

mglearn.plots.plot_dbscan()

plt.show()


008 无监督学习 数据聚类_DBSCAN 使用方法

# 数据聚类_DBSCAN 使用方法

# 概要: 
#     1. 创建数据源
#     2. 使用之前学过的方法,先对数据进行预处理(缩放)
#     3. 将缩放后的数据使用DBSCAN进行处理
#     4. 绘制散点图, 数据源使用(2), 类别目标使用(3)

# 注意:
#     书中原话:
#         在使用DBSCAN 时,你需要谨慎处理返回的簇分配。如果使用簇标签对另一个数据进行
#         索引,那么使用-1 表示噪声可能会产生意料之外的结果。
#     我理解: 一般使用方法为,谁判别的类别,数据源也已应当用谁的,这样是最准确的,
#           但这个例子中,源和目标分别使用了两个算法计算结果,可能就是为了演示说明用,
#           如果DBSCAN计算结果中有-1产生的话,需要谨慎使用结果.

import mglearn
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import DBSCAN

#################
# 数据源
#################
X, y = make_moons(n_samples=200, noise=0.05, random_state=0)

#################
# 缩放数据
#################
# 将数据缩放成平均值为0、方差为1
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

#################
# 使用DBSCAN算法来判别类别
#################
# 注意: DBSCAN算法只能用在训练数据上,因此没有predict方法
# 默认情况下:
# eps=0.5
# min_samples=5
dbscan = DBSCAN()
clusters = dbscan.fit_predict(X_scaled)

# 这个例子中好像都判别正确了
# In [2]: clusters
# Out[2]: 
# array([0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1,
#        0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
#        1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0,
#        0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0,
#        1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0,
#        0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0,
#        0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0,
#        1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1,
#        1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1,
#        0, 1], dtype=int64)

# 如果这么设置的话,就会产生-1,也就是结果不正确 最终显示的图片来看,确识也不对.
# In [3]: dbscan = DBSCAN(eps=0.2)
# In [4]: clusters = dbscan.fit_predict(X_scaled)
# In [5]: clusters
# Out[5]: 
# array([ 0,  1,  2, -1,  3,  3,  0,  1,  0,  1,  6,  4,  3,  1,  5,  0,  6,
#         4,  6,  6,  3,  1,  6,  1,  6,  4,  3,  3,  4,  6,  0, -1,  1,  2,
#         6,  3,  4,  0,  5,  2,  1,  6,  6,  4,  1,  6,  0,  5,  4,  1,  6,
#         4,  3, -1,  1,  6,  6,  1,  6,  6,  4,  5,  3,  0,  1,  5,  6,  1,
#         6,  5,  1,  5,  1, -1,  1,  5,  2,  0,  6,  1,  3,  6,  1,  3,  2,
#         6,  0,  6,  1,  3,  6,  0,  3,  6,  3,  3,  3,  4,  5,  4, -1,  3,
#         6,  0,  5,  2,  5,  6,  1,  6,  6,  6,  6,  5,  0,  1,  6,  3,  1,
#         0,  0,  5,  2,  6,  2,  5,  0,  4,  1,  3,  0,  6,  5,  4,  1,  3,
#         1,  5,  3,  0,  4,  2,  6,  6,  6,  0,  4,  1,  6,  2,  1,  1,  0,
#         6,  1,  6,  1,  4,  6,  6,  4,  1,  6,  1,  1,  3,  6,  1,  1,  1,
#         6, -1,  5,  5,  4,  1,  3,  5,  5,  6,  1,  5,  3,  1,  3,  6,  6,
#         1,  5,  6,  5,  6,  0,  0,  2,  6,  3,  4,  6,  3], dtype=int64)

#################
# 绘制散点图
#################
# 绘制簇分配
plt.scatter(X_scaled[:, 0], X_scaled[:, 1], c=clusters, cmap=mglearn.cm2, s=60)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")

plt.show()

009 无监督学习 数据聚类_真实值评估

# 数据聚类_真实值评估

# 概要: 
#   1. 创建数据
#   2. 绘制4副图
#       2.1 利用随机数打乱原有结果的绘图
#       2.2 K均值聚类计算结果的绘图
#       2.3 凝聚聚类计算结果的绘图
#       2.4 带有噪点的空间凝聚计算结果的绘图
#   3. 使用聚类评估函数将结果与真实值之间进行比对

# 结论: 对于ARI来说 取值是0~1  越趋近于1 模型越完美.

import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics.cluster import adjusted_rand_score
# from sklearn.metrics.cluster import normalized_mutual_info_score
from sklearn.cluster import KMeans
from sklearn.cluster import DBSCAN
from sklearn.cluster import AgglomerativeClustering
from sklearn.datasets import make_moons
from sklearn.preprocessing import StandardScaler


###############
# 创建数据
###############
X, y = make_moons(n_samples=200, noise=0.05, random_state=0)

###############
# 缩放数据
###############
# 将数据缩放成平均值为0、方差为1
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

###############
# 创建4个绘图
###############
fig, axes = plt.subplots(1, 4, figsize=(15, 3),
                         subplot_kw={'xticks': (), 'yticks': ()})
# 列出要使用的算法
algorithms = [KMeans(n_clusters=2), AgglomerativeClustering(n_clusters=2),
              DBSCAN()]

###############
# 第一个图就是加入随机数,尽量让结果与实际不符来验证adjusted_rand_score
###############
# 创建一个随机的簇分配,作为参考
random_state = np.random.RandomState(seed=0)
random_clusters = random_state.randint(low=0, high=2, size=len(X))

# In [2]: random_clusters
# Out[2]: 
# array([0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1,
#        1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1,
#        1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0,
#        0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1,
#        1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0,
#        1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1,
#        1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1,
#        0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0,
#        1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1,
#        0, 0])

# 绘制随机分配
axes[0].scatter(X_scaled[:, 0], X_scaled[:, 1], c=random_clusters,
                cmap=mglearn.cm3, s=60)
axes[0].set_title("Random assignment - ARI: {:.2f}".format(
    adjusted_rand_score(y, random_clusters)))

###############
# 将其它3中算法的结果值与真实值进行比较
###############
for ax, algorithm in zip(axes[1:], algorithms):
    # 绘制簇分配和簇中心
    clusters = algorithm.fit_predict(X_scaled)
    ax.scatter(X_scaled[:, 0], X_scaled[:, 1], c=clusters,
               cmap=mglearn.cm3, s=60)
    ax.set_title("{} - ARI: {:.2f}".format(algorithm.__class__.__name__,
                                           adjusted_rand_score(y, clusters)))

plt.show()

010 无监督学习 数据聚类_无真实值评估_轮廓系数

# 数据聚类_无真实值评估_轮廓系数

# 概要:
#   1. 创建数据
#   2. 绘制4副图
#       2.1 利用随机数打乱原有结果的绘图
#       2.2 K均值聚类计算结果的绘图
#       2.3 凝聚聚类计算结果的绘图
#       2.4 带有噪点的空间凝聚计算结果的绘图
#   3. 使用轮廓系数将结果与真实值之间进行比对

# 结论: 轮廓系数反映的是数据点的凝聚力,评估值并不能很好的反应模型真实水平
# 从得分中可以看出,DBSCAN的得分竟然比K均值聚类还低

import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics.cluster import silhouette_score
from sklearn.datasets import make_moons
from sklearn.cluster import KMeans
from sklearn.cluster import AgglomerativeClustering
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler

# 创建数据集
X, y = make_moons(n_samples=200, noise=0.05, random_state=0)

# 将数据缩放成平均值为0、方差为1
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)

# 准备绘图模板
fig, axes = plt.subplots(1, 4, figsize=(15, 3),
                         subplot_kw={'xticks': (), 'yticks': ()})

# 用随机数打乱结果,作为第一张图
# 创建一个随机的簇分配,作为参考
random_state = np.random.RandomState(seed=0)
random_clusters = random_state.randint(low=0, high=2, size=len(X))
# 绘制随机分配
axes[0].scatter(X_scaled[:, 0], X_scaled[:, 1], c=random_clusters,
                cmap=mglearn.cm3, s=60)
axes[0].set_title("Random assignment: {:.2f}".format(
    silhouette_score(X_scaled, random_clusters)))

# 绘制另外三个模型的计算结果
algorithms = [KMeans(n_clusters=2), AgglomerativeClustering(n_clusters=2),
              DBSCAN()]
for ax, algorithm in zip(axes[1:], algorithms):
    clusters = algorithm.fit_predict(X_scaled)
    # 绘制簇分配和簇中心
    ax.scatter(X_scaled[:, 0], X_scaled[:, 1], c=clusters, cmap=mglearn.cm3,
            s=60)
    
    # 看看各个模型的轮廓评估得分
    ax.set_title("{} : {:.2f}".format(algorithm.__class__.__name__,
                                    silhouette_score(X_scaled, clusters)))

plt.show()

011 无监督学习 数据聚类_使用DBSCAN计算人脸数据

# 数据聚类_使用DBSCAN计算人脸数据

# - 带有噪点的空间聚类
#   - 聚类特点:
#     - 无需指定簇个数,
#     - 可以控制噪声,
#     - 无法聚集大簇
#     - 簇的分布可能不太均匀

# 概要:
#     1. 使用人脸数据,并控制每人最多只能有50张图
#     2. 使用PCA提取人脸识别的主要成分
#     3. 查看DBSCAN在PCA过滤后数据上的表现
#     4. 学会如何调整DBSCAN的最适参数
#     5. 使用最佳参数应用DBSCAN,将聚合后的簇的人脸图片打印出来

# 观察结果:
#     预想结果: 通过最佳参数的DBSCAN计算的簇中的图片应当尽量接近
#     实际结果: 有的簇会出现完全不同的人脸图片

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.datasets import fetch_lfw_people
from sklearn.cluster import DBSCAN

##########################
# 人脸数据
##########################
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape

# 每人人脸图像采样最多50个
mask = np.zeros(people.target.shape, dtype=np.bool)
for target in np.unique(people.target):
    mask[np.where(people.target == target)[0][:50]] = 1
X_people = people.data[mask]
y_people = people.target[mask]

##########################
# 人脸数据
##########################
# 这里用PCA预处理一下数据,因为pca不用考虑方向,对人脸数据识别较准确
# 而且适用的变换法可以缩减需要判断的数据源能效提升运行效率
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)

##########################
# 使用带噪点的聚类算法看看主成分数据效果
##########################
# 应用默认参数的DBSCAN
dbscan = DBSCAN()
labels = dbscan.fit_predict(X_pca)
print("Unique labels: {}".format(np.unique(labels)))

# 很遗憾, DBSCAN把所有的主成分都算成噪点了. (-1 表示噪点)
# In [2]: print("Unique labels: {}".format(np.unique(labels)))
# Unique labels: [-1]

# 回想一下DBSCAN算法的重要参数, min_samples 和 eps. 即数据点数和范围
# 默认情况下之找出一类,说明参数还需要调整. 因为PCA本身没有问题.

##########################
# 如何找到DBSCAN的最佳参数设置
##########################
# 这里就是举例说明, 不要纠结为什么用1,3,5,,,这个列表,
# 在实际使用中,步长可能会拆的更细
for eps in [1, 3, 5, 7, 9, 11, 13]:
    print("\neps={}".format(eps))
    dbscan = DBSCAN(eps=eps, min_samples=3)
    labels = dbscan.fit_predict(X_pca)
    print("Clusters present: {}".format(np.unique(labels)))
    print("Cluster sizes: {}".format(np.bincount(labels + 1)))

# 输出结果:
# 在我们已经大体掌训练数据分类个数的基础上,分析算法结果可以得出
# 1. 计算结果簇的个数越接近训练数据的分类数,模型越完美
# 2. 每个簇的成员个数最多不能超过50(训练数据人脸个数限定出现50个)
# 3. 计算结果表现上来看,DBSCAN的eps参数并不是越大越好

# 目前来看eps=7时, 分类个数较其它情况要细一些,但仍然与真实结果相差不小.
# 例子中假定了min_sample=3, 实际上,我们也可以用嵌套循环的方法使用不同取值来反复验证
# min_sample和eps的计算结果,然后通过簇个数和核心数来与真实数据比较以确定最佳参数.
# 如果我们手里拿到的训练数据,没有目标值的时候,可以将簇种类较多和簇成员较均匀的簇的图片打出来验证算法.

# eps=1
# Clusters present: [-1]
# Cluster sizes: [1272]

# eps=3
# Clusters present: [-1]
# Cluster sizes: [1272]

# eps=5
# Clusters present: [-1]
# Cluster sizes: [1272]

# eps=7
# Clusters present: [-1  0  1  2  3  4  5  6  7]
# Cluster sizes: [1229    3   20    5    3    3    3    3    3]

# eps=9
# Clusters present: [-1  0  1  2]
# Cluster sizes: [807 458   4   3]

# eps=11
# Clusters present: [-1  0]
# Cluster sizes: [283 989]

# eps=13
# Clusters present: [-1  0]
# Cluster sizes: [  91 1181]


##########################
# 使用较精准的参数来设置DBSCAN,看看找出的图片
##########################
# 我们假设通过上面的方法已经找出了min_samples = 3, eps = 7
dbscan = DBSCAN(min_samples=3, eps=7)
labels = dbscan.fit_predict(X_pca)
# In[20]: labels.shape
# Out[20]: (1272,)

# 这里的循环排除了DBSCAN认为是噪点的图片
# -1 为噪点
for cluster in range(max(labels) + 1):
    # 跟控制人脸出现次数时使用的方法一致
    # 先设定一个值,然后将数组中与这个值相同的位置标记成true,其它的标记成false
    # 然后用标记好的坐标去筛选数组.
    mask = labels == cluster
    
    # 由于mask里存的是位置的True和False. True为1 False为0 
    # sum计算的是本次循环满足cluster值的簇个数总数
    n_images = np.sum(mask)
    fig, axes = plt.subplots(1, n_images, figsize=(n_images * 1.5, 4),
                             subplot_kw={'xticks': (), 'yticks': ()})
    for image, label, ax in zip(X_people[mask], y_people[mask], axes):
        # ax.imshow(image.reshape(image_shape), vmin=0, vmax=1)
        ax.imshow(image.reshape(image_shape))
        ax.set_title(people.target_names[label].split()[-1])

plt.show()

011 无监督学习 数据聚类_使用K均值聚类计算人脸数据

# 数据聚类_使用K均值聚类计算人脸数据

# - K均值聚类
# - 聚类特点:
#     - 算法需指定最终簇个数(簇个数可任意设定), 簇中心为这个簇的平均值.
#     - 无法控制噪声.
#     - 簇中成员分布均匀
    
# 概要:
#     1. 使用人脸数据,并控制每人最多只能有50张图
#     2. 使用PCA提取人脸识别的主要成分
#     3. 查看KMeans在PCA过滤后数据上的表现

# 观察结果:
#     因为KMeans算法是计算簇的平均值,因此,簇中心点重建后的图片更趋近于大众脸
#   也就是簇中成员图片的平局表现.
#     KMeans算法的起始点是随机的,因此结果会与我的有不一致的地方
#   同样也应为平局值得特性,结果并不太让人满意.例子中使用了10个簇来调用kmeans,
#   当然个数可以随意指定,这点在之前的例子中已经验证过了,簇得个数指定越多,说明细节关注就越多,
#   理论上是可以但是太多得细节反而观察起来麻烦.
#   比如: 设置100个簇, Kmeans也成功合成了100个簇, 现在需要找到头向上,手捂住嘴,人物是xxx的图片,
#       那么就需要先逐一排查100个簇,先找到哪组簇是符合上面规则的,才能做下一步.这一步只能人工完成,
#       因为Kmenas算法在合成簇的参数里,并没有能设置其它参数的机会,只能单纯的合成簇,至于合成的簇是什么
#       Kmeans并不知道是什么,也不知道怎么用,也是比较符合无人监督算法地.

    
import mglearn
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.datasets import fetch_lfw_people
from sklearn.cluster import KMeans

##########################
# 人脸数据
##########################
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
# print(image_shape)

# 每人人脸图像采样最多50个
mask = np.zeros(people.target.shape, dtype=np.bool)
for target in np.unique(people.target):
    mask[np.where(people.target == target)[0][:50]] = 1
X_people = people.data[mask]
y_people = people.target[mask]

##########################
# 人脸数据
##########################
# 这里用PCA预处理一下数据,因为pca不用考虑方向,对人脸数据识别较准确
# 而且适用的变换法可以缩减需要判断的数据源能效提升运行效率
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)

km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)
print("Cluster sizes k-means: {}".format(np.bincount(labels_km)))

# 单从簇成员来看,成员分布情况较DBSCAN要好一些
# 但是个数上大多超过了50, 说明准确性还是差了一些
# In [2]: print("Cluster sizes k-means: {}".format(np.bincount(labels_km)))
# Cluster sizes k-means: [ 95 358 122 121  10  73  93  87 190 123]


##########################
# 重建簇中图片并显示
##########################
# 注意: 是重建,不是直接显示,因此需要使用inverse_transform来补充被PCA算法删减的数据
# vmin, vmax 参数不知到怎么回事,设置上图片就打印不出来.
fig, axes = plt.subplots(2, 5, subplot_kw={'xticks': (), 'yticks': ()},
                         figsize=(12, 4))
for center, ax in zip(km.cluster_centers_, axes.ravel()):
    ax.imshow(pca.inverse_transform(center).reshape(image_shape),
            vmin=0, vmax=1)

##########################
# 重建簇中图片并显示
##########################
# plot_kmeans_faces 没有函数帮助, 接口大概都能看懂.
# 算法内步骤:
# 1. 使用PCA结果重建图像 放在第一列位置
# 2. 用KMeans结果中的簇的前5个数据和后5个数据重建图像,放到2~11列位置
# 3. 画3个框 框起来 1
mglearn.plots.plot_kmeans_faces(km, pca, X_pca, X_people,
                                y_people, people.target_names)

plt.show()

011 无监督学习 数据聚类_使用凝聚聚类计算人脸数据

# 数据聚类_使用凝聚聚类计算人脸数据

# - 凝聚聚类
#   - 聚类特点:
#     - 算法需指定最终簇个数(簇个数可任意设定).
#     - 需指定寻找相近值得规则.
#     - 无法控制噪声
#     - 簇中成员分布均匀

# 概要:
#     1. 使用人脸数据,并控制每人最多只能有50张图
#     2. 使用PCA提取人脸识别的主要成分
#     3. 查看凝聚聚类(ward 方差最小)在PCA过滤后数据上的表现
#     4. 评估Kmenas与凝聚聚类所形成簇的相似度
#     5. 重建凝聚聚类簇人脸图像

# 观察结果:
#     同Kmeans出现了同样的问题,簇的形成时基于相似度参数来完成的(使用的ward 默认值),
#   但是算法并不直到用户关心的合成是哪方面的,因此会按照自己的规则来寻找相似图片并形成簇.
#   此时如果想寻找指定特征的图片,还是得需要人工参与.
#     比Kmeans算法好的一点是,凝聚聚类算法所形成得簇,成员相似度较高,如果拿到得簇内容误差太大
#   可以增加算法中簇的个数,个数越细,精准度越高,相似度越高,当然人工抽检成本也越高.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.datasets import fetch_lfw_people
from sklearn.cluster import AgglomerativeClustering
from sklearn.cluster import KMeans
from sklearn.metrics import adjusted_rand_score

##########################
# 人脸数据
##########################
people = fetch_lfw_people(min_faces_per_person=20, resize=0.7)
image_shape = people.images[0].shape
# print(image_shape)

# 每人人脸图像采样最多50个
mask = np.zeros(people.target.shape, dtype=np.bool)
for target in np.unique(people.target):
    mask[np.where(people.target == target)[0][:50]] = 1
X_people = people.data[mask]
y_people = people.target[mask]

##########################
# 人脸数据
##########################
# 这里用PCA预处理一下数据,因为pca不用考虑方向,对人脸数据识别较准确
# 而且适用的变换法可以缩减需要判断的数据源能效提升运行效率
pca = PCA(n_components=100, whiten=True, random_state=0)
pca.fit_transform(X_people)
X_pca = pca.transform(X_people)


##########################
# 凝聚聚类
##########################
agglomerative = AgglomerativeClustering(n_clusters=10)
labels_agg = agglomerative.fit_predict(X_pca)
print("Cluster sizes agglomerative clustering: {}".format(
    np.bincount(labels_agg)))

# 簇的成员数分布不太均匀, 差于Kmenas, 但好于DBSCAN
# Cluster sizes agglomerative clustering: [529 376  64 114  31  39   4  66   8  41]

##########################
# 比较凝聚聚类与K均值聚类形成簇的相似度
##########################
# 聚类比较之前介绍过,在有真实值的情况下可以用 adjusted_rand_score来评估相似度
km = KMeans(n_clusters=10, random_state=0)
labels_km = km.fit_predict(X_pca)
print("ARI: {:.2f}".format(adjusted_rand_score(labels_agg, labels_km)))

# 得分很低
# 原因:
#   Kmeans聚集算平均值,无差别聚集,因此原理核中心点,可能偏差很大
#   凝聚聚类每个簇中的成员都或多或少的相似,
#   因此在比较这两个簇时会出现得分很低的情况.
# ARI: 0.09


##########################
# 显示凝聚聚类找到的簇中人像
##########################
# 凝聚聚类没有像Kmeans那样的簇中心点,但是可以通过簇间接得到簇个数.
# 这里直接定义常量了,简单明了
n_clusters = 10
for cluster in range(n_clusters):
    mask = labels_agg == cluster
    fig, axes = plt.subplots(1, 10, subplot_kw={'xticks': (), 'yticks': ()},
                             figsize=(15, 8))
    axes[0].set_ylabel(np.sum(mask))
    for image, label, asdf, ax in zip(X_people[mask], y_people[mask],
                                      labels_agg[mask], axes):
        ax.imshow(image.reshape(image_shape))
        ax.set_title(people.target_names[label].split()[-1],
                     fontdict={'fontsize': 9})

plt.show()

第四章 数据表示与特征工程

001 数据变换&表示_分类数据的N取一方案

# 分类数据_读取并显示CSV数据

# 概要:
# 1. 使用panada来读取CSV数据
# 2. 挑选感兴趣的列,缩小数据范围
# 3. 将缩小的数据中,计算机无法理解的内容通过N取一的方案进行统一转换
# 4. 剔除预测结果列(为了让实际结果不影响模型的预测结果)
# 5. 创建模型, 拆分数据, 喂数据, 测试模型得分


from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
# 库最然重了点但相比较是最容易理解及实现的解析CSV的库
import pandas as pd
# Ipython重要用于终端交互,对控制台的输出有颜色主题及格式化,比较适合演示
from IPython.display import display

# CSV_FILE = "data/adult.data"
# 这个文件中的数据共有15列,只有数据没有title
# CSV_FILE = r"D:\010_WorkSpace\000_VirtualEnv\.python_machine_learn\Lib\site-packages\mglearn\data\adult.data"
CSV_FILE = r"D:\001_Work\002_DevelopSource\VirtualEnv_Manager\.venv_Python_Machine_Learn\Lib\site-packages\mglearn\data\adult.data"

# 使用panadas来打开csv文件,顺便把每列的列名给加上.
# 列名可以随便起,新定义的名字将作为对应列的标识,内部应当就是个大dict.
data = pd.read_csv(
    CSV_FILE, header=None, index_col=False,
    names=['age', 'workclass', 'fnlwgt', 'education', 'education-num',
           'marital-status', 'occupation', 'relationship', 'race', 'gender',
           'capital-gain', 'capital-loss', 'hours-per-week', 'native-country',
           'income'])

# 读取后的CSV文件因为附加了列标识,因此可以随意选用其中的列
# 为了便于说明,我们只选了其中几列具有代表性的.
data = data[['age', 'workclass', 'education', 'gender', 'hours-per-week',
             'occupation', 'income']]

# IPython.display可以在Jupyter notebook中输出漂亮的格式
display(data.head(10))

# Out[16]:
#    age          workclass   education   gender  hours-per-week          occupation  income
# 0   39          State-gov   Bachelors     Male              40        Adm-clerical   <=50K
# 1   50   Self-emp-not-inc   Bachelors     Male              13     Exec-managerial   <=50K
# 2   38            Private     HS-grad     Male              40   Handlers-cleaners   <=50K
# 3   53            Private        11th     Male              40   Handlers-cleaners   <=50K
# 4   28            Private   Bachelors   Female              40      Prof-specialty   <=50K
# 5   37            Private     Masters   Female              40     Exec-managerial   <=50K
# 6   49            Private         9th   Female              16       Other-service   <=50K
# 7   52   Self-emp-not-inc     HS-grad     Male              45     Exec-managerial    >50K
# 8   31            Private     Masters   Female              50      Prof-specialty    >50K
# 9   42            Private   Bachelors     Male              40     Exec-managerial    >50K

# 每个列都有value_counts的方法,用于显示内容个数
# 下面结果是确认gender列都有什么值以及值的个数
# 通过确认发现只有两种值 Male和Female.
print(data.gender.value_counts())
#  Male      21790
#  Female    10771
# Name: gender, dtype: int64

# get_dummies用于简单实现N取一
# 我们就以gender为例:
# 原来的gender列现在变成了 'gender_ Female', 'gender_ Male'
# 新列的命名方式为: 原始列名_ 值内容  (注意下划线后有一个空格)
data_dummies = pd.get_dummies(data)
print(data_dummies.columns)
# Index(['age', 'hours-per-week', 'workclass_ ?', 'workclass_ Federal-gov',
#        'workclass_ Local-gov', 'workclass_ Never-worked', 'workclass_ Private',
#        'workclass_ Self-emp-inc', 'workclass_ Self-emp-not-inc',
#        'workclass_ State-gov', 'workclass_ Without-pay', 'education_ 10th',
#        'education_ 11th', 'education_ 12th', 'education_ 1st-4th',
#        'education_ 5th-6th', 'education_ 7th-8th', 'education_ 9th',
#        'education_ Assoc-acdm', 'education_ Assoc-voc', 'education_ Bachelors',
#        'education_ Doctorate', 'education_ HS-grad', 'education_ Masters',
#        'education_ Preschool', 'education_ Prof-school',
#        'education_ Some-college', 'gender_ Female', 'gender_ Male',
#        'occupation_ ?', 'occupation_ Adm-clerical', 'occupation_ Armed-Forces',
#        'occupation_ Craft-repair', 'occupation_ Exec-managerial',
#        'occupation_ Farming-fishing', 'occupation_ Handlers-cleaners',
#        'occupation_ Machine-op-inspct', 'occupation_ Other-service',
#        'occupation_ Priv-house-serv', 'occupation_ Prof-specialty',
#        'occupation_ Protective-serv', 'occupation_ Sales',
#        'occupation_ Tech-support', 'occupation_ Transport-moving',
#        'income_ <=50K', 'income_ >50K'],
#       dtype='object')

# 从新创建的列名我们可以发现,重新创建的列都是计算机无法直接识别的文字,对于计算机能理解的
# 数字,并没有创建新列,比如 列:age, 调用方法前后没有变化.
# 接下来看一下新列中的内容,跟预想的一样全部变成数值了
print(data_dummies.head(10))
#    age  hours-per-week  workclass_ ?  workclass_ Federal-gov  ...  occupation_ Tech-support  occupation_ Transport-moving  income_ <=50K  income_ >50K
# 0   39              40             0                       0  ...                         0                             0              1             0
# 1   50              13             0                       0  ...                         0                             0              1             0
# 2   38              40             0                       0  ...                         0                             0              1             0
# 3   53              40             0                       0  ...                         0                             0              1             0
# 4   28              40             0                       0  ...                         0                             0              1             0
# 5   37              40             0                       0  ...                         0                             0              1             0
# 6   49              16             0                       0  ...                         0                             0              1             0
# 7   52              45             0                       0  ...                         0                             0              0             1
# 8   31              50             0                       0  ...                         0                             0              0             1
# 9   42              40             0                       0  ...                         0                             0              0             1

# 书中使用的ix已经废弃了
# 我们这一步的目的是提取感兴趣的列,以及该列的内容.
# loc方法可以办到, 第一参数的范围,第二参数是列的范围, 起始列和终了列可以参照columns的输出结果
# 可以参考https://blog.csdn.net/sushangchun/article/details/83514803看看它的用法
# 提取列的方法知道了,但是为什么提取也得知道,模型结果参与模型计算会使得模型预测不准
# 通常预测模型需要把结果部分剔除,才可以正常建模
features = data_dummies.loc[:, 'age':'occupation_ Transport-moving']

# 现在又回到了已经学过的内容,用数据建模,预测结果
X = features.values
y = data_dummies['income_ >50K'].values


# 这里有个细节需要注意一下: 书中是将数据的整个集合来执行N取一方案,然后再拆分
# 如果先拆分再采用N取一方案的话有很大机率会使某一列做包含的种类不全,
# 而N取一又是通过值的位置来创建新列,因此可能会出现同一位置,训练数据和测试数据的内容不同,
# 导致创建的新列含义也不同,进而影响模型预测
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
print("Test score: {:.2f}".format(logreg.score(X_test, y_test)))
# Test score: 0.81

001 数据变换&表示_如何将数字列转换成分类项

# 分类数据_将数字数据转换成分类数据

# 概要:
# 1. 创建一个简单的额数据表格
# 2. 将数值列的数值转换成分类项

import pandas as pd
from IPython.display import display


# 还记得DataFrame吧,有段时间没用了.
# DataFrame把它简单理解成excel表格就好, 很多方法名都能对的上,实际使用上也差不多.
demo_df = pd.DataFrame({'Integer Feature': [0, 1, 2, 1],
                        'Categorical Feature': ['socks', 'fox', 'socks', 'box']})
display(demo_df)

#    Integer Feature Categorical Feature
# 0                0               socks
# 1                1                 fox
# 2                2               socks
# 3                1                 box

dummies_df = pd.get_dummies(demo_df)
# 可以看出来[Integer Feature]并没有转化,计算机认为它能理解这些值,但其实值只是个代号,全都理解错了
display(dummies_df)
#    Integer Feature  Categorical Feature_box  Categorical Feature_fox  Categorical Feature_socks
# 0                0                        0                        0                          1
# 1                1                        0                        1                          0
# 2                2                        0                        0                          1
# 3                1                        1                        0                          0

# 将需要的列类型强转一下, 数字转字符串很容易,没什么需要多考虑的.
demo_df['Integer Feature'] = demo_df['Integer Feature'].astype(str)

dummies_df_1 = pd.get_dummies(demo_df)
# 已经可以正常转换了.
display(dummies_df_1)

#    Integer Feature_0  Integer Feature_1  ...  Categorical Feature_fox  Categorical Feature_socks
# 0                  1                  0  ...                        0                          1
# 1                  0                  1  ...                        1                          0
# 2                  0                  0  ...                        0                          1
# 3                  0                  1  ...                        0                          0

# [4 rows x 6 columns]

# 也可以单独看指定列的转换结构
dummies_df_2 = pd.get_dummies(demo_df, columns=['Integer Feature'])
display(dummies_df_2)

#   Categorical Feature  Integer Feature_0  Integer Feature_1  Integer Feature_2
# 0               socks                  1                  0                  0
# 1                 fox                  0                  1                  0
# 2               socks                  0                  0                  1
# 3                 box                  0                  1                  0

002 数据表示与特征工程_离散化线性数据

# 分类数据_离散化线性数据

# 概要:
# 1. 准备用于训练箱子模型的数据:
#   1.1. 创建波形数据
#   1.2. 将波形数据进行分箱处理
# 2. 创建N取一方案模型用于解析分箱数据
# 3. 训练N取一方案模型
#   3.1 用分箱后的波形数据开始训练箱子模型
# 4. 创建新的线性数据用于线性模型
# 5. 训练线性模型
#   5.1 使用波形数据训练的N取一模型来辅助训练线性模型(训练时的数据均来自波形数据)
# 6. 使用线性模型预测新数据
#   6.1 将新线性数据先分箱后再让线性模型处理
#       (现在的数据通道是 数据 -> 分箱 -> N取一模型 -> 线性模型)
#          因此数据必须有分箱操作,线性模型才能理解

import mglearn
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from IPython.display import display

# 线性模型
from sklearn.linear_model import LinearRegression
# 跟pandas的get_dummies方法类似的N取一方案.
from sklearn.preprocessing import OneHotEncoder

###################
# 创建波形数据
###################
X, y = mglearn.datasets.make_wave(n_samples=100)

###################
# 创建分箱
###################
# 创建10个箱子(11个元素就会有10个空隙, 这个空隙就是箱子)
bins = np.linspace(-3, 3, 11)
# In [25]: bins
# Out[25]: array([-3. , -2.4, -1.8, -1.2, -0.6,  0. ,  0.6,  1.2,  1.8,  2.4,  3. ])

###################
# 将波形数据进行分箱处理
###################
which_bin = np.digitize(X, bins=bins)
display(X[:5])
# array([[-0.75275929],
#        [2.70428584],
#        [1.39196365],
#        [0.59195091],
#        [-2.06388816]])

# 检查分箱是否有问题
display(which_bin[:5])
# [[ 4]
#  [10]
#  [ 8]
#  [ 6]
#  [ 2]]

'''
 分箱后的数据已经有线性数据转变成分类数据
 接下来将应用N取一方案,解析分类数据
'''
###################
# 创建N取一方案模型模型并用分型数据进行训练
###################
# sparse: True表示返回矩阵, False表示返回array
encoder = OneHotEncoder(sparse=False)
# 填充模型
encoder.fit(which_bin)
# 训练模型
X_binned = encoder.transform(which_bin)

# 我们来确认一下数据形状,看看是否只加列(特征)未加行(数据样本个数)
# 目前的数据列(特征)已经变成了10个,跟我们预测的一样
display(X_binned.shape)
# In [30]: X_binned.shape
# Out[30]: (100, 10)

# 内容方面也都是分类项目的位置信息,ok
display(X_binned[:5])
# In [31]: display(X_binned[:5])
# array([[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
#        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
#        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
#        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
#        [0., 1., 0., 0., 0., 0., 0., 0., 0., 0.]])

'''
 到目前为止我们只完成了箱子模型的训练,
 看样子机器学习中涉及到的模型,不管是直接作用于数据还是都辅助其它模型作用于数据,都需要单独训练才行.
 接下来我们需要结合之前学到的线性模型的使用方法的基础上,再增加箱子模型的使用.
'''
###################
# 创建线性数据,这个数据才是真正的作用于线性模型
###################
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)

###################
# 在用于线性模型之前,先用前面创建好的箱子模型进行预处理
###################
# 这里可能有人会问: 箱子模型都创建好了,也训练完了,直接[encoder.transform(line)]不就可以了吗?
#  大家可以看看上面的代码,箱子在训练之初,并不是直接使用波形数据,而是分箱后才训练的,
#  因此这里也必须先分箱,箱子模型才能识别同结构的数据
line_binned = encoder.transform(np.digitize(line, bins=bins))

###################
# 创建线性模型
###################
# 这里用到了之前学过的线性模型喂数据的方法,不同的是,本次不在是直接使用数据,而是经过箱子处理过的数据
# 另外接口中使用的y需要说一下:
# y是随着X在最初箱子建模时就产生的,因此不管X怎么分箱,箱子中的值仍然是X,只要一个X就一定有个y与之对应
# 这里虽然是用X_binned来喂数据,但实际参与的数据仍然是X,因此结果也必须用y
# 如此设定最终的效果是能让一个箱子中的值都是一样的,显示效果是箱子中的结果是一条与X轴平行的直线
reg = LinearRegression().fit(X_binned, y)

# 建模培训数据后,这个模型就可以正式使用了,使用的时候当然都要使用新的数据来预测.
# 只要注意现在的线性模型接受的是箱子数据即可.
plt.plot(line, reg.predict(line_binned), label='linear regression binned')

# 如果想打印原始点的轨迹可以用下面这个方法
# plt.plot(X[:, 0], y, 'o', c='k')
plt.vlines(bins, -3, 3, linewidth=1, alpha=.2)
plt.legend(loc="best")
plt.ylabel("Regression output")
plt.xlabel("Input feature")

plt.show()

003 特征工程_增加交互特征

# 数据分箱离散化_增加交互特征

'''
    概要:
    1. 创建用于训练N取一模型的波形数据
    2. 将创建好的波形数据先进行分箱处理.
        目的是将线性数据转变成分类数据
    3. 使用分箱后的波形数据训练N取一模型
    4. 将训练后的N取一模型上附加一个数据属性特征
    5. 用带有buff的N取一模型开始训练线性模型
    6. 创建测试数据,按照之前的步骤将数据进行预处理以及格式化
    
    总结:
    训练数据的整个过程:
        创建数据 -> 分箱 -> 训练N取一模型 -> 追加新的特征buff -> 训练线性模型
    
    测试数据的整个过程:
        创建数据 -> 分箱 -> N取一模型预处理 -> 追加新的特征buff -> 线性模型处理 -> 最终结果

'''

import mglearn
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from IPython.display import display
# 线性模型
from sklearn.linear_model import LinearRegression
# 跟pandas的get_dummies方法类似的N取一方案.
from sklearn.preprocessing import OneHotEncoder

###################
# 创建波形数据
###################
X, y = mglearn.datasets.make_wave(n_samples=100)

###################
# 创建分箱
###################
# 创建10个箱子(11个元素就会有10个空隙, 这个空隙就是箱子)
bins = np.linspace(-3, 3, 11)

###################
# 将波形数据进行分箱处理
###################
which_bin = np.digitize(X, bins=bins)

###################
# 创建N取一方案模型模型并用分型数据进行训练
###################
# sparse: True表示返回矩阵, False表示返回array
encoder = OneHotEncoder(sparse=False)
# 填充模型
encoder.fit(which_bin)
# 训练模型
X_binned = encoder.transform(which_bin)

###################
# 在训练完成的N取一模型上添加新的数据特征
###################
# 需要注意的是:一定要在模型训练之后再添加新的数据特征
#   因为这个特征是给线性模型用的,不是N取一模型用的.
# 另外:关于hstack的用法可以参照进行理解: https://blog.csdn.net/csdn15698845876/article/details/73380803/
# 通俗易懂的理解方式是:
#   将数据已矩形方式平铺在桌面上类似下面这样:
#    array([[1,  2,  3,  4],        |     array([[1],
#           [5,  6,  7,  8],        |            [2],
#           [9, 10, 11, 12],        |            [3],
#           [13, 14, 15, 16],       |            [4],
#           [17, 18, 19, 20]])      |            [5]])

#   接下来拿一个足够长的擀面杖来碾压这些数据, hstack表示水平碾压,vstack表示垂直碾压
#   需要注意的是,hstack从最上开始往下,vstack从最左开始往右
#   直至将整个数据处理完毕.
#   hstack和vstack都是从stack演化来的,stack属于自定义堆叠函数,可以自由定义堆叠的深度,更灵活
#   hstack和vstack则是自动寻找深度,并且附带方向.

# 回到本例中,例中使用hstack即水平方向自上而下堆叠数据,根据上面的数据形状我们很容观察到
# 每次堆叠得到的数据其实都是单条数据的所有数据特征也就是y列
# X_binned.shape -> (100, 10)   对应的y为10
# X.shape.shape ->  (100, 1) 对应的y为1
# 因此合并两个数的结果即为11个元素
X_combined = np.hstack([X, X_binned])

# 对照纵向堆叠后的输出结果
# In [6]: X_binned[:2]
# Out[6]: 
# array([[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
#        [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]])

# In [7]: X_combined[:2]
# Out[7]: 
# array([[-0.75275929,  0.        ,  0.        ,  0.        ,  1.        ,
#          0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
#          0.        ],
#        [ 2.70428584,  0.        ,  0.        ,  0.        ,  0.        ,
#          0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
#          1.        ]])


###################
# 训练线性模型
###################
# In [14]: X_binned.shape
# Out[14]: (100, 10)
# In [13]: X_combined.shape
# Out[13]: (100, 11)
# In [9]: y.shape
# Out[9]: (100,)

# 注意看X_combined的形状,行方向任然是100,纵方向由10变成了11
# 说明hstack的确是纵方向堆叠,这样就相当于只追加数据特征而不增加数据个数.
# 因为记录数相同,因此预想结果的个数也相同,因此这里使用y没有问题.
reg = LinearRegression().fit(X_combined, y)

###################
# 创建本次需要处理的线性数据
###################
# 上面那一堆操作都是模型正式使用前的准备,这里开始才是真正的实操
# 同样的,按照现在线性模型能处理数据格式来格式化线性数据
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
line_binned = encoder.transform(np.digitize(line, bins=bins))
# 不要忘记在分箱后的数据上也追加一个特征
line_combined = np.hstack([line, line_binned])

# 可以开始看看模型处理的效果了
plt.plot(line, reg.predict(line_combined), label='linear regression combined')
for bin in bins:
    # 显示每个箱子的边界
    # 相当于plt.plot(tuple_x,tuple_y)
    # plt画图的时候取点方法: [(tuple_x[0], tuple_y[0]), (tuple_x[1], tuple_y[1],...)
    plt.plot([bin, bin], [-3, 3], ':', c='k')
    # 上个例子是用下面这个方法显示箱子边界
    # 感觉还是vlines好理解些
    # plt.vlines(bin, -3, 3, linewidth=1, alpha=.2)
    

plt.legend(loc="best")
plt.ylabel("Regression output")
plt.xlabel("Input feature")

# 把原始数据的分布图打出来做一个参考
plt.plot(X[:, 0], y, 'o', c='k')

plt.show()

003 特征工程_增加多个交互特征

# 数据分箱离散化_增加多项交互特征

'''
    概要:
    1. 创建用于训练N取一模型的波形数据
    2. 将创建好的波形数据先进行分箱处理.
        目的是将线性数据转变成分类数据
    3. 使用分箱后的波形数据训练N取一模型
    4. 将训练后的N取一模型上附加多个数据属性特征
    5. 用带有buff的N取一模型开始训练线性模型
    6. 创建测试数据,按照之前的步骤将数据进行预处理以及格式化
    
    总结:
    训练数据的整个过程:
        创建数据 -> 分箱 -> 训练N取一模型 -> 追加新的特征buff -> 训练线性模型
    
    测试数据的整个过程:
        创建数据 -> 分箱 -> N取一模型预处理 -> 追加新的特征buff -> 线性模型处理 -> 最终结果

'''

import mglearn
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from IPython.display import display
# 线性模型
from sklearn.linear_model import LinearRegression
# 跟pandas的get_dummies方法类似的N取一方案.
from sklearn.preprocessing import OneHotEncoder

###################
# 创建波形数据
###################
X, y = mglearn.datasets.make_wave(n_samples=100)

###################
# 创建分箱
###################
# 创建10个箱子(11个元素就会有10个空隙, 这个空隙就是箱子)
bins = np.linspace(-3, 3, 11)

###################
# 将波形数据进行分箱处理
###################
which_bin = np.digitize(X, bins=bins)

###################
# 创建N取一方案模型模型并用分型数据进行训练
###################
# sparse: True表示返回矩阵, False表示返回array
encoder = OneHotEncoder(sparse=False)
# 填充模型
encoder.fit(which_bin)
# 训练模型
X_binned = encoder.transform(which_bin)

###################
# 在训练完成的N取一模型上添加新的数据特征
###################
# 我们本次的目的是添加多个数据特征
'''
    回顾以前学过的内容,核支持向量机章节曾经用过这一方法来拓展新列,当时的方法是把指定列进行
放大或缩小,然后同样的使用hstack来扩充数据特征. 这种方法在当时来说比较好理解,因为毕竟完全是
由单一属性演化的,因此对模型来说,就相当于针对这个属性预测,是忽略还是更能精确.

    这一章用到了新的方法来扩充数据特征: 将目标特征列与其它特征相乘,通过交叉计算的方法拓展新属性.
这种方法因为融合了其它属性,感觉一定会对结果产生影响.

具体方法:
    1. 我们在拓展一个属性的时候使用的代码是: X_combined = np.hstack([X_binned, X])
        X_binned.shape -> (100, 10)
        X.shape        -> (100, 1)  (拓展的内容)
        现在我们需要想办法把X的特征变得更多
        简单的方法是: X * X_binned
        数据形状: (X * X_binned).shape -> (100, 10)
        关于形状的问题可以移步下面这个网址,内容整理的不错.
            https://www.cnblogs.com/zhjblogs/p/14725424.html 
            里面没说唯一没说清楚的是不同形状数组之间的点积(dot)计算方法:
            计算规则是 第一个数组的每一行 * 第二个数组的每一列,然后将点积放入所乘列的位置.
            因此计算结果的shape应当是第一个数组的行*第二个数组的列.
            (3,2) * (2,3) -> (3,3)              (2,3) * (3,2) -> (3,3)
                In [74]: c1
                Out[74]: 
                array([[1, 2, 3],
                       [4, 5, 6]])

                In [75]: c2
                Out[75]: 
                array([[1, 2],
                       [3, 4],
                       [5, 6]])

                In [76]: np.dot(c2,c1)
                Out[76]: 
                array([[ 9, 12, 15],
                       [19, 26, 33],
                       [29, 40, 51]])

                In [77]: np.dot(c1,c2)
                Out[77]: 
                array([[22, 28],
                       [49, 64]])
    
         经过计算(X * X_binned)可以得到(100, 10)的数据结构
         X_binned本身就是(100,10)的结构, 因此经过hstack处理后就得到了(100,20)的多特征数据结构
'''
X_combined = np.hstack([X_binned, X * X_binned])

###################
# 训练线性模型
###################
reg = LinearRegression().fit(X_combined, y)

###################
# 创建本次需要处理的线性数据
###################
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
# 分箱后用N取一模型进行预处理
line_binned = encoder.transform(np.digitize(line, bins=bins))
# 按照准备模型时使用的方法再将现在的数据扩充以一下.
line_combined = np.hstack([line_binned, line * line_binned])

# 可以开始看看模型处理的效果了
plt.plot(line, reg.predict(line_combined), label='linear regression combined')
for bin in bins:
    # 显示每个箱子的边界
    # 相当于plt.plot(tuple_x,tuple_y)
    # plt画图的时候取点方法: [(tuple_x[0], tuple_y[0]), (tuple_x[1], tuple_y[1],...)
    plt.plot([bin, bin], [-3, 3], ':', c='k')
    # 上个例子是用下面这个方法显示箱子边界
    # 感觉还是vlines好理解些
    # plt.vlines(bin, -3, 3, linewidth=1, alpha=.2)
    

plt.legend(loc="best")
plt.ylabel("Regression output")
plt.xlabel("Input feature")

# 把原始数据的分布图打出来做一个参考
plt.plot(X[:, 0], y, 'o', c='k')

plt.show()


'''
写到这里我一直在想一个问题: 
为什么现在20个数据特征就可以让每个箱子呈现不同的斜线,之前的例子中11个数据特征仅能呈现
多条相同斜率的斜线.11个特征也不少了呀.

我自己的理解:
    首先需要回顾一下现在的模型与第二章所学的线性模型在使用方法有什么不同(分箱 + N取一模型预处理 + 追加属性)
分箱是为了模型预处理格式化数据的,因此分箱可以忽略.现在只剩下 N取一模型和追加属性两个了.

    先来分析一下N取一模型:
        In [86]: X_binned
        Out[86]: 
        array([[0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
            [0., 0., 0., 0., 0., 0., 0., 0., 0., 1.],
            [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
        N取一模型就是简单的将数据进行分类,只会产生0,1两种内容,就是个开关的作用.
        预处理后的数据直接拿来训练线性模型,那么线性模型就只认箱子,而且分类后的箱子里啥也没有,
        也就是此时虽然有了10个数据特征,但都是箱子的特征,与箱子里的内容无关,因此出现直线也就不奇怪了.
        
    后来我们追加了一个数据特征 np.hstack([X, X_binned]),我们是在分箱后追加的特征,
        因而可以影响斜率,但是一个特征仅能产生一个斜率,因此会出现位置不同斜率相同的线.
        
    再后来我们追加了多个数据特征 np.hstack([X_binned, X * X_binned]),此时每个箱子已经有
        足够的属性来产生不同的斜率.表面上是产生了 20(相乘得到的特征数) - 10(箱子相关特征) = 10(参与计算的实际特征)
        但其实看下X_binned内容你会发现,分类项数组 * 实际数据的话,仅能在分类是1的时候有效,
        其它分类是0的位置即使乘于数据结果也是0. 
        所以表面上是10个特征参与计算斜率,其实就两个,不过2两点成一线,也够用了.
        
        In [90]: X_combined[0]
        Out[90]: 
        array([ 0.        ,  0.        ,  0.        ,  1.        ,  0.        ,
                0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
               -0.        , -0.        , -0.        , -0.75275929, -0.        ,
               -0.        , -0.        , -0.        , -0.        , -0.        ])

'''

003 特征工程_显示增加多项式特征

# 数据分箱离散化_显示增加原始特征的多项式

'''
处理概要:
    1.创建波形数据
    2.创建多项式模型
    3.添加波形数据的多项式
    4.创建线性模型,用上面处理好的数据进行训练
    5.用训练好的线性模型测试新的线性数据
'''

# 多项式处理函数
from sklearn.preprocessing import PolynomialFeatures

import mglearn
import numpy as np
import matplotlib.pyplot as plt
# 线性模型
from sklearn.linear_model import LinearRegression

###################
# 创建波形数据
###################
X, y = mglearn.datasets.make_wave(n_samples=100)

###################
# 创建多项式
###################
# 关于多项式函数参数与结果的关系参照:
#     https://www.cnblogs.com/liweiwei1419/p/9715702.html
poly = PolynomialFeatures(degree=10, include_bias=False)
poly.fit(X)
X_poly = poly.transform(X)

# In [132]: X.shape
# Out[132]: (100, 1)

# In [133]: X_poly.shape
# Out[133]: (100, 10)

# In [134]: PolynomialFeatures(degree=6, include_bias=False).fit_transform(X).shape
# Out[134]: (100, 6)

'''
由数据列扩展出的多项式数组较之前以交互方式生成的数组相比,内容要丰富的多
'''
# In [140]: X_poly[:2]
# Out[140]: 
# array([[-7.52759287e-01,  5.66646544e-01, -4.26548448e-01,
#          3.21088306e-01, -2.41702204e-01,  1.81943579e-01,
#         -1.36959719e-01,  1.03097700e-01, -7.76077513e-02,
#          5.84199555e-02],
#        [ 2.70428584e+00,  7.31316190e+00,  1.97768801e+01,
#          5.34823369e+01,  1.44631526e+02,  3.91124988e+02,
#          1.05771377e+03,  2.86036036e+03,  7.73523202e+03,
#          2.09182784e+04]])

'''
如果想了解多项式的产生过程可以通过以下方法获取title, title里会记录计算过程
x0表示x[0], 第一项中的x0其实想表达的意思是x[0]^1
'''
# In [142]: poly.get_feature_names()
# Out[142]: ['x0', 'x0^2', 'x0^3', 'x0^4', 'x0^5', 'x0^6', 'x0^7', 'x0^8', 'x0^9', 'x0^10']

###################
# 创建线性模型
###################
reg = LinearRegression().fit(X_poly, y)

###################
# 创建本次需要处理的线性数据
###################
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)
line_poly = poly.transform(line)

plt.plot(line, reg.predict(line_poly), label='polynomial linear regression')
plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel("Regression output")
plt.xlabel("Input feature")
plt.legend(loc="best")

plt.show()

'''
这次与之前的例子相比并没有出现分箱操作,为什么?
    首先的需要了解分箱的目的是什么.训练用的波形数据的数据特征只有一个,为了让这一个特征
能更详细的表达数据,因此才有了分箱,为了进一步表达箱子中的数据,进而又在箱子的基础上追加了
特征的交互特征.归根揭底都是因为数据特征个数不足而导致了线性模型无法更精确的表达数据含义.
多项式的原理是完全基于数据特征本身进行的扩展,能够提供足够的特征数用于模型表达,
因此无需额外的分箱操作.

本节涉及了一个经典模型: 多项式回归模型
    多项式回归模型: 多项式特征 + 线性回归模型

'''

003 特征工程_隐式增加多项式特征(核支持向量机SVM(针对分类))

# 特征工程_隐式增加多项式(核支持向量机SVM(分类))

'''
处理概要:
    1. 创建波形数据
    2. 创建用于分类数据分析的核支持向量机模型
    3. 训练&测试模型得分
    
备注:
    1. 早在第二章我们就接触过核支持向量机模型,当时使用的例子是SVC,因为只是停留在应用层面
    因此并没有深入的了解各个参数含义.SVM跟SVC同属于一个PKG,也是核支持向量机的一种.
    参考其它博客得知SVM应当善于处理分类问题.
    2. 因为SVM的最低层被编译成2进制文件,因此无法了解每个参数的具体作用,但是其使用的参数名
    与SVC大都一致,因此猜想参数的机制也差不多.
    3. 从程序的执行结果来看,即使未对数据进行(多项式,交互项,缩放等)预处理,模型的处理结果也
    相当给力,说明很有可能这些步骤很可能已经封装近函数内.
    4. 例子中只使用了gamma, gamma影响着分类项目决策边界的精确度,gamma越大,边界越精确,模型越过拟合.
    5. SVM还有一个重要参数是degree,但是不太理解,现在可以释怀了,它是提供给多项式用的参数,
    可以影响最终结果线的平滑度.
    
    关于SVM的简单讲解:
        https://blog.csdn.net/weixin_42104292/article/details/126038195
'''

import mglearn
import numpy as np
import matplotlib.pyplot as plt

# 处理回归问题的核支持向量机
from sklearn.svm import SVR

X, y = mglearn.datasets.make_wave(n_samples=100)
line = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)

for gamma in [1, 10]:
    svr = SVR(gamma=gamma).fit(X, y)
    plt.plot(line, svr.predict(line), label='SVR gamma={}'.format(gamma))

print(svr.score(X, y))
# 0.8014576797100137

plt.plot(X[:, 0], y, 'o', c='k')
plt.ylabel("Regression output")
plt.xlabel("Input feature")
plt.legend(loc="best")

plt.show()

003 特征工程_多项式对模型得分的影响

上面的例子中,模型的预测曲线貌似很不错,接下来我们要看看多项式等一些数据的预处理是如何影响模型得分.

# 特征工程_多项式模型评估

'''
    概要:
        确认数据预处理对模型的影响.
        
        1. 创建数据
        2. 将数据进行预处理
        3. 查看线性模型使用不同数据源后,模型的得分
        4. 查看核支持向量机模型,针对不同数据源的得分
'''

import matplotlib.pyplot as plt
from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
# 数据预处理: 缩放
from sklearn.preprocessing import MinMaxScaler
# 数据预处理: 多项式
from sklearn.preprocessing import PolynomialFeatures
# 标准线性模型: Ridge
# 适合处理多特征数据的模型
from sklearn.linear_model import Ridge


##############################
# 创建数据
##############################
boston = load_boston()
X_train, X_test, y_train, y_test = train_test_split(
    boston.data, boston.target, random_state=0)

##############################
# 数据预处理_1: 缩放
# 将数据统一缩放到一个标准,防止较大的值(噪点)影响模型结果
##############################
scaler = MinMaxScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# In [10]: X_train_scaled.shape
# Out[10]: (379, 13)

##############################
# 数据预处理_1: 多项式
# 使用多项式函数增加数据的交互项及属性个数,以尽量使曲线平滑
##############################
poly = PolynomialFeatures(degree=2).fit(X_train_scaled)
X_train_poly = poly.transform(X_train_scaled)
X_test_poly = poly.transform(X_test_scaled)

# In[11]: X_train_poly.shape
# Out[11]: (379, 105)

##############################
# 标准线性模型: Ridge
##############################
ridge = Ridge().fit(X_train, y_train)
print("Ridge Score without interactions: {:.3f}".format(
    ridge.score(X_test, y_test)))
# Ridge Score without interactions: 0.627

# 查看线性模型只使用 [缩放] 后的数据时,模型的表现及得分
ridge = Ridge().fit(X_train_scaled, y_train)
print("Ridge Score with scale: {:.3f}".format(
    ridge.score(X_test_scaled, y_test)))
# Ridge Score with scale: 0.621

# 查看线性模型 [使用缩放+多项式] 后的数据时,模型的表现及得分
ridge = Ridge().fit(X_train_poly, y_train)
print("Ridge Score with scale and poly: {:.3f}".format(
    ridge.score(X_test_poly, y_test)))
# Ridge Score with scale and poly: 0.753

'''
由此可见多项式数据更适合Ridge模型
'''

##############################
# 核支持向量机: SVR(回归)
##############################
# 使用前面例子中表现良好的SVR看看结果如何
from sklearn.svm import SVR

# 使用原始数据
svr = SVR().fit(X_train, y_train)
print("SVM score : {:.3f}".format(
    svr.score(X_test, y_test)))
# SVM score: 0.084

# 使用缩放数据
svr = SVR().fit(X_train_scaled, y_train)
print("SVM score with scale: {:.3f}".format(
    svr.score(X_test_scaled, y_test)))
# SVM score with scale: 0.475

# 使用缩放+多项式数据
svr = SVR().fit(X_train_poly, y_train)
print("SVM score with scale + poly self: {:.3f}".format(
    svr.score(X_train_poly, y_train)))
# SVM score with scale + poly self: 0.646
print("SVM score with scale + poly: {:.3f}".format(
    svr.score(X_test_poly, y_test)))
# SVM score with scale + poly: 0.447


# 尝试使用类说明中推荐的方法使用SVR
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

svr = make_pipeline(StandardScaler(), SVR(gamma='auto'))
svr.fit(X_train, y_train)
print(svr.score(X_train, y_train))
# 0.7200115985825024
print(svr.score(X_test, y_test))
# 0.5084292459321271


'''
前一个例子中SVR使用原始数据的表现比较好,但是这个例子中就差点意思,不知道问题出在哪.
之后用使用预处理过的数据分别又试了一下SVR变现,也还是不尽如人意.
这两天查了几个别人的博客,想试着从理论方面找到原因,奈何太久没碰数学,纯理论理解有点费劲,暂且作罢.
下面是比较好的博客,有兴趣的可以自己研究一下:

SVM函数用法:
    https://blog.csdn.net/sherry_gp/article/details/51823380
SVM各向量机的简单例子:
    https://zhuanlan.zhihu.com/p/518318434
SVM偏向理论的介绍(数学公式比较多):
    https://www.cnblogs.com/lsm-boke/category/1618523.html
SVM各向量机理论与例子:
    http://www.javashuo.com/article/p-bdmobndf-bs.html

由于我的需求主要偏向于应用,因此太深的理论问题暂时先放一放.
在调查了类的帮助文档后,偶然发现SVM在使用的时候也不全是直接用原始数据,
可以创建管道(make_pipeline)将预处理方法通过先进先出的方式塞进管道,然后再训练模型时自动调用
之前预设置的方法.从结果来看,已经趋近于Ridge模型的得分了.核支持向量机的优势在于参数可调,
相信微调某些参数,得分最终是会超过Ridge的,这里就暂时不去深研究了.
'''

##############################
# 核支持向量机: SVC(分类)
##############################
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

X = X_train
y = y_train
from sklearn.svm import SVC
clf = make_pipeline(StandardScaler(), SVC(gamma='auto'))
clf.fit(X, y)
print(svr.score(X, y))

#     189 y_type = type_of_target(y)
#     190 if y_type not in [
#     191     "binary",
#     192     "multiclass",
#    (...)
#     195     "multilabel-sequences",
#     196 ]:
# # --> 197     raise ValueError("Unknown label type: %r" % y_type)
#
# ValueError: Unknown label type: 'continuous'

'''
因为核支持向量机SVR的得分不高,因此怀疑所选用的模型可能不对,所以尝试选用其它的模型,看看结果能如何.

结果是: 训练模型的时候就报错,错误原因是SVC找不到y的分类. 因为SVC属于分类模型,因此y的每个结果都必须
是整数才可以,但是实际的y(如下)与模型不符,因此训练失败.
到目前为止接触过的每一个模型,都是在实际生活中产生的,都是为了更好的解决特定问题才出现的,
因此这些模型对数据都有特定的要求,想得到更好的结果还得需要记住各模型的特点以及数据特点才行.

# In[38]: y
# Out[38]:
# array([18.5, 19.6, 33.2, 13.1,  7.5, 13.6, 17.4,  8.4, 35.4, 24., 13.4,
#        26.2,  7.2, 13.1, 24.5, 37.2, 25., 24.1, 16.6, 32.9, 36.2, 11.,
# ....
#        18.5, 36.4, 19.2, 16.6, 23.1])
'''

004 特征预处理_单变量非线性变换

# 特征工程_单变量非线性变换

'''
概要:
    1. 创建测试数据
        1.1 创建随机数
        1.2 将随机数以指数形式新城新的数组
        1.3 使用泊松函数将新生成得数据处理成符合泊松理论得数据
        1.4 将做好得数据拆分成训练数据和测试数据
    2. 创建线性模型 Ridge
    3. 测试线性模型
        3.1 适应训练数据和测试数据分别测试模型得分
        3.2 将训练数据和测试数据进行缩放后分别测试模型得分
        3.3 将训练数据和测试数据进行对数运算后分别测试模型得分
'''

'''
结论: 
    1. 数据的预处理
        对于测试集(同属性,正数)里各值相差很大的处理对策(目前为止):
        1.1 缩放
            特点: 可以将整体数据控制在某个范围内,但是遇到复杂数据仅仅缩放可能改善不了结果
        1.2 对数运算
            特点: 由于0的对数没有意义,因此小于0的数据不太适合使用.
                对数处理可以在保留元数据各个数值关系的基础上有效缩小数据范围.
    2. 模型方面
        数据预处理对于简单模型,如本例的线性模型Ridge很有效,但是对于复杂模型
        比如树形模型,核支持向量机则没有多大效果,这也是我们在上个例子中无论怎么
        预处理数据SVM的表现都不尽如人意的原因.
        模型的底层都是由数学函数作为支撑,每个模型都是为了解决一个特定问题,
        因此找出函数善于处理的数据以及变换出函数能处理的数据是关键.
    3. 数据分析
        最难的因该是数据分析,拿这个例子来说,使用对数来预处理数据并不是因为我们知道
        数据是从指数运算演变而来的,大家要知道我们拿到的一手数据很多时候根本不知道数据之前经历了什么,
        因此先分析数据特点,特征尤为重要,之后才能少走弯路.
        对于大型数据的预演很耗时,挨个模型去试,不太现实.
    4. 学习路线
        现在看来书上的章节安排的挺合理, 模型->数据
        如果展开的话学习顺序应当是: 模型 -> 模型函数(数学函数) -> 数据处理 -> 数据分析 -> 源数据据
        机器学习是个很大的话题,书上那几页字不可能说的清楚,况且对应的库也很多,
        我们也只不过才用了sklearn,因此记数学函数和数据的关系才能一劳永逸.
        对于数据处理部分最好的办法也只能遇到一个记一个.

'''

from sklearn.linear_model import Ridge
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import numpy as np
import matplotlib.pyplot as plt

# 初始化随机类, 设置种子数为0,以便让第一次创建的随机结果都相同
rnd = np.random.RandomState(0)

# In [2]: rnd = np.random.RandomState(0)
#    ...: X_org = rnd.normal(size=(1000, 3))

# In [3]: rnd = np.random.RandomState(0)
#    ...: X_org1 = rnd.normal(size=(1000, 3))

# In [4]: X_org == X_org1
# Out[4]: 
# array([[ True,  True,  True],
#        [ True,  True,  True],
#        [ True,  True,  True],
#        ...,
#        [ True,  True,  True],
#        [ True,  True,  True],
#        [ True,  True,  True]])


# In [10]: rnd = np.random.RandomState(0)
# In [11]: x1 = rnd.normal(size=(1000, 3))
# In [12]: x2 = rnd.normal(size=(1000, 3))
# In [13]: x1 == x2
# Out[13]: 
# array([[False, False, False],
#        [False, False, False],
#        [False, False, False],
#        ...,
#        [False, False, False],
#        [False, False, False],
#        [False, False, False]])


# 创建1000个随机数
X_org = rnd.normal(size=(1000, 3))

# In [5]: X_org.shape
# Out[5]: (1000, 3)

# 创建3个随机数
w = rnd.normal(size=3)

# 这里有两个知识点:
# 1. 指数运算
#     以e为底,将每个随机数作为指数分别进行运算.numpy确实很强大,省去不少事儿.
# 2. 泊松处理
#     概念: https://baike.baidu.com/item/%E6%B3%8A%E6%9D%BE%E5%88%86%E5%B8%83/1442110
#     属于统计和概率方面的函数.善于处理周期与出现概率之间的问题.
#     关于函数的简单使用: https://blog.csdn.net/neweastsun/article/details/126417204
#     这里因为直接传了个数组,目标就是生成满足泊松分布的数组.
X = rnd.poisson(10 * np.exp(X_org))
# In [17]: X_org[:2]
# Out[17]: 
# array([[ 1.76405235,  0.40015721,  0.97873798],
#        [ 2.2408932 ,  1.86755799, -0.97727788]])

# In [18]: X[:2]
# Out[18]: 
# array([[56, 18, 27],
#        [81, 57,  3]])

# 将泊松随机矩阵用图形表现一下
bins_0 = np.bincount(X[:, 0])
bins_1 = np.bincount(X[:, 1])
bins_2 = np.bincount(X[:, 2])
plt.bar(range(len(bins_0)), bins_0)
plt.bar(range(len(bins_1)), bins_1)
plt.bar(range(len(bins_2)), bins_2)
plt.ylabel("Number of appearances")
plt.xlabel("Value")

# 泊松分布产生的随机数既有很大的值也有很小的值
# 多数模型不能很好的处理这种差别非常大的原始数据
# 如果按照之前学过的知识可能首选数据缩放,将数据控制在同一标准的合理范围之内,
# 但是这样模型依然找不出这些数据与y之间的关系,因为数据进行了过多的数学函数处理.
# plt.show()

# 将产生的泊松分布数组与随机数进行点积操作
y = np.dot(X_org, w)

X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
score = Ridge().fit(X_train, y_train).score(X_test, y_test)
print("Test score: {:.3f}".format(score))
# Test score: 0.622

# 尝试使用最常用的缩放数据的方法看看模型的得分
X_train_scale = StandardScaler().fit_transform(X_train)
X_test_scale = StandardScaler().fit_transform(X_test)
score_scale = Ridge().fit(X_train_scale, y_train).score(X_test_scale, y_test)
print("Test score: {:.3f}".format(score))
# Test score: 0.622

# 得分一样? 是缩放没成功吗? 并不是,从结果来看缩放本事并没有问题,问题是模型仍然无法理解数据
# In[167]: X_train
# Out[167]:
# array([[5,  1, 12],
#        [1, 15,  5],
#        [16,  5,  3],
#        ...,
#        [37,  7, 17],
#        [7, 85, 11],
#        [2, 20, 10]])

# In[168]: X_train_scale
# Out[168]:
# array([[-0.61024595, -0.74523262, -0.17734398],
#        [-0.84729605,  0.00385244, -0.55201435],
#        [0.0416418, -0.53120832, -0.65906303],
#        ...,
#        [1.28615478, -0.42419617,  0.09027772],
#        [-0.49172091,  3.74927775, -0.23086832],
#        [-0.78803352,  0.27138282, -0.28439265]])

# In [143]: y[:2]
# Out[143]: array([2.92614361, 4.74436373])

# 对于值相差很大的值还有一种方法是把它们变成对数,这样不仅可以做小值还能保留各个值之前的关系
# 对数0没有意义,因此所正数全部向右平移一个单位
X_train_log = np.log(X_train + 1)
X_test_log = np.log(X_test + 1)

# 经过处理后的数据再次用模型测试得分已经有很大改善
score = Ridge().fit(X_train_log, y_train).score(X_test_log, y_test)
print("Test score: {:.3f}".format(score))
# Test score: 0.875

005 特征选择_单变量统计

# 特征选择_单变量统计
'''
概要:
    1. 创建数据
        1.1 创建原始数据
        1.2 在原始数据中增加噪点
    2. 创建单变量统计模型
        2.1 训练模型
        2.2 使用模型提供的get_support方法查看筛选结果
    3. 创建线性模型
        3.1 使用原始数据训练模型&查看得分
        3.2 使用点变量统计模型处理后的数据训练线性模型&查看得分
'''

'''
结论:
    数据预处理之单变量统计在处理过程中有损失,在筛查噪点的同时会损失一部分的原始数据特征
    最终会导致模型得分低于原始数据的模型得分.
    优点是加快模型执行效率.

    模型存在的意义:
        对于复杂数据建模困难的时候,不妨用此模型筛选一遍,减少工作量而且提高效率.
    这个模型应当数据数据预处理还靠前那一阶段的工作(数据分析,判断).
'''

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split


# 特征选择(按百分比过滤)
from sklearn.feature_selection import SelectPercentile
# 特征选择(按个数过滤)
from sklearn.feature_selection import SelectKBest


########################
# 创建数据并增加干扰用的噪点
########################
cancer = load_breast_cancer()
# 获得确定性的随机数
rng = np.random.RandomState(42)
noise = rng.normal(size=(len(cancer.data), 50))

# In [7]: cancer.data.shape
# Out[7]: (569, 30)

# In [8]: noise.shape
# Out[8]: (569, 50)

# 向数据中添加噪声特征
# 前30个特征来自数据集,后50个是噪声
X_w_noise = np.hstack([cancer.data, noise])

# In [9]: X_w_noise.shape
# Out[9]: (569, 80)

# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    X_w_noise, cancer.target, random_state=0, test_size=.5)

########################
# 创建特征选择模型
########################
# SelectPercentile 参数说明:
# score_func(处理数据的函数)
#   f_classif(默认值): 用于处理分类问题的数据
#   f_regression(默认值): 用于处理回归问题的数据
#   其它的函数因为书中没介绍,暂时先放一放
# percentile(百分比阈值设定)
select = SelectPercentile(percentile=50)
select.fit(X_train, y_train)

# 对训练集进行变换
X_train_selected = select.transform(X_train)

print("X_train.shape: {}".format(X_train.shape))
print("X_train_selected.shape: {}".format(X_train_selected.shape))

# X_train.shape: (284, 80)
# X_train_selected.shape: (284, 40)

'''
但从shape来看,SelectPercentile好像不仅把噪点过滤掉了,还额外过滤了10个特征.
因为模型的百分比设置的是50, 因此会正好过滤一半的特征.
至于是不是把噪点全过滤了,需要验证一下.
'''

########################
# 查看别标记的特征
########################
mask = select.get_support()

# In [12]: mask
# Out[12]: 
# array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
#        False,  True, False,  True,  True,  True,  True,  True,  True,
#        False, False,  True,  True,  True,  True,  True,  True,  True,
#         True,  True,  True, False, False, False,  True, False,  True,
#        False, False,  True, False, False, False, False,  True, False,
#        False,  True, False, False,  True, False,  True, False, False,
#        False, False, False, False,  True, False,  True, False, False,
#        False, False,  True, False,  True, False, False, False, False,
#         True,  True, False,  True, False, False, False, False])


# 将遮罩可视化——黑色为True,白色为False
# matshow用于绘制矩阵
#   绘制的顺序是顺序读取数组,然后按照数组的形状绘制,
#   绘图的顺序是从左上至右下 可以用下面的例子试试效果理解一下
#   a = np.arange(1,16)
#   b = a.reshape(3,-1)
        # In[21]: b.shape
        # Out[21]: (3, 5)
#   plt.matshow(b,  cmap='gray_r')
#   plt.show()
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("Sample index")
# 想看看执行效果的话,把下面这句打开
# plt.show()

'''
mask中被标记成True的内容是保留项,False为过滤项.
从结果可以看出,噪点中(30~80)中有部分被保留了下来.
'''

########################
# 查看单变量统计在线性模型中的表现
########################
from sklearn.linear_model import LogisticRegression

lr = LogisticRegression()
lr.fit(X_train, y_train)
# 查看原始数据的模型得分
print("Score with all features: {:.3f}".format(lr.score(X_test, y_test)))
# Score with all features: 0.919

# 查看经过单变量统计模型处理后的模型得分
lr.fit(X_train_selected, y_train)
X_test_selected = select.transform(X_test)
print("Score with only selected features: {:.3f}".format(
    lr.score(X_test_selected, y_test)))
# Score with only selected features: 0.909

'''
可以明显看出单变量统计处理后的模型得分反而低了,
原因很好立即,结合上面的mask矩阵你会发现,一方面30~80之间的噪点有保留,
另一方面0~30的数据点也有屏蔽.说明这种处理方法对原始数据来说是有损失的.
'''

005 特征选择_基于模型的特征选择

# 特征选择_基于模型的特征选择

'''
概要:
    1. 创建数据
        1.1 创建原始数据
        1.2 在原始数据中增加噪点
    2. 创建基于模型的特征选择器
        2.1 选用随机森林作为标记特征重要的模型
        2.2 使用选择器提供的get_support方法查看筛选结果
    3. 创建线性模型
        3.1 使用原始数据训练模型&查看得分
        3.2 使用选择器标记处理后的数据训练线性模型&查看得分
'''

'''
结论:
    虽然基于模型的特征选择器与单变量统计对于相同数据有着相同的标记结果,
    但是基于模型的特征选择器由于不损失特征,而是通过标记结果排序处理重要特征的特点,
    在处理速度上虽然不及单变量统计,但是在学习能力上要高于它.

    模型存在的意义:
        我理解主要是弥补简单模型处理复杂数据上的能力不足,需要借助其它模型来共通处理,
        然后还想最大化的保留原汁原味数据的前提下产生的.

        它跟之前学习的数据预处理的区别在于:
            1. 因为直接使用成型模型,因此可以充分利用各模型的特点处理特征
            2. 之前为了让简单模型更好的理解数据,使用过交互项,多项式,缩放...数据处理手段
            而在有选择器的时候,只需选择更能理解数据的模型预先标记下特征就可以了
            3. 模型选择器不用变换数据,不损失数据特征,并能排序重要特征

    关于借助其它模型标记:
        1. 例子中使用随机森林标记, 函数帮助里使用的是线性模型标记,
        之前学过不少模型,不知道是不是所有模型均可以使用,这点以后遇到了在总结,
        现阶段只需知道有这么一种处理数据的方法就可以了.
'''

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 之前学过的随机森林模型
from sklearn.ensemble import RandomForestClassifier
# 线性模型
from sklearn.linear_model import LogisticRegression

# 基于模型的特征选择器
from sklearn.feature_selection import SelectFromModel


########################
# 创建数据并增加干扰用的噪点
########################
cancer = load_breast_cancer()
# 获得确定性的随机数
rng = np.random.RandomState(42)
noise = rng.normal(size=(len(cancer.data), 50))

# In [7]: cancer.data.shape
# Out[7]: (569, 30)

# In [8]: noise.shape
# Out[8]: (569, 50)

# 向数据中添加噪声特征
# 前30个特征来自数据集,后50个是噪声
X_w_noise = np.hstack([cancer.data, noise])

# In [9]: X_w_noise.shape
# Out[9]: (569, 80)

# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    X_w_noise, cancer.target, random_state=0, test_size=.5)


########################
# 创建基于模型的特征选择器
########################
'''
特征选择的原理是
  通过其它模型来标记数据的特征重要度,以便用这个度量对特征进行排序
所选用的模型的随机森林
  随机森林由100棵树组成
  添加了随机种子42
  threshold是第一次遇到,用于控制阈值,可以指定数字和预留的字符,
      这里使用median是预留字符中的一个,表示中位数(用它将结果对半分)
      设置中位数的原因是为了能与单变量统计选择器比较得分,因而保留了相同个数的数据特征
'''
select = SelectFromModel(
    RandomForestClassifier(n_estimators=100, random_state=42),
    threshold="median")

select.fit(X_train, y_train)
X_train_l1 = select.transform(X_train)
print("X_train.shape: {}".format(X_train.shape))
# X_train.shape: (284, 80)
print("X_train_l1.shape: {}".format(X_train_l1.shape))
# X_train_l1.shape: (284, 40)

# 因为使用了中位数,因此数据特征取半 80/2 = 40

mask = select.get_support()
# 将遮罩可视化——黑色为True,白色为False
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("Sample index")
# plt.show()

# In [45]: mask
# Out[45]: 
# array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
#        False,  True, False,  True,  True,  True,  True,  True,  True,
#        False, False,  True,  True,  True,  True,  True,  True,  True,
#         True,  True,  True, False, False, False,  True, False,  True,
#        False, False,  True, False, False, False, False,  True, False,
#        False,  True, False, False,  True, False,  True, False, False,
#        False, False, False, False,  True, False,  True, False, False,
#        False, False,  True, False,  True, False, False, False, False,
#         True,  True, False,  True, False, False, False, False])
# 我将单变量统计生成的mask与上面的比较了以下,结果时完全相同


########################
# 查看线性模型得分
########################

# 原始数据测试模型
score_0 = LogisticRegression().fit(X_train, y_train).score(X_test, y_test)
print("Test score: {:.3f}".format(score_0))
# Test score: 0.919

# 用随机森林标记后的数据来测试模型
X_test_l1 = select.transform(X_test)
score_1 = LogisticRegression().fit(X_train_l1, y_train).score(X_test_l1, y_test)
print("Test score: {:.3f}".format(score_1))
# Test score: 0.930

'''
模型得分不仅比原始数据的得分要高,
而且比单变量统计的的得分[Score with only selected features: 0.909]也要高.
'''

005 特征选择_迭代特征选择

# 特征选择_迭代特征选择

'''
概要:
    1. 创建数据
        1.1 创建原始数据
        1.2 在原始数据中增加噪点
    2. 创建迭代特征选择器
        2.1 选用随机森林作为标记特征重要的模型,并
        2.2 使用迭代选择器提供的get_support方法查看筛选结果
    3. 创建线性模型
        3.1 使用原始数据训练模型&查看得分
        3.2 使用选择器标记处理后的数据训练线性模型&查看得分
'''

'''
结论:
    按照执行效率又快到慢:
    单变量统计选择器 -> 基于模型的特征选择器 -> 迭代特征选择器

    从本次的例子来开,迭代特征选择器执行40次得效果与前一个基于模型得特征选择器的结果相同
    说明只要选对数据的识别模型,这两个选择器的效果是画等号的,那么可以选择执行效率更快的模型来
    开展分析工作.

    有了特征选择器,可以用其它模型的特长来弥补当前模型的短板,例子中就让线性模型表现出
    随机森林的优秀分析能力.

    模型存在的意义:
        - [sklearn.feature_selection]模块我认为它的利用重点是在数据分析阶段,
        有点像之前学过的术语:"预剪枝",把数据先做好分析,再有重点的分配给对应模型.
        它主要是应对更复杂更有深度的数据,例子中的数据还是太简单可能.
        - 还有一点实际工作中也可能会用到,在成型source上打patch,肯定不能大面积改变source结构,
        追加个patch在数据处理前先来个小整理应当可行.

'''

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
# 之前学过的随机森林模型
from sklearn.ensemble import RandomForestClassifier
# 线性模型
from sklearn.linear_model import LogisticRegression

# 迭代模型选择器 递减迭代
from sklearn.feature_selection import RFE


########################
# 创建数据并增加干扰用的噪点
########################
cancer = load_breast_cancer()
# 获得确定性的随机数
rng = np.random.RandomState(42)
noise = rng.normal(size=(len(cancer.data), 50))

# In [7]: cancer.data.shape
# Out[7]: (569, 30)

# In [8]: noise.shape
# Out[8]: (569, 50)

# 向数据中添加噪声特征
# 前30个特征来自数据集,后50个是噪声
X_w_noise = np.hstack([cancer.data, noise])

# In [9]: X_w_noise.shape
# Out[9]: (569, 80)

# 拆分数据
X_train, X_test, y_train, y_test = train_test_split(
    X_w_noise, cancer.target, random_state=0, test_size=.5)


########################
# 创建迭代特征选择器
########################
# 与[SelectFromModel]不同的是增加了n_features_to_select选项,用于控制保留的特征个数
# 为了便于选择器之间的横向比较,我们把特征数都控制成一样
select = RFE(RandomForestClassifier(n_estimators=100, random_state=42),
             n_features_to_select=40)

select.fit(X_train, y_train)
mask = select.get_support()

# 在默认参数下生成的mask与之前的结果有点不同,但是噪点以外的前30个点也有标记错的
# In [50]: mask
# Out[50]: 
# array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
#         True,  True,  True,  True,  True,  True,  True, False,  True,
#         True,  True,  True,  True,  True,  True,  True,  True,  True,
#         True,  True,  True, False,  True, False, False, False, False,
#        False, False, False, False, False, False, False, False, False,
#        False, False,  True,  True, False,  True, False, False,  True,
#         True, False, False, False,  True, False, False, False, False,
#        False, False,  True, False,  True, False, False, False, False,
#        False,  True, False, False, False,  True, False, False])

plt.matshow(mask.reshape(1, -1), cmap='gray_r')
plt.xlabel("Sample index")
# plt.show()

########################
# 查看线性模型的表现
########################

# 原始数据得分
score_0 = LogisticRegression().fit(X_train, y_train).score(X_test, y_test)
print("Test score_0: {:.3f}".format(score_0))
# Test score: 0.919

# 迭代特征选择器处理后的得分
X_train_rfe = select.transform(X_train)
X_test_rfe = select.transform(X_test)
score_1 = LogisticRegression().fit(X_train_rfe, y_train).score(X_test_rfe, y_test)
print("Test score_1: {:.3f}".format(score_1))
# Test score: 0.930

'''
得分与基于模型的特征选择器结果相同.
一方面说明随机森林模型很good,一次就可以将数据近乎完美的分析,
另一方面也可能是数据太简单,导致两次结果相同.
'''

占位

占位

;