出品 | CDA数据分析研究院,转载需受权
顾名思义,分类变量用于表示类别或标签。例如,一个分类变量能够表明世界上的主要城市,一年的四季,或者一个公司的所在产业(如石油,旅游,科技)。html
在现实世界中,分类变量的值是无穷多的。这些值能够用数字表示。然而,与数值变量不一样,分类变量的值不能相互排序。python
例如石油做为一种行业类型,既不高于也不低于旅游业,这被称为非序数。git
那么怎么判断一个变量是不是分类变量呢?能够经过一个简单的问题来检验"值的大小重不重要,是否具备计算关系?"若是值的大小重要而且具备计算关系,那么就是数值型变量。例如,500 美圆的股价是 100 美圆的 5 倍,因此股票价格应该用一个连续的数字变量来表示。另外一方面,值的大小不重要且不能够计算,那么就是分类变量,例如公司的行业(石油、旅游、技术等)是分类变量。github
少类别分类变量:类别数量的数量级小web
像是性别、颜色、产业等分类变量的类别数量级小,那么就属于少类别分类变量数据库
多类别分类变量:类别数量的数量级大json
多类别分类变量在事务性记录中特别常见。例如,许多 web 服务使用 id 跟踪用户,这是一个包含数亿到数亿个值的分类变量,具体取决于服务的惟一用户数量。互联网交易的 IP 地址是另外一个多类别分类变量的例子。它们是分类变量,由于即便用户 id 和 IP 地址是数字,它们的大小一般与手头的任务无关。例如,当对单个交易进行欺诈检测时,IP 地址多是相关的。某些 IP 地址或子网可能比其余 IP 地址或子网产生更多的欺诈交易。可是 164.203.x 的子网本质上并不比 164.202.x 更具欺诈性,子网的数值并不重要。api
文档语料库的词汇能够解释为一个多类别分类变量。若是一个类(例如 apple这个单词)在一个数据点(文档)中出现屡次,那么咱们能够将其表示为一个计数,并经过它们的计数统计数据表示全部的类别。这就是所谓的bin-counting
。app
咱们从少类别分类变量的处理开始讨论,最后进入到多类别分类变量的处理的讨论。dom
范畴变量的类别一般不是数字。例如,眼睛的颜色能够是"黑色""蓝色""棕色"等等。所以,须要一种编码方法来将这些非数字类别转换为数字。简单地给每一个 k 可能的类别分配一个整数,好比 1 到 k,是很诱人的。可是获得的值能够相互排序,这对于分类变量来讲是不容许的。
一个更好的方法是使用一组字节,每一个字节表明一个可能的类别。若是变量不能同时是多个类别,那么组中只能有一个字节是有数据的。这被称为 one-hot 编码,它在 ScikitLearn 中实现为sklearn.preprocessing.OneHotEncoder 。每一个字节都是一个特征。所以,可能类别为 k 的分类变量被编码为长度为 k 的特征向量。
城市 | $e_1 $ | $e_2 $ | $e_3 $ |
---|---|---|---|
San Francisco | 1 | 0 | 0 |
New York | 0 | 1 | 0 |
Seattle | 0 | 0 | 1 |
独热编码很是容易理解。若是咱们看到位中的前 k-1 位都为零,那么最后一位必须为 1,由于变量必须取一个 k 值。在数学上,咱们能够把这个约束写成"全部位之和必须等于 1"。
约束用公式表示
$$ e_{1}+e_{2}+\ldots+e_{k}=1 $$
独热编码的问题在于它容许 k 个自由度,而变量自己只须要 k-1。虚拟编码经过在表示中仅使用 k-1 特征来消除多余的自由度。一个类别,由全部字节为零的向量表示。这就是所谓的参考类别。虚拟编码和一热编码,Pandas 中实现为pandas.get_dummies。
城市 | $e_1 $ | $e_2 $ |
---|---|---|
San Francisco | 1 | 0 |
New York | 0 | 1 |
Seattle | 0 | 0 |
与独热编码相比,采用虚拟编码的建模结果更具备可解释性。
这一点很容易在简单线性回归问题中看出来。
小案例
假设咱们有三个城市的公寓租赁价格数据:旧金山、纽约和西雅图。
import pandas as pd from sklearn import linear_model
df = pd.DataFrame({'City': ['SF', 'SF', 'SF', 'NYC', 'NYC', 'NYC', 'Seattle', 'Seattle', 'Seattle'], 'Rent': [3999, 4000, 4001, 3499, 3500, 3501, 2499, 2500, 2501]}) df
City | Rent | |
---|---|---|
0 | SF | 3999 |
1 | SF | 4000 |
2 | SF | 4001 |
3 | NYC | 3499 |
4 | NYC | 3500 |
5 | NYC | 3501 |
6 | Seattle | 2499 |
7 | Seattle | 2500 |
8 | Seattle | 2501 |
df['Rent'].mean()
3333.333333333335
咱们能够训练一个线性回归模型,仅仅根据城市的特征来预测租金价格。
线性回归模型能够这样写:
$$ y=w_{1} x_{1}+\ldots+w_{n} x_{n} $$
一般须要增长一个称为截距的常量项,这样当 x 为零时,y 能够是一个非零值:
$$ y=w_{1} x_{1}+\ldots+w_{n} x_{n}+b $$
独热编码处理
#将数据库中的分类变量转换为 one-hot编码 one_hot_df = pd.get_dummies(df, prefix=['city']) one_hot_df
Rent | city_NYC | city_SF | city_Seattle | |
---|---|---|---|---|
0 | 3999 | 0 | 1 | 0 |
1 | 4000 | 0 | 1 | 0 |
2 | 4001 | 0 | 1 | 0 |
3 | 3499 | 1 | 0 | 0 |
4 | 3500 | 1 | 0 | 0 |
5 | 3501 | 1 | 0 | 0 |
6 | 2499 | 0 | 0 | 1 |
7 | 2500 | 0 | 0 | 1 |
8 | 2501 | 0 | 0 | 1 |
#导入线性回归模型 lin_reg = linear_model.LinearRegression() #对线性回归模型进行训练,分类变量做为x,Rent做为y lin_reg.fit(one_hot_df[['city_NYC', 'city_SF', 'city_Seattle']], one_hot_df['Rent']) LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False) w1 = lin_reg.coef_ b1 = lin_reg.intercept_
#回归系数 lin_reg.coef_
array([ 166.66666667, 666.66666667, -833.33333333])
#截距项 lin_reg.intercept_
3333.3333333333335
虚拟编码处理
#pd.get_dummies里的drop_first为默认为false,默认是独热编码,当为true时,则为虚拟编码 dummy_df = pd.get_dummies(df, prefix=['city'], drop_first=True) dummy_df
Rent | city_SF | city_Seattle | |
---|---|---|---|
0 | 3999 | 1 | 0 |
1 | 4000 | 1 | 0 |
2 | 4001 | 1 | 0 |
3 | 3499 | 0 | 0 |
4 | 3500 | 0 | 0 |
5 | 3501 | 0 | 0 |
6 | 2499 | 0 | 1 |
7 | 2500 | 0 | 1 |
8 | 2501 | 0 | 1 |
#利用分类变量和Rent进行训练线性模型 lin_reg.fit(dummy_df[['city_SF', 'city_Seattle']], dummy_df['Rent'])
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=1, normalize=False) w2 = lin_reg.coef_ b2 = lin_reg.intercept_
#回归系数 lin_reg.coef_
array([ 500., -1000.])
#截距项 lin_reg.intercept_
3500.0
两种方法区别
独热编码处理,截距项表示目标变量 Rent 的全局均值,每一个线性系数表示城市平均租金与全局均值的差别。
虚拟编码处理,截距项表示参考类别(字节都为零的那个类别)的 y 的平均值,例子中的参考类别是纽约市。第 i 类特征的系数等于第 i 类特征的平均值与参考类别的平均值之差。
$x_1$ | $x_2$ | $x_3$ | $b$ | |
---|---|---|---|---|
独热编码 | 166.67 | 666.67 | -833.33 | 3333.33 |
虚拟编码 | 0 | 500 | -1000 | 3500 |
%matplotlib inline import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import seaborn as sns sns.set(style="whitegrid", font_scale=1.4, color_codes=True)
sns.swarmplot(x="City", y="Rent", data=df);
print('One-hot encoding weights: ',w1, ' and intercept: ', b1) print('Dummy encoding weights: ' , w2, ' and intercept: ', b2)
One-hot encoding weights: [ 166.66666667 666.66666667 -833.33333333] and intercept: 3333.33333333 Dummy encoding weights: [ 500. -1000.] and intercept: 3500.0
# 独热编码和虚拟编码对比回归结果 # 独热编码的线性回归结果 one_hot_y = [((w1[0] * one_hot_df.city_NYC[i]) + (w1[1] * one_hot_df.city_SF[i]) + (w1[2] * one_hot_df.city_Seattle[i]) + b1) for i in range(0,one_hot_df.shape[0])] # 虚拟编码的线性回归结果 dummy_y = [((w2[0] * dummy_df.city_SF[i]) + (w2[1] * dummy_df.city_Seattle[i]) + b2) for i in range(0,dummy_df.shape[0])] print(one_hot_y) print(dummy_y)
[4000.0, 4000.0, 4000.0, 3500.0, 3500.0, 3500.0, 2500.0, 2500.0, 2500.0] [3999.9999999999995, 3999.9999999999995, 3999.9999999999995, 3500.0, 3500.0, 3500.0, 2500.0, 2500.0, 2500.0]
另外一种类型的变量编码方法被称为效应编码。效应编码与虚拟编码很是类似,区别在于参考类别如今由全部 -1的向量表示。
城市 | $e_1$ | $e_2$ |
---|---|---|
San Francisco | 1 | 0 |
New York | 0 | 1 |
Seattle | -1 | -1 |
效应编码与虚拟编码很是类似,可是在线性回归模型中获得的结果更容易解释。
接下来演示如何将效应编码做为输入。截距项表示目标变量的全局平均值,单个系数表示单个类别的平均值与全局平均值的差别程度。(这称为类别或级别的主要影响,所以称为“效应编码”)。独热编码实际上产生了相同的截距和系数,但在这种状况下,每一个城市都有线性系数。在效应编码中,没有单个特征表明参考类别。(参见什么是效应编码?了解更多详细信息。)
小案例
效应编码的线性回归模型
#复制原始数据 effect_df = dummy_df.copy() #进行效应编码 effect_df.loc[3:5, ['city_SF', 'city_Seattle']] = -1.0 effect_df
Rent | city_SF | city_Seattle | |
---|---|---|---|
0 | 3999 | 1.0 | 0.0 |
1 | 4000 | 1.0 | 0.0 |
2 | 4001 | 1.0 | 0.0 |
3 | 3499 | -1.0 | -1.0 |
4 | 3500 | -1.0 | -1.0 |
5 | 3501 | -1.0 | -1.0 |
6 | 2499 | 0.0 | 1.0 |
7 | 2500 | 0.0 | 1.0 |
8 | 2501 | 0.0 | 1.0 |
#训练线性模型 lin_reg.fit(effect_df[['city_SF','city_Seattle']], effect_df['Rent'])
#回归系数 lin_reg.coef_
array([ 666.66666667, -833.33333333])
#截距项 lin_reg.intercept_
3333.3333333333335
独热编码 | 虚拟编码 | 效应编码 | |
---|---|---|---|
优势 | 容易理解 可处理丢失数据 |
不冗余 惟一性 可解释性强 |
不冗余 可处理丢失数据 |
缺点 | 冗余的 非惟一性 可解释性弱 |
没法轻松处理丢失数据 全零向量映射到参考类别 |
-1 的存储和计算都是复杂的 无现成工具包 |
当类别的数量级很是大时,上述三种编码技术都会崩溃,所以处理多类别分类变量须要不一样的策略。
互联网上的自动数据收集能够生成多类别分类变量。这在定向广告和欺诈检测等应用中很常见。在定向广告中,任务是根据用户的搜索查询或当前页面,为用户匹配一组广告。功能包括用户 id、广告的网站域名、搜索查询、当前页面,以及这些功能的全部可能的组合。(查询是一个文本字符串,能够被分割成一般的文本特征。可是,查询一般很短,一般由短语组成。所以,在这种状况下,最好的作法一般是保持它们的完整性,或者经过散列函数来简化存储和比较。
难点在于找到一个良好的特征表示,既能有效利用内存,又能快速生成准确的模型。
现有的解决方案:
特征缩放。
使用简单one-hot 编码是一个有效的选择。对于微软的搜索广告引擎,使用了贝叶斯回归模型中的这种二进制值特性,能够经过简单的更新在线训练。与此同时,其余团体则主张采用缩放方法。雅虎的研究人员青睐特性哈希法。与此同时,微软的其余员工接受了bin-counting
的想法。
咱们将先介绍这三种方法,而后讨论它们的优缺点。
哈希函数是一个肯定性函数,它将一个潜在的无界整数映射到一个有界的整数范围[1,m]。
因为输入域可能大于输出范围,所以能够将多个数字映射到相同的输出。这就是所谓的碰撞。一个统一的哈希函数能够确保将编号相同的数据映射到 m 个箱子中的一个。
总而言之,咱们能够把哈希函数想象成一台机器,它输入编号的球,并将其映射到到 m 个箱子中的一个。号码相同的球老是会被送到相同的箱子中。
哈希函数能够以任何能够用数字表示的对象构造(对于任何能够存储在计算机上的数据都是如此):数字、字符串、复杂结构等等。
哈希函数将键映射到箱子。这样既保持了特征空间,又减小了机器学习训练和评估周期中的存储
和处理时间。
当特征不少的时候,存储特征向会占用不少空间。特征哈希经过对特征 id 应用哈希函数将原始特征向
量压缩为一个 m 维向量。例如,若是原始特征是文档中的单词,那么不管输入中有多少个惟一的单词,
通过哈希函数处理以后的词汇表大小都是固定的m。
关于文本数据的特征哈希法
def hash_features(word_list, m): output = [0] * m for word in word_list: index = hash_fcn(word) % m #hash_fcn()是须要根据不一样情景本身定义的 output[index] += 1 return output
特性哈希的另外一种变体添加了一个符号函数,这样就能够从哈希容器中增减成员。
这能够确保哈希特征之间的内部积与原始特征之间的指望值相等。
def hash_features(word_list, m): output = [0] * m for word in word_list: index = hash_fcn(word) % m # hash_fcn()是须要根据不一样情景本身定义的 sign_bit = sign_hash(word) % 2 # sign_hash()一样是须要本身定义 if (sign_bit == 0): output[index] -= 1 else: output[index] += 1 return output
哈希后内积的值在原始内积的$O\left(\frac{1}{\sqrt{m}}\right)$范围内。所以,哈希表的大小m是在必定偏差范围内被选择的。
在实践中,选择正确的m可能须要反复试错。
特征哈希方法可用于处理特征向量的和回归系数的内积,如线性模型和核方法。
特性哈希的一个缺点是,哈希特征是原始特征的集合,再也不具备可解释性。
在这个小案例中,咱们将使用Yelp评论数据集来演示使用Scikit-learn中FeatureHasher的存储和可解释性之间的权衡。
# 加载前 10000 条评论 f = open('data/yelp/v6/yelp_dataset_challenge_academic_dataset/yelp_academic_dataset_review.json') #数据书籍原文并无提供 js = [] for i in range(10000): js.append(json.loads(f.readline())) f.close() review_df = pd.DataFrame(js) review_df.shape
(10000, 8)
# 咱们将m的值定义为business_id的惟一值的数量 m = len(review_df.business_id.unique()) print(m)
528
from sklearn.feature_extraction import FeatureHasher h = FeatureHasher(n_features=m, input_type='string') f = h.transform(review_df['business_id'])
# 这怎么影响可解释性的呢 review_df['business_id'].unique().tolist()[0:5]
['vcNAWiLM4dR7D2nwwJ7nCA', 'UsFtqoBl7naz8AVUBZMjQQ', 'cE27W9VPgO88Qxe4ol6y_g', 'HZdLhv6COCleJMo7nPl-RA', 'mVHrayjG3uZ_RLHkLj-AMg']
f.toarray()
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.], [ 0., 0., 0., ..., 0., 0., 0.]])
可解释性不是很好。可是,让咱们看看咱们的特性的存储大小
# 咱们能够经过观察它们的大小来了解它们在将来将如何发挥做用 from sys import getsizeof print('Our pandas Series, in bytes: ', getsizeof(review_df['business_id'])) print('Our hashed numpy array, in bytes: ', getsizeof(f))
Our pandas Series, in bytes: 790104
Our hashed numpy array, in bytes: 56
咱们能够清楚地看到,使用特征哈希将使咱们在计算上受益,牺牲了可解释性。当从数据探索和可视化发展到大型数据集的机器学习工做流时,这是一个很容易接受的折衷方案。
Bin-counting
是机器学习中的一个常年不断的从新发现。它已经被从新发明并用于各类各样的应用,从广告点进率预测到硬件分支预测。然而,因为它是一种特征工程技术,而不是一种建模或优化方法,所以目前尚未相关的研究论文。关于这项技术最详细的描述能够在 MishaBilenko 的 博客"Big Learning Made Easy with Counts" 找到。
Bin-counting
的想法很是简单:与其使用类别变量的值做为特征,不如使用该值下目标变量的条件几率。换句话说,不是编码和类别值统一块儿来,而是计算该值与咱们但愿预测的目标之间的关联统计数据。对于若是熟悉朴素贝叶斯分类器,由于在假设全部特征都是条件独立的状况下,这个数据就是这个类别的条件几率。
举例说明
User | Number of clicks | Number of non-clicks | Proy of click | QueryHash,AdDomain | Number of clicks | Number of non-clicks | Probability of click |
---|---|---|---|---|---|---|---|
Alice | 5 | 120 | 0.04 | 0x598fd4fe, foo.com |
5,000 | 30,000 | 0.167 |
Bob | 20 | 230 | 0.08 | 0x50fa3cc0, bar.org |
100 | 9,00 | 0.100 |
Joe | 2 | 3 | 0.4 | 0x437a45e1, qux.net |
6 | 18 | 0.250 |
Bin-counting
假定历史数据可用于统计计算。包含分类变量每一个可能值的汇总历史数据。
根据用户点击任何广告的次数和未点击的次数,咱们能够计算出用户"Alice"点击任何广告的几率。
相似地,咱们能够计算任何查询-广告-域组合的点击几率。
在训练时,每次咱们看到"Alice",使用她的点击几率做为模型的输入特征。
对于像"0x437a45e1,qux.net"这样的 QueryHash-AdDomain 组合也是如此。
z假设有 10000 个用户。一个独热编码将生成一个长度为 10,000 的稀疏向量,对应于当前数据点值的列中
只有一个 1。Bin-counting
将全部 10,000 个二进制列编码为一个实际值介于 0 和 1 之间的单一特征。
除了历史点击几率以外,咱们还能够包括其余特征:
咱们这里的例子是预测广告点击率。可是这种技术很容易应用于通常的二分类。它也能够很容易地扩展到多分类问题。
Bin-Counting
的计算
它经过提出这样一个问题来考察它们之间的联系强度:
"当 x 为真时y 有多大可能为真。"
例如,咱们可能会问,"爱丽丝点击广告的可能性比通常人高多少?"
这里,x 是二进制变量"当前用户是Alice",y 是变量"是否点击"
这种计算使用了所谓的列联表(基本上是对应于 x 和y 的 4 种可能组合的 4 个数字)。
Click | Non-Click | Total | |
---|---|---|---|
Alice | 5 | 120 | 125 |
Not Alice | 995 | 18,880 | 19,875 |
Total | 1000 | 19,000 | 20,000 |
给定一个输入变量$x$,和目标变量$y$,定义
odds_ratio
$ = \frac{(P(Y=1 | X=1) / P(Y=0 | X=1)}{(P(Y=1 | X=0) / P(Y=0 | X=0)}$
在咱们的例子中,这就是"Alice点击广告而不是不点击的可能性有多大"和"其余人点击而不是不点击的可
能性有多大"之间的比率在这个例子中是
odds ratio (user, ad click)
$=\frac{(5 / 125) /(120 / 125)}{(995 / 19,875) /(18,880 / 19,875)}=0.7906$
更简单地说,咱们能够看一下分子,它检查单个用户(Alice)点击广告与不点击广告的可能性有多大。这
适用于具备多类别的分类变量,而不只仅是两个值。
odds ratio (Alice, ad click)
$=\frac{5 / 125}{120 / 125}=0.04166$
比值很容易变得很是小或很是大。例如,有些用户几乎历来不点击广告,也许有些用户点击广告的频率要比不点击的频繁得多。
对数变换再次来拯救咱们,对数的另外一个有用的特性是它把除法变成减法。
log-odds ratio (Alice, ad click)
$=\log \left(\frac{5}{125}\right)-\log \left(\frac{120}{125}\right)=-3.178$
简而言之,Bin-counting
将一个分类变量转换为关于值的统计信息。
它将分类变量的大型稀疏矩阵表示转换为很是小的稠密的实数数值表示。(以下图)
在实现方面,bin-counting
须要在每一个类别及其相关计数之间存储一个映射。(剩下的统计数据能够从原始计数中动态地推导出来。)所以它须要$ O(k)$的复杂度,其中 k 是分类变量惟一值的个数。
小案例
点进率数据集统计案例。
竞赛是用广告数据来预测点进率,可是咱们将用它来演示如何经过计算垃圾桶来大大减小大量流数据的特征空间。
import pandas as pd
df = pd.read_csv('data/train_subset.csv')
df.head(3)
id | click | hour | C1 | banner_pos | site_id | site_domain | site_category | app_id | app_domain | ... | device_type | device_conn_type | C14 | C15 | C16 | C17 | C18 | C19 | C20 | C21 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1.000009e+18 | 0 | 14102100 | 1005 | 0 | 1fbe01fe | f3845767 | 28905ebd | ecad2386 | 7801e8d9 | ... | 1 | 2 | 15706 | 320 | 50 | 1722 | 0 | 35 | -1 | 79 |
1 | 1.000017e+19 | 0 | 14102100 | 1005 | 0 | 1fbe01fe | f3845767 | 28905ebd | ecad2386 | 7801e8d9 | ... | 1 | 0 | 15704 | 320 | 50 | 1722 | 0 | 35 | 100084 | 79 |
2 | 1.000037e+19 | 0 | 14102100 | 1005 | 0 | 1fbe01fe | f3845767 | 28905ebd | ecad2386 | 7801e8d9 | ... | 1 | 0 | 15704 | 320 | 50 | 1722 | 0 | 35 | 100084 | 79 |
<p>3 rows × 24 columns</p>
# 分类变量的惟一值个数 len(df['device_id'].unique())
device_id
这个特征的惟一值数量是 7201 个
对于每个分类变量 $\theta$ ,咱们都须要进行以下转换计算:
$\theta$ = [$N^+$, $N^-$, $log(N^+)-log(N^-)$, 其余衍生变量]
$N^+$ = $p(+)$ = $n^+/(n^+ + n^-)$
$N^-$ = $p(-)$ = $n^-/(n^+ + n^-)$
$log(N^+)-log(N^-)$ = $\frac{p(+)}{p(-)}$
$n^+$是表明一个特征
其余衍生变量=: (这里没有展现)
代码实现以下所示
def click_counting(x, bin_column): clicks = pd.Series(x[x['click'] > 0][bin_column].value_counts(), name='clicks') no_clicks = pd.Series(x[x['click'] < 1][bin_column].value_counts(), name='no_clicks') counts = pd.DataFrame([clicks,no_clicks]).T.fillna('0') counts['total'] = counts['clicks'].astype('int64') + counts['no_clicks'].astype('int64') return counts def bin_counting(counts): counts['N+'] = counts['clicks'].astype('int64').divide(counts['total'].astype('int64')) counts['N-'] = counts['no_clicks'].astype('int64').divide(counts['total'].astype('int64')) counts['log_N+'] = counts['N+'].divide(counts['N-']) # 咱们直接筛选出['N+', 'N-', 'log_N+']这些字段 bin_counts = counts.filter(items= ['N+', 'N-', 'log_N+']) return counts, bin_counts
# 以device_id为例 bin_column = 'device_id' device_clicks = click_counting(df.filter(items= [bin_column, 'click']), bin_column) device_all, device_bin_counts = bin_counting(device_clicks)
# 检验是否数目一致 len(device_bin_counts) # 7201
因而咱们利用bin-counting
的特征处理方法,新生成的数据表结果以下。
device_all.sort_values(by = 'total', ascending=False).head(4)
clicks | no_clicks | total | N+ | N- | log_N+ | |
---|---|---|---|---|---|---|
a99f214a | 15729 | 71206 | 86935 | 0.180928 | 0.819072 | 0.220894 |
c357dbff | 33 | 134 | 167 | 0.197605 | 0.802395 | 0.246269 |
31da1bd0 | 0 | 62 | 62 | 0.000000 | 1.000000 | 0.000000 |
936e92fb | 5 | 54 | 59 | 0.084746 | 0.915254 | 0.092593 |
# 对比独热编码和binning-count两种方法处理后的结果 from sys import getsizeof print('Our pandas Series, in bytes: ', getsizeof(df.filter(items= ['device_id', 'click']))) print('Our bin-counting feature, in bytes: ', getsizeof(device_bin_counts))
Our pandas Series, in bytes: 7,300,031
Our bin-counting feature, in bytes: 525,697
从这里咱们能够清楚地看到,bin-counting
处理完后,数据量下降了一个数量级。
独热编码方法 | 特征哈希方法 | Bin-counting方法 | |
---|---|---|---|
优势 | 最容易实现、多是最准确的 、可线上进行 | 容易实现、训练成本低 适应新的类别、可处理稀有类别 可线上进行 | 训练时最小的计算负担、容易适应新的类别使用树模型可解释性强 |
缺点 | 计算效率低、不适应不断增加的类别、只适用线性模型 | 仅适用于线性或核模型哈希后特征不可解释、准确度报告不一 | 须要历史数据、须要延迟更新,不适合线上进行、更高的过拟合风险 |
没有一个方法是完美的,选择使用哪个取决于所采用的机器学习模型。线性模型训练成本更低,更适合采用独热编码。基于树的模型一般采用Bin-counting
的方法。特征哈希方法须要结合实际状况,灵活运用。
zeof(device_bin_counts))
Our pandas Series, in bytes: **7,300,031** Our bin-counting feature, in bytes: **525,697** 从这里咱们能够清楚地看到,`bin-counting`处理完后,数据量下降了一个数量级。 优缺点对比 ---------- | | 独热编码方法 | 特征哈希方法 | Bin-counting方法 | | ---- | ------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 优势 | 最容易实现、多是最准确的 、可线上进行 | 容易实现、训练成本低 适应新的类别、可处理稀有类别 可线上进行 | 训练时最小的计算负担、容易适应新的类别使用树模型可解释性强 | | 缺点 | 计算效率低、不适应不断增加的类别、只适用线性模型 | 仅适用于线性或核模型哈希后特征不可解释、准确度报告不一 | 须要历史数据、须要延迟更新,不适合线上进行、更高的过拟合风险 | 没有一个方法是完美的,选择使用哪个取决于所采用的机器学习模型。线性模型训练成本更低,更适合采用独热编码。基于树的模型一般采用`Bin-counting`的方法。特征哈希方法须要结合实际状况,灵活运用。 *本文内容整理、翻译Principl的《Feature Engineering for Machine Learning》* *原文代码地址:https://github.com/alicezheng/feature-engineering-book.*