赞
踩
离散化:就是把无限空间中有限的个体映射到有限的空间中。数据离散化操作大多是针对连续数据进行的,处理之后的数据值域分布将从连续属性变为离散属性。
为什么离散化:模型中,同一线性系数应该对所有可能的计数值起作用。过大的计数值对无监督学习方法也会造成破坏,比如k-均值聚类,它使用欧氏距离作为相似度函数来测量数据点之间的相似度。数据向量某个元素中过大的计数值对相似度的影响会远超其他元素,从而破坏整体的相似度测量。
离散化方式会影响后续数据建模和应用效果:
使用决策树往往倾向于少量的离散化区间,过多的离散化将使得规则过多受到碎片区间的影响。
关联规则需要对所有特征一起离散化,关联规则关注的是所有特征的关联关系,如果对每个列单独离散化将失去整体规则性。
离散化处理后将分散的时间特征转换为更高层次的时间特征。有以下数据,数据集地址:案例数据集《机器学习-特征工程-数据离散化-会员数据》,如下:
- import pandas as pd
- from sklearn.cluster import KMeans #k-均值分类器
- from sklearn import preprocessing
-
- df=pd.read_table('data7.txt',names=['id','amount','income','datetime','age'])
- df.info()
- df.head()
输出:
将datetime字段转换为周数。
- #针对时间数据的离散化
- df['datetime1']=pd.to_datetime(df['datetime']).apply(lambda x:x.weekday())
- df.head()
输出:
多值离散数据要进行离散化可能是划分的逻辑有问题,需要重新划分,这个问题通常是由于业务逻辑的变更。
针对多值离散数据age字段的离散化
- #查看age的值域
- df.groupby('age')['age'].unique()
输出:
- age
- 0-10 [0-10]
- 10-20 [10-20]
- 20-30 [20-30]
- 30-40 [30-40]
- 40-50 [40-50]
- 50-60 [50-60]
- 60-70 [60-70]
- 70-80 [70-80]
- 80-90 [80-90]
- >90 [>90]
- Name: age, dtype: object
- #对age的值域进行重构
- map_df=pd.DataFrame([['0-10','0-40'],['10-20','0-40'],['20-30','0-40'],['30-40','0-40'],
- ['40-50','40-80'],['50-60','40-80'],['60-70','40-80'],['70-80','40-80'],
- ['80-90','>80'],['>90','>80']],columns=['age','age2'])
- map_df
-
- #离散化
- df=pd.merge(df,map_df,how='inner',on=['age'])
- df
输出:
连续数据的离散化结果可以分为两类:一类是将连续数据划分为特定区间的集合,例如{(0,10],(10,20],(20,50],(50,100]};一类是将连续数据划分为特定类,例如类1、类2、类3。
可使用等距区间或自定义区间的方式进行离散化,分段可以是线性的,也可以是指数性的。
该方法(尤其是等距区间)可以较好地保持数据原有的分布。
1.1、等距区间(线性)
等距区间和自定义区间都可以利用cut方法和digtize方法来实现,等距区间只要将bins序列定义成等距序列均可,等距区间可以理解为特殊的自定义区间。
下面我们介绍一种专用于等距区间划分的方法。
- import numpy as np
- small_counts = np.random.randint(0, 100, 20)
- small_counts
-
- bins=np.floor_divide(small_counts, 10) #除法,结果向下取整
-
- ['['+str(i*10)+','+str((i+1)*10)+')' for i in bins] #向下开区间
输出:
1.2、自定义区间
- #1、自定义分箱区间
- #1)cut方法
- bins=[0,200,1000,5000,10000]
- df['amount1']=pd.cut(df['amount'],bins)
- df
- #2)digitize方法
- bins=bins=[0,200,1000,5000,10000] #注意:bins数据是有要求的,bins内的数据一定要是降序或者升序的数据,不能是一堆无序数据。
- indices=np.digitize(df['amount'],bins) #返回值为每个值所属区间的索引。
- indices
- df['amount2']=[str(bins[i-1])+"~"+str(bins[i]) for i in indices]
- df
输出:
digitize方法的返回值为区间上限所在bins中的索引。
1.3、指数性区间
当数值横跨多个数量级时,最好按照10的幂(或任何常数的幂)来进行分组:0~9、10~99、100~999、1000~9999。
- large_counts = np.array([296, 8286, 64011, 80, 3, 725, 867, 2215, 7689, 11495, 91897, 44, 28, 7971,926, 122, 22222])
- large_counts
-
- bins=np.floor(np.log10(large_counts)) #取对数之后再向下取整
-
- ['['+str(10**i)+','+str(10**(i+1))+')' for i in bins]
输出:
该思想也可以拓展到数据的可视化上,如我们有以下数据,数据集地址:案例数据集《机器学习-特征工程-数据离散化-会员数据2》。如下:
- biz_file = open('精通特征工程/精通特征工程/data/yelp_academic_dataset_business.json')
- biz_df = pd.DataFrame([json.loads(x) for x in biz_file.readlines()])
- biz_file.close()
-
- biz_df.info()
输出:
我们对其中的变量review_count进行分布展示,正常的分布图如下:
- sns.set_style('whitegrid')
- fig, ax = plt.subplots()
- biz_df['review_count'].hist(ax=ax, bins=100) #review_count表示点评数量
- ax.tick_params(labelsize=14) #设置刻度线标签字体大小
- ax.set_xlabel('Review Count', fontsize=14) #横坐标
- ax.set_ylabel('Occurrence', fontsize=14) #纵坐标
输出:
我们可以看到初始的review_count数据严重集中在低计数值区域,呈现一个典型的重尾分布,对于这种情况,我们可以将坐标轴修改成按照对数分布分割的刻度,如下:
- sns.set_style('whitegrid')
- fig, ax = plt.subplots()
- biz_df['review_count'].hist(ax=ax, bins=100) #review_count表示点评数量
- ax.set_yscale('log') #将轴的刻度设置成对数刻度
- ax.set_xscale('log') #将轴的刻度设置成对数刻度
- ax.tick_params(labelsize=14) #设置刻度线标签字体大小
- ax.set_xlabel('Review Count', fontsize=14) #横坐标
- ax.set_ylabel('Occurrence', fontsize=14) #纵坐标
输出:
注意:此图一般很少用,因为第一直观的视觉上容易让我们错误理解数据的原始分布。
- #2、聚类算法
- data=df['amount']
- data_reshape=data.values.reshape((data.shape[0],1))
- model_kmeans=KMeans(n_clusters=4,random_state=0)
- kmeans_result=model_kmeans.fit_predict(data_reshape)
- df['amount3']=kmeans_result
- df
输出:
固定宽度分箱非常容易计算,但如果计数值中有比较大的缺口,就会产生很多没有任何数据的空箱子。根据数据的分布特点,进行自适应的箱体定位,就可以解决这个问题。这种方法可以使用数据分布的分位数来实现。
- #3、四分位数
- df['amount4']=pd.qcut(df['amount'],4
- # ,labels=['bad','medium','good','awesome']
- ) #分位数。 10用于十分位,四用于四分位,例如 [0,.25,.5,.75,1.]用于四分位数
- df
输出:
pandas.DataFrame.qcut函数返回的是分位数区间,而pandas.DataFrame.quantile,可以返回具体的分位数的值。详见《python——numpy——数据分区(digitize,cut,qcut,quantile函数)》
将数据按照不同数据的频率分布进行排序,然后按照等频率或指定频率离散化,这种方法会将数据变换成均匀分布,好处是各区间的观测值是相同的,不足是已经改变了原有数据的分布状态。
通过使用基于卡方的离散化方法,找出数据的最佳临近区间并合并,形成较大的区间。
每个数据点和阈值进行比较,大于阈值设置为某一固定值(例如1),小于阈值设置为某一固定值(例如0),然后得到一个只拥有两个值域的二值化数据集。
- #针对连续变量的二值化
- binarizer_scaler=preprocessing.Binarizer(threshold=df['income'].mean()) #建立对象,threshold用于设定阈值
- income_tmp=binarizer_scaler.fit_transform(df['income'].values.reshape((df['income'].shape[0],1))) #二值化转换,需要将一维数据转换为二维数据
- df['income1']=income_tmp
- df
输出:
income二值化的阈值设置为income的平均值。
参考:
《python数据分析和数据化运营》
《精通特征工程》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。