[机器学习与scikit-learn-32]:算法-回归-普通线性模型拟合非线性分布数据-分箱

2023-11-11

作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:https://blog.csdn.net/HiWangWenBing/article/details/123562666


目录

前言:

第1章 分箱机制

分箱的本质: 用多个线段替代一个直线对目标样本数据进行拟合

第2章 代码实现 

2.1 导入库

2.2 创建非线性分布的数据集

2.3 用原始数据集进行模型训练

2.4 分箱的基本原理

2.5 分箱数据预处理建模

2.6 用分箱后的数据进行线性模型拟合(10个箱子)

2.7 不同箱子对线性模型拟合效果的影响(5个箱子做比较)

2.8 如何动态评估不同箱子效果


前言:

普通线性模型拟合非线性分布数据的问题是:线性拟合只有一根直线,导致拟合非线性数据是出现误差较大的情况,如下图所示:

第1章 分箱机制

为了解决线性函数在整个样本空间沿着线性曲线单调增加或单调较小的缺陷,我们使用N个线段替代单一直线进行线性拟合,得到如下的右图示意图。

(1)用20根线段替代单一直线

 (2)用5根线段替代单一直线

很显然,替代后,线性拟合出来的不在是单一直线,而是一个个阶梯型线段,这个线段比单一的直线,更加解决非线性数据分布。上述的思想就是分箱的思想。

分箱的本质是:

(1)属于数据预处理

(2)它对样本数据进行预处理,把连续的样本数据分成N个离散的等级(分段),因变量不在连续。

(3)由于是线性拟合,因此预测结果也不在连续,而是M个离散值,M不一定等于N, 就如上图中的梯形,不是单调增,也不是单调减。

分箱的本质: 用多个线段替代一个直线对目标样本数据进行拟合

第2章 代码实现 

2.1 导入库

# 1. 导入所需要的库
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor

2.2 创建非线性分布的数据集

# 2. 创建需要拟合的数据集
rnd = np.random.RandomState(42) #设置随机数种子
X = rnd.uniform(-3, 3, size=100) #random.uniform,从输入的任意两个整数中取出size个随机数
#生成y的思路:先使用NumPy中的函数生成一个sin函数图像,然后再人为添加噪音
y = np.sin(X) + rnd.normal(size=len(X)) / 3 #random.normal,生成size个服从正态分布的随机数

#使用散点图观察建立的数据集是什么样子
plt.scatter(X, y,marker='o',c='k',s=20)
plt.show()

#为后续建模做准备:sklearn只接受二维以上数组作为特征矩阵的输入
print(X.shape)
X = X.reshape(-1, 1)
print(X.shape)

2.3 用原始数据集进行模型训练

(1)训练模型

# 3.1 使用原始数据训练模型
LinearR = LinearRegression()
LinearR = LinearR.fit(X, y)

TreeR = DecisionTreeRegressor(random_state=0)
TreeR = TreeR.fit(X, y)

(2)展示结果

#3.2 显示原始训练模型效果
# 放置画布
fig, ax1 = plt.subplots(1)
#创建测试数据:一系列分布在横坐标上的点
line_X = np.linspace(-3, 3, 1000, endpoint=False).reshape(-1, 1)

#将测试数据带入predict接口,获得模型的拟合效果并进行绘制
ax1.plot(line_X, LinearR.predict(line_X), linewidth=2, color='green', label="linear regression")
ax1.plot(line_X, TreeR.predict(line_X), linewidth=2, color='red', label="decision tree")
#将原数据上的拟合绘制在图像上
ax1.plot(X[:, 0], y, 'o', c='k')
#其他图形选项
ax1.legend(loc="best")
ax1.set_ylabel("Regression output")
ax1.set_xlabel("Input feature")
ax1.set_title("Result before discretization")
plt.tight_layout()
plt.show()

2.4 分箱的基本原理

(1)分箱

# 4 分箱的基本原理
from sklearn.preprocessing import KBinsDiscretizer

#将数据分箱
#encode模式"onehot":使用做哑变量方式做离散化
#之后返回一个稀疏矩阵(m,n_bins),每一列是一个分好的类别
#对每一个样本而言,它包含的分类(箱子)中它表示为1,其余分类中它表示为0
enc = KBinsDiscretizer(n_bins=10          # 分10个箱子
                       ,encode="ordinal")  # 
X_binned = enc.fit_transform(X)
print("X.min=", X.min())
print("X.max=", X.max())
print("X.shape=", X.shape)
print("X.X_binned=", X_binned.shape)
print("X的前5个数据:\n", X[0:5])
print("X_binned的前5个数据:\n",X_binned[0:5])
X.min= -2.9668672972583856
X.max= 2.9213216196031038
X.shape= (100, 1)
X.X_binned= (100, 1)
X的前5个数据:
 [[-0.75275929]
 [ 2.70428584]
 [ 1.39196365]
 [ 0.59195091]
 [-2.06388816]]
X_binned的前5个数据:
 [[4.]
 [9.]
 [7.]
 [6.]
 [2.]]

(2)分箱的基本工作方式

# 4 分箱的基本工作方式
from sklearn.preprocessing import KBinsDiscretizer

#将数据分箱
#encode模式"onehot":使用做哑变量方式做离散化
#之后返回一个稀疏矩阵(m,n_bins),每一列是一个分好的类别
#对每一个样本而言,它包含的分类(箱子)中它表示为1,其余分类中它表示为0
enc = KBinsDiscretizer(n_bins=10          # 分10个箱子
                       ,encode="onehot")  #ordinal
X_binned = enc.fit_transform(X)
print("X.min=", X.min())
print("X.max=", X.max())
print("X.shape=", X.shape)
print("X.X_binned=", X_binned.shape)
print("X的前5个数据:\n", X[0:5])
print("X_binned的前5个数据:\n",X_binned[0:5])
# X_binned数据格式:
# 第一列:序号
# 第二列:onehot的编码位置
# 第三列:onehot的编码值

#使用pandas打开稀疏矩阵
import pandas as pd
pd.DataFrame(X_binned.toarray()).head()
X.min= -2.9668672972583856
X.max= 2.9213216196031038
X.shape= (100, 1)
X.X_binned= (100, 10)
X的前5个数据:
 [[-0.75275929]
 [ 2.70428584]
 [ 1.39196365]
 [ 0.59195091]
 [-2.06388816]]
X_binned的前5个数据:
   (0, 4)	1.0
  (1, 9)	1.0
  (2, 7)	1.0
  (3, 6)	1.0
  (4, 2)	1.0

Out[23]:

0 1 2 3 4 5 6 7 8 9
0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0
1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0
2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0
3 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0
4 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0

2.5 分箱数据预处理建模

分箱不属于拟合,而是数据预处理!!

# 5. 使用分箱数据进行建模和绘图

# 定义分箱对象
enc = KBinsDiscretizer(n_bins=10,encode="onehot")
#enc = KBinsDiscretizer(n_bins=10,encode="ordinal")

# 对输入样本进行等级分箱,分箱后,输入样本不在是连续量,而是离散量。
X_binned = enc.fit_transform(X)

line_binned = enc.transform(line_X)  # 分箱后,X轴数据被分割成10个段,即10个箱子。
print(line_X.shape)
print(line_binned.shape)
print("line_X.min=", line_X.min())
print("line_X.max=", line_X.max())
print("line_X.shape=", line_X.shape)
print("line_binned=", X_binned.shape)
print("line_X的前5个数据:\n", line_X[0:5])
print("line_binned的前5个数据:\n",line_binned[0:5])  # 分箱后的X不再连续
(1000, 1)
(1000, 10)
line_X.min= -3.0
line_X.max= 2.9939999999999998
line_X.shape= (1000, 1)
line_binned= (100, 10)
line_X的前5个数据:
 [[-3.   ]
 [-2.994]
 [-2.988]
 [-2.982]
 [-2.976]]
line_binned的前5个数据:
   (0, 0)	1.0
  (1, 0)	1.0
  (2, 0)	1.0
  (3, 0)	1.0
  (4, 0)	1.0

2.6 用分箱后的数据进行线性模型拟合(10个箱子)

#将两张图像绘制在一起,布置画布
fig, (ax1, ax2) = plt.subplots(ncols=2
                               , sharey=True #让两张图共享y轴上的刻度
                               , figsize=(10, 4))

#在图1中布置在原始数据上建模的结果
ax1.plot(line_X, LinearR.predict(line_X), linewidth=2, color='green',label="linear regression")
ax1.plot(line_X, TreeR.predict(line_X), linewidth=2, color='red', label="decision tree")
ax1.plot(X[:, 0], y, 'o', c='k')
ax1.legend(loc="best")
ax1.set_ylabel("Regression output")
ax1.set_xlabel("Input feature")
ax1.set_title("Result before discretization")

#使用分箱数据进行建模
LinearR_ = LinearRegression().fit(X_binned, y)
TreeR_ = DecisionTreeRegressor(random_state=0).fit(X_binned, y)

#进行预测,在图2中布置在分箱数据上进行预测的结果
ax2.plot(line_X #横坐标
         , LinearR_.predict(line_binned) #分箱后的特征矩阵的结果Y, 也不再是连续量,而是离散量。
         , linewidth=2
         , color='green'
         , linestyle='-'
         , label='linear regression')

ax2.plot(line_X, TreeR_.predict(line_binned), linewidth=2, color='red',linestyle=':', label='decision tree')
#绘制和箱宽一致的竖线
ax2.vlines(enc.bin_edges_[0] #x轴
           , *plt.gca().get_ylim() #y轴的上限和下限
           , linewidth=1
           , alpha=.2)

#将原始数据分布放置在图像上
ax2.plot(X[:, 0], y, 'o', c='k')
#其他绘图设定
ax2.legend(loc="best")
ax2.set_xlabel("Input feature")
ax2.set_title("Result after discretization")
plt.tight_layout()
plt.show()

#备注:
# 分箱后,线性模型可以拟合非线性数据
# 分箱后,可以克服决策的过拟合

 2.7 不同箱子对线性模型拟合效果的影响(5个箱子做比较)

enc = KBinsDiscretizer(n_bins=5,encode="onehot")

2.8 如何动态评估不同箱子效果

# 7. 如何选取最优的箱数
from sklearn.model_selection import cross_val_score as CVS
import numpy as np
pred,score,var = [], [], []
#binsrange = [2,5,10,15,20,30]
binsrange = list(range(2,30))

for i in binsrange:
    #实例化分箱类
    enc = KBinsDiscretizer(n_bins=i,encode="onehot")
    #转换数据
    X_binned = enc.fit_transform(X)
    line_binned = enc.transform(line_X)
    
    #建立线性模型
    LinearR_ = LinearRegression()
    
    #全数据集上的交叉验证
    cvresult = CVS(LinearR_, X_binned,y,cv=5)
    
    # 记录分数
    score.append(cvresult.mean())
    var.append(cvresult.var())
    #测试数据集上的打分结果
    pred.append(LinearR_.fit(X_binned,y).score(line_binned,np.sin(line_X)))

#绘制随着箱子数目的变化,交叉验证的分数
plt.figure(figsize=(6,5))
plt.plot(binsrange,pred,c="orange",label="test")
plt.plot(binsrange,score,c="k",label="full data")
plt.plot(binsrange,score+np.array(var)*0.5,c="red",linestyle="--",label = "var")
plt.plot(binsrange,score-np.array(var)*0.5,c="red",linestyle="--")
plt.legend()
plt.show()

#备注:
# 1. 从图形可以看出,随着箱子数目的增加,交叉验证的分数在数据上会逐渐增加。
# 2. 但当箱子的数目超过20之后,交叉验证的分数反而是下降的
# 3. 测试集的分数要高于全数据集

mapping = [*zip(pred, binsrange)]
mapping = sorted(mapping,reverse = True)
mapping

[(0.9660434948725315, 19),
 (0.9652516448868957, 21),
 (0.9639830451101487, 24),
 (0.9622614514281138, 12),
 (0.9620400358666081, 14),
 (0.9619989011062795, 23),
 (0.9590978882491229, 15),
 (0.9585720815564842, 18),
 (0.9584114197704767, 26),
 (0.957237960534212, 16),
 (0.9566086751329097, 20),
 (0.9541740734521215, 17),
 (0.9524184920312658, 22),
 (0.9513138137979673, 11),
 (0.9505447185393926, 28),
 (0.950463224130405, 29),
 (0.9486284965918873, 9),
 (0.946414455583129, 13),
 (0.9452817343808607, 25),
 (0.9449968422975642, 27),
 (0.9441330750510549, 10),
 (0.938682004554821, 8),
 (0.9209034773625397, 7),
 (0.9171762233471714, 6),
 (0.8649069759304867, 5),
 (0.8344479728983831, 4),
 (0.8239109278531977, 2),
 (0.7478242248690032, 3)]

备注:

分数排名前三的箱子数目为:19,21,24。


作者主页(文火冰糖的硅基工坊):文火冰糖(王文兵)的博客_文火冰糖的硅基工坊_CSDN博客

本文网址:https://blog.csdn.net/HiWangWenBing/article/details/123562666

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[机器学习与scikit-learn-32]:算法-回归-普通线性模型拟合非线性分布数据-分箱 的相关文章

随机推荐

  • 01--背包问题以及构造最优解

    目录 1 01 背包问题 2 构造最优解 3 动态规划法求解01 背包问题的局限性 1 01 背包问题 01 背包问题 就是有n个物品 它们有各自的体积和价值 现有给定容量的背包 如何让背包里装入的物品具有最大的价值总和 一个物品只有装与不
  • 调试flex程序

    如果程序编译时报错 需要修改程序 有时还要进行调试 在Flash中 最常用的调试方式是使用trace函数 将想要检测的对象或函数运行结果在输出面板中打印出来 在Flex中 同样可以使用trace函数来进行调试 修改上面的代码
  • 自动代码生成 - 使用Seq2Seq模型在代码生成数据集上进行自动代码生成任务。

    1 引言 自动代码生成是一个具有挑战性和实用性的任务 它可以帮助开发人员自动化生成代码 提高开发效率 在这篇博客中 我们将介绍如何使用Seq2Seq模型进行自动代码生成任务 并在代码生成数据集上进行实验 我们将使用Python作为代码生成语
  • SeleniumLibrary4.5.0 关键字详解(一)

    SeleniumLibrary4 5 0 关键字详解 一 库版本 4 5 0 库范围 全局 命名参数 受支持 简介 SeleniumLibrary是Robot Framework的Web测试库 本文档说明了如何使用SeleniumLibra
  • leetcode 编写一个函数来查找字符串数组中的最长公共前缀。

    编写一个函数来查找字符串数组中的最长公共前缀 如果不存在公共前缀 返回空字符串 string longestCommonPrefix vector
  • Flutter控件之CircularProgressIndicator

    CircularProgressIndicator的作用 Flutter中的CircularProgressIndicator是一个圆形进度指示器 用于表示正在进行的任务的进度 它通常用于长时间运行的任务 例如文件下载 网络请求等 Circ
  • vue-amap生成地图遮罩层、点标记和弹窗

  • vmware 安装window server 2012 完只有命令窗口

    原因是 解决办法 输入 Dism online enable feature all featurename Server Gui Mgmt featurename Server Gui Shell featurename ServerCo
  • signature=27dcc93dc9d59db77c2d43c8888c8f5d,ftv-20201027

    0001659166 20 000184 txt 20201027 0001659166 20 000184 hdr sgml 20201027 20201027162536 ACCESSION NUMBER 0001659166 20 0
  • TypeError: Argument ‘bb’ has incorrect type (expected numpy.ndarray, got list)

    问题说明 这个问题是在mmdetect中使用使用自己做的coco数据集用maskrcnn做目标检测时遇到的 主要原因是你的json文件里面的segmentation中的数据不符合要求 正常来说这里面是类似于 x y x y x y x y
  • C++中placement new操作符(经典)

    C 中placement new操作符 经典 placement new是重载operator new的一个标准 全局的版本 它不能被自定义的版本代替 不像普通的operator new和operator delete能够被替换成用户自定义
  • static和final、抽象类使用详解

    这里写目录标题 static 静态导入包 final 抽象类 abstract 接口interface 这里是引用 static 放在方法上就是静态方法 放在属性上的就是静态属性 可以通过类名直接调用属性 用在方法上 同理 另外我们可以在非
  • 使用jQuery实现返回顶部功能

  • JS 树(数组存储)进行递归遍历获取路径

    JS 树 数组存储 进行递归遍历获取路径 实现功能 通过叶子节点 id 寻找包含该叶子节点的整条路径 树的数据以数组形式保存 直接上代码 const getPathByKey curKey data gt let result 记录路径结果
  • python 使用sphinx 快速生成说明文档

    目录 python 使用sphinx 快速生成说明文档 1 安装sphinx 2 文件结构 3 修改配置文件 4 生成html文档 生成markdown文档 1 安装依赖 2 修改配置文件 3 生成markdown文档 python 使用s
  • 矩阵论—凯莱-哈密顿定理

    凯莱 哈密顿定理内容 凯莱 哈密顿定理典型例题 典型例题 我们先来观察这个题目 题目要求 若直接将矩阵A 代入计算 则会非常复杂 因此 这条路是走不通的 我们试着引入我们今天介绍的凯莱 哈密顿定理来解这个题目 令 我们要求 即求即可 接下来
  • C++ 友元

    友元一般存在于不同类之间 在一个类中 可以用全局函数作友元函数 而在不同类中 类成员函数作友元函数 友元可以是一个函数 该函数被称为友元函数 函数既可以是全局也可以是类的成员 友元也可以是一个类 该类被称为友元类 同类对象间无私处 异类对象
  • C语言实现惯导系统的间接粗对准

    C语言实现惯导系统的间接粗对准 惯导系统是一种常见的导航系统 用于测量和跟踪飞行器的位置 速度和方向 其中的粗对准是指通过传感器测量的数据进行校准 以提高系统的准确性和稳定性 本文将介绍如何使用C语言实现惯导系统的间接粗对准算法 并提供相应
  • json文件解析出现异常

    今天在尝试用自带的NSJSONSerialization方法来解析本地json文件的时候碰到了系统异常 app自动终止 问题如下 代码
  • [机器学习与scikit-learn-32]:算法-回归-普通线性模型拟合非线性分布数据-分箱

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 123562666 目录 前言 第1章