R语言数据预处理——离散化(分箱)

R语言数据预处理——离散化(分箱)

一、项目环境

  • 开发工具:RStudio
  • R:3.5.2
  • 相关包:infotheo,discretization,smbinning,dplyr,sqldf

二、导入数据

# 这里我们使用的是鸢尾花数据集(iris)
data(iris)
head(iris)
Sepal.LengthSepal.WidthPetal.LengthPetal.WidthSpecies
15.13.51.40.2setosa
24.93.01.40.2setosa
34.73.21.30.2setosa
44.63.11.50.2setosa
55.03.61.40.2setosa
65.43.91.70.4setosa

相关数据解释:

  • Sepal.Length:萼片长度

  • Sepal.Width:萼片宽度

  • Petal.Length:花瓣长度

  • Petal.Width:花瓣宽度

  • Species:鸢尾花品种

三、 数据划分

library(dplyr)
library(sqldf)

# 为数据集增加序号列(id)
iris$id <- c(1:nrow(iris))

# 将鸢尾花数据集中70%的数据划分为训练集
iris_train <- sample_frac(iris, 0.7, replace = TRUE)

# 使用sql语句将剩下的30%花费为测试集
iris_test <- sqldf("
               select *
               from iris
               where id not in (
               select id
               from iris_train
               )
               ")

# 去除序号列(id)
iris_train <- iris_train[,-6]
iris_test <- iris_test[,-6]

【注】:这里使用到sqldf包的函数sqldf函数来时间在R语言中使用SQL语句

四、 无监督分箱

常见的几种无监督分箱方法

  • 等宽分箱法
  • 等频分箱法
  • kmeans分箱法

1、 分箱前准备法

# 导入无监督分箱包——infotheo
library(infotheo)

# 分成几个区域
nbins <- 3 

2、 等宽分箱法

### 等宽分箱的原理非常简单,就是按照相同的间距将数据分成相应的等分

# 将连续型数据分成三份,并以1、2、3赋值
equal_width <- discretize(iris_train$Sepal.Width,"equalwidth",nbins)

### 查看分箱情况

# 查看各分类数量
table(equal_width)

# 用颜色表明是等宽分箱
plot(iris_train$Sepal.Width, col = equal_width$X)


### 保存每个等分切割点的值(阙值)

# 计算各个分类相应的切割点
width <- (max(iris_train$Sepal.Width)-min(iris_train$Sepal.Width))/nbins

# 保存阙值
depreciation <- width * c(1:nbins) + min(iris_train$Sepal.Width)

> # 查看各分类数量
> table(equal_width)
equal_width
1 2 3
23 59 23
>
> # 用颜色表明是等宽分箱
> plot(iris_train S e p a l . W i d t h , c o l = e q u a l w i d t h Sepal.Width, col = equal_width Sepal.Width,col=equalwidthX)
>

在这里插入图片描述

3、 等频分箱

### 等频分箱是将数据均匀的分成相应的等分(数量不一定是完全相同的)

# 将连续型数据分成三份,并以1、2、3赋值
equal_freq <- discretize(iris_train$Sepal.Width,"equalfreq",nbins)

### 查看分箱情况

# 查看各分类数量
table(equal_width)

# 用颜色表明是等频分箱
plot(iris_train$Sepal.Width, col = equal_freq$X)


### 保存每个等分切割点的值(阙值)

data <- iris_train$Sepal.Width[order(iris_train$Sepal.Width)]

depreciation <- as.data.frame(table(equal_freq))$Freq

> # 查看各分类数量
> table(equal_freq)
equal_width
1 2 3
43 32 30
>
> # 用颜色表明是等频分箱
> plot(iris_train S e p a l . W i d t h , c o l = e q u a l f r e q Sepal.Width, col = equal_freq Sepal.Width,col=equalfreqX)
>

在这里插入图片描述

4、kmeans分箱法

# kmeans分箱法,先给定中心数,将观察点利用欧式距离计算与中心点的距离进行归类,再重新计算中心点,直到中心点# 不再发生变化,以归类的结果做为分箱的结果。


# 将连续型数据分成三份,并以1、2、3赋值
k_means <- kmeans(iris_train$Sepal.Width, nbins)

# 查看各分类数量
table(k_means$cluster)

# 查看实际分箱状况
k_means$cluster

# 保存阙值
# rev() 的作用是倒置数据框
# 统一从左往右,从大到小
depreciation <- rev(k_means$centers)

> # 查看各分类数量
> table(k_meansKaTeX parse error: Expected 'EOF', got '#' at position 36: … 26 36 43 \> #̲ 查看实际分箱状况 \> k_…cluster
[1] 3 2 2 2 3 1 3 3 2 2 3 3 3 2 3 1 1 1 3 1 3 3 1 2 2 1 2 3 3 3 1 1 1 1 3 3 1 2 2
[40] 3 3 3 3 3 2 2 2 3 3 2 3 2 2 1 2 1 2 1 1 2 3 3 3 2 3 2 3 1 3 3 1 1 1 2 3 3 2 3
[79] 3 2 2 2 2 1 3 2 2 3 2 3 2 2 1 3 3 3 3 2 1 1 1 1 2 3 2

五、 有监督分箱

discretization提供了几个主要的离散化的工具函数:

  • chiM,ChiM算法进行离散化

  • chi2, Chi2算法进行离散化

  • mdlp,最小描述长度原理(MDLP)进行离散化

  • modChi2,改进的Chi2方法离散数值属性

  • disc.Topdown,自上而下的离散化

  • extendChi2,扩展Chi2算法离散数值属性

smbinning提供的工具函数:

  • smbinning ,基于构造条件推断树ctree的监督式分箱

1、chiM算法进行离散化

### 有监督的数据离散化
library(discretization)# 有监督分箱

# 使用ChiMerge算法基于卡方检验进行自下而上的合并
chi1 <- chiM(iris_train, alpha = 0.05) # alpha 为显著性指标

apply(chi1$Disc.data,2,table)

# 保存阙值
depreciation <- chi1$cutp[[2]]


## 其他有监督分享算法
# chi2 <- chi2(iris,alp=0.5,del=0.05) # chi2()算法
# chi3 <- modChi2(iris,alp=0.5)       # modChi2()算法
# chi4 <- extendChi2(iris,alp = 0.5)  # extendChi2()算法
# m1 <- mdlp(iris)                    # 使用熵准则将最小描述长度作为停止规则来离散化
# d1 <- disc.Topdown(iris,method=1)   # 该功能实现了三种自上而下的离散化算法(CAIM,CACC,Ameva)

> apply(chi1$Disc.data,2,table)
> $Sepal.Length

1 2 3 4
28 14 54 9

$Sepal.Width

1 2 3
50 32 23

$Petal.Length

1 2 3 4
23 45 4 33

$Petal.Width

1 2 3
23 51 31

$Species

setosa versicolor virginica
23 47 35

2、基于构造条件推断树ctree的监督式分箱

# 分箱前数据准备
library(smbinning) 

# 查看测试用例
head(smbsimdf1)

fgoodcbs1cbs2cbinqcblinecbtermcblineutcbtobcbdpdcbnewpmttobdpddepdcodhomeincddonlinerndperiod
160.11NA0220047.513615NoNoM200No10481.402001NoW0600Yes0.466410292018-03-31
145.6266.720220252.362224NoNoA102Hi10182.431701NoW1000Yes0.919802862018-05-31
130.8666.940220035.896405NoYesM202Hi9645.372300NoW0500Yes0.338040092018-07-31
162.3849.120230141.935786NoNoP400No13702.763101No00Yes0.764756002017-12-31
154.3641.220010044.236625NoNoP400No18720.092602YesW0801Yes0.585637952018-02-28
168.7850.800000043.592487YesYesA401Lo10217.073100NoW0900Yes0.057563962018-03-31

【注】:这里之所以不适用鸢尾花数据集的原因在于,这个函数的使用条件较为苛刻。首先它不允许数据集的列名中含有 “.” ,比如 鸢尾花数据集中的“Sepal.Width”就不可以。其次它要求用于学习的列必须是二分类,且数据类型必须是numeric,二分类的值也必须是(0, 1) 。也是因为这些原因,为了方便在这里使用包中自带的数据集。


# 使用smbinning函数进行分箱,df 为原始数据,y表示目标标签,x表示需要分箱的标签
result <- smbinning(df = smbsimdf1,y = "fgood",x = "cbs1") 


# 查看分箱结果的分布情况,不良率和证据权重
par(mfrow=c(2,2))
boxplot(smbsimdf1$cbs1~smbsimdf1$fgood,horizontal=T, frame=F, col="lightgray",main="Distribution")
smbinning.plot(result,option="dist")
smbinning.plot(result,option="badrate")
smbinning.plot(result,option="WoE")

在这里插入图片描述

result$ivtable # 相关重要信息
result$ctree # 决策树
result$cuts # 阙值
smbinning.sql(result) # 输出相应的sql语句

> resultKaTeX parse error: Expected 'EOF', got '#' at position 9: ivtable #̲ 相关重要信息 …ctree # 决策树

Model formula:
fgood ~ cbs1

Fitted party:
[1] root
| [2] cbs1 <= 51.77
| | [3] cbs1 <= 36.44: 0.559 (n = 245, err = 60.4)
| | [4] cbs1 > 36.44: 0.741 (n = 829, err = 159.2)
| [5] cbs1 > 51.77
| | [6] cbs1 <= 59.5: 0.838 (n = 520, err = 70.4)
| | [7] cbs1 > 59.5: 0.935 (n = 650, err = 39.3)

Number of inner nodes: 3
Number of terminal nodes: 4
> result$cuts # 阙值
[1] 36.4400 51.7701 59.5000
> smbinning.sql(result) # 输出相应的sql语句
alter table ‘TableName’ add ‘NewCharName’
go
update ‘TableName’ set ‘NewCharName’
case
when cbs1 <= 36.44 then ‘01: cbs1 <= 36.44’
when cbs1 <= 51.7701 then ‘02: cbs1 <= 51.7701’
when cbs1 <= 59.5 then ‘03: cbs1 <= 59.5’
when cbs1 > 59.5 then ‘04: cbs1 > 59.5’
when cbs1 Is Null then ‘05: cbs1 Is Null’
else ‘99: Error’ end
>


# 使用训练好的函数对数据进行分箱(训练集和测试集都需要)
smbsimdf1 <- smbinning.gen(smbsimdf1, result, chrname = "gcbs1")

# 查看分箱情况
table(smbsimdf1$gcbs1)

> table(smbsimdf1$gcbs1)

00 Miss 01 <= 36.44 02 <= 51.7701 03 <= 59.5 04 > 59.5
256 245 829 520 650

【注】:除此之外也可以用smbinning.sql(result) 生成的sql语句,配合sqldf包进行数据分箱操作。

六、 使用阙值对测试集进行分箱操作

​ 上述方法中,除了最后一种方法,我们都没有将训练好的函数用于测试集。但是在实际的分析,我们让数据离散化最主要的目的更多的是为了降低机器学习的负担。因此我们除了需要对训练集进行分箱操作之外,将同样的分箱方法作用与测试集。那么下面我们就将使用之前得到的阙值,对测试集进行分享操作。

### 对测试集进行分箱操作


# 使用之前保存的阙值
# 这里之所以要前后加上Inf,是为了让它的范围能够向正负无穷延伸
# (-Inf, a],[b, Inf)
break1<-c(-Inf,depreciation,Inf)


labels = c("差", "中", "良", "优")

# 第一个值是数据
# 第一个值是分箱的区间
# 第三个值是替换成的数
# ordered_result表示被替换成的数是否有前后顺序
iris_test$Sepal.Width <- cut(iris_test$Sepal.Width,break1,labels,ordered_result = T)
iris_test$Sepal.Width

> iris_test$Sepal.Width
[1] 良 优 优 优 优 优 良 良 优 优 优 优 优 优 优 良 优 优 优 优 优 优 优 优 优 中
[27] 优 优 良 优 优 优 良 良 差 良 差 良 优 良 差 良 良 良 良 良 优 良 良 中 良 良
[53] 优 良 良 良 优 良 优 良 良 良 良 良 优 良 良 良 优 优 良 优 良 良
Levels: 差 < 中 < 良 < 优

七、 结语

​ 本文更多的是从实际操作的角度进行说明,之间涉及到的很多算法的原理没有进行过多的说明。有兴趣的可以查看本文主要参考的两篇文章,或者自行搜索学习。

​ 主要参考:https://blog.csdn.net/kMD8d5R/article/details/84351546,https://mp.weixin.qq.com/s/_E6QjFfJb8Dm5qI5lMNeaA


很长一段时间没有在csdn中写文章了,事实上后面自己学习过程中的大部分文档都是在语雀中完成的,基本都是自己写自己看。后续文章都会分享到自己的个人博客上,感兴趣的话可以关注一下。个人博客地址