在一些算法中,需要将具有连续属性的特征转换成离散属性的特征。离散化后的特征对于异常数据会有更强的鲁棒性,模型会更加的稳定。在建立分类模型时,例如:逻辑回归的算法,对数据进行预先的离散化,可以十分有效地提高模型的结果。
在这里我主要记录并介绍我最近自己使用的等频分箱的代码。
等频离散化顾名思义,使划分的区间中,样本数量尽量保持一致。
例如对数据【2,2,3,4,8,10,12,16,17】。我们使用等频分箱,设置分箱数为3,一共分成三个区间。那么等频分箱的结果将会是:
【2,2,3】
【4,8,10】
【12,16,17】
每个样本中都含有3个样本
第一种实现方法是使用sklearn.preprocessing中的KbinsDiscretizer函数。
这是sklearn中的函数使用说明:KBinsDiscretizer;具体函数可以参考这个链接,这个函数也可以实现等宽分箱、聚类分箱以及其他的一些分箱方法。
这里放一个中文的简单说明:
而对于等频分箱的函数设置如下
est=KBinsDiscretizer(n_bins=5, encode=’ordinal’, strategy=’quantile’)
完整的实现代码如下
import pandas as pd
import osfrom sklearn.preprocessing import KBinsDiscretizer
#读取数据
datapath="你的文件路径"
data=pd.read_csv(datapath)
column_headers =list(data.columns.values)
unselected_features=['feature0','feature1'] #将数据里的离散数据先去除掉,这里需要手工选择。也可以不选择这一步,不过会将本来就离散化的数据进行离散化,会导致最后的数据不正确
selected_features=[item for item in column_headers if item not in set(unselected_features)]
#分箱代码
k=5 #设置分箱数
est=KBinsDiscretizer(n_bins=k,encode='ordinal',strategy='quantile')
est.fit(data[selected_features])
Xt=est.transform(data[selected_features])
#将离散化后的数据与之前的离散数据拼起来
dd=pd.DataFrame(Xt,columns=selected_features)
# print(df)
du=data[unselected_features]
df=pd.concat([du,dd],axis=1)
#print(df)
这种方法较为简单方便。不过由于函数已经封装好了,所有数据都会按照相同的分箱数进行分箱。对于稀疏数据集,特征中含有大量的0,会导致离散化效果不好。
因此针对稀疏数据集,我自己写了两个方法来解决数据中大量0的问题。
方法二和方法三主要将数据中为0的数据与非0数据进行分开处理。并没有使用KBinsDiscretizer函数,如果存在逻辑上的错误,请指出,欢迎交流。
方法二主要通过设置分箱数来进行离散化。
方法三则通过设置每个区间中的最小样本数来进行离散化。
方法二和方法三主要使用的是pandas中的pd.qcut()函数。
qcut()函数本身的作用就是对数据进行等频率的划分。
pd.cut(x, q, labels=None, retbins=False, precison=3, duplicates=‘raise’)。如果数据中某一个值的数据特别多,导致划分使有多个区间是一样的,则需要将duplicates设置为‘drop’
import pandas as pd
datapath="你的文件路径"
data=pd.read_csv(datapath)
column_headers =list(data.columns.values)
unselected_features=['feature0','feature1'] #将数据里的离散数据先去除掉,这里需要手工选择。也可以不选择这一步,不过会将本来就离散化的数据进行离散化,会导致最后的数据不正确
selected_features=[item for item in column_headers if item not in set(unselected_features)]
#分箱代码
k=5 #设置分箱数
for feature in selected_features:
feature_series=data[feature]
data1=feature_series[feature_series!=0]
data0=feature_series[feature_series==0]
bindata=pd.qcut(data1,k,labels= False,duplicates='drop') #核心语句
bindata+=1 #+1与0区别开来
dlast=pd.concat([data0,bindata])
dlast=dlast.sort_index()
df=pd.concat([Bindata,dlast],axis=1)
方法三与方法二基本思路是一样的。只不过在设置分箱数k时,不是所有特征设置相同的值,而是通过设置每个区间中的最小样本数,分箱数k=非0样本总数/最小样本数,这样会使每个特征列中的分箱数不一样,使得离散化更加合理。
代码与方法二相比仅修改了一小部分
#分箱代码
num=100 #设置最小样本数
for feature in selected_features:
feature_series=data[feature]
data1=feature_series[feature_series!=0]
data0=feature_series[feature_series==0]
k=math.ceil(len(data1)/num) #向上取整
bindata=pd.qcut(data1,k,labels= False,duplicates='drop')
bindata+=1
dlast=pd.concat([data0,bindata])
dlast=dlast.sort_index()
df=pd.concat([Bindata,dlast],axis=1)