公众号:尤而小屋
作者:Peter
编辑:Peter
大家好,我是Peter~
本文给大家介绍一种数据挖掘中常见的特征处理方式:基于独热码的哑变量生成。
哑变量
又叫做虚拟变量
,虚设变量
或者名义变量
,是人为设定的用于将分类变量引入回归模型中的方法。
比如学历、职业、性别等分类变量的数据是本身不能量化的,但是可以通过构造0和1的哑变量可以考察定性因素(分类变量)对因变量的影响。
哑变量一般在回归问题的相关模型中经常使用。在虚拟变量的设置中:表示的基础类型、肯定类型取值为1;如果是比较类型,否定类型则取值为0。
在实际的数据处理中,通过独热码one-hot
来实现哑变量。Pandas中的get_dummies
函数能够实现此功能。
下面这张图很形象地解释了哑变量的生成过程:
get_dummies使用
Pandas的get_dummies可以用来生成哑变量,下面是函数的参数解释与应用:
pandas.get_dummies(data, # 待处理数据
prefix=None, # 前缀
prefix_sep='_', # 和前缀的连接符
dummy_na=False, # 是否显示控制
columns=None, # 指定字段
sparse=False, # 是否表示为稀疏矩阵
drop_first=False, # 是否删除生成后的第一个字段
dtype=None) # 指定字段类型
import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder
s = pd.Series(list("abadc"))
s
0 a
1 b
2 a
3 d
4 c
dtype: object
pd.get_dummies(s)
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
a | b | c | d | |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
2 | 1 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 1 |
4 | 0 | 0 | 1 | 0 |
可以看到上列名展开成了4个,也就是s中的4个取值。
前缀处理-prefix
pd.get_dummies(s) # 默认
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
a | b | c | d | |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
2 | 1 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 1 |
4 | 0 | 0 | 1 | 0 |
统一加上一个前缀col
pd.get_dummies(s, prefix="col") # 统一加上前缀col,默认连接符是_
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
col_a | col_b | col_c | col_d | |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
2 | 1 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 1 |
4 | 0 | 0 | 1 | 0 |
连接符
默认的连接符是_
,指定为.
pd.get_dummies(s, prefix="col", prefix_sep=".") # 统一加上前缀col,连接符是.
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
col.a | col.b | col.c | col.d | |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
2 | 1 | 0 | 0 | 0 |
3 | 0 | 0 | 0 | 1 |
4 | 0 | 0 | 1 | 0 |
空值处理
s1 = pd.Series(["a","b",np.nan,"c"])
s1
0 a
1 b
2 NaN
3 c
dtype: object
pd.get_dummies(s1, dummy_na=False) # 默认是False
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
a | b | c | |
---|---|---|---|
0 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
2 | 0 | 0 | 0 |
3 | 0 | 0 | 1 |
默认空值是不显示的,下面改成显示空值:
pd.get_dummies(s1, dummy_na=True) # 显示空值
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
a | b | c | NaN | |
---|---|---|---|---|
0 | 1 | 0 | 0 | 0 |
1 | 0 | 1 | 0 | 0 |
2 | 0 | 0 | 0 | 1 |
3 | 0 | 0 | 1 | 0 |
删除第一个字段
pd.get_dummies(s1)
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
a | b | c | |
---|---|---|---|
0 | 1 | 0 | 0 |
1 | 0 | 1 | 0 |
2 | 0 | 0 | 0 |
3 | 0 | 0 | 1 |
pd.get_dummies(s1, drop_first=True)
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
b | c | |
---|---|---|
0 | 0 | 0 |
1 | 1 | 0 |
2 | 0 | 0 |
3 | 0 | 1 |
指定字段类型dtype
pd.get_dummies(s1, dtype="float")
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
a | b | c | |
---|---|---|---|
0 | 1.0 | 0.0 | 0.0 |
1 | 0.0 | 1.0 | 0.0 |
2 | 0.0 | 0.0 | 0.0 |
3 | 0.0 | 0.0 | 1.0 |
案例1-两种分类
df = pd.DataFrame({
"id":["ID1","ID2","ID3","ID4","ID5","ID6"],
"sex":["Female","Male","Female","Male","Male","Female"],
"amount":[2000,2500,1800,1900,2000,2600]
})
df
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | sex | amount | |
---|---|---|---|
0 | ID1 | Female | 2000 |
1 | ID2 | Male | 2500 |
2 | ID3 | Female | 1800 |
3 | ID4 | Male | 1900 |
4 | ID5 | Male | 2000 |
5 | ID6 | Female | 2600 |
sex字段中存在两种取值:
pd.get_dummies(df["sex"])
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
Female | Male | |
---|---|---|
0 | 1 | 0 |
1 | 0 | 1 |
2 | 1 | 0 |
3 | 0 | 1 |
4 | 0 | 1 |
5 | 1 | 0 |
结果:从sex变量延伸出两个变量Female和Male,这两个变量就是sex中的不同取值。
当原数据中出现了Female,则哑变量Female取值为1,否则为0;Male是一样的
pd.get_dummies(df["sex"], prefix="sex")
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
sex_Female | sex_Male | |
---|---|---|
0 | 1 | 0 |
1 | 0 | 1 |
2 | 1 | 0 |
3 | 0 | 1 |
4 | 0 | 1 |
5 | 1 | 0 |
# 指定对sex执行独热码
pd.get_dummies(df, columns=["sex"])
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | amount | sex_Female | sex_Male | |
---|---|---|---|---|
0 | ID1 | 2000 | 1 | 0 |
1 | ID2 | 2500 | 0 | 1 |
2 | ID3 | 1800 | 1 | 0 |
3 | ID4 | 1900 | 0 | 1 |
4 | ID5 | 2000 | 0 | 1 |
5 | ID6 | 2600 | 1 | 0 |
当指定columns,会自动加上前缀。
案例2-多种分类
df1 = pd.DataFrame({
"id":["ID1","ID2","ID3","ID4","ID5","ID6"],
"sex":["Female","Male","Female","Male","Male","Female"],
"education":["高中","本科","本科","研究生","本科","研究生"],
"amount":[1000,2500,1800,4900,2000,3600]
})
df1
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | sex | education | amount | |
---|---|---|---|---|
0 | ID1 | Female | 高中 | 1000 |
1 | ID2 | Male | 本科 | 2500 |
2 | ID3 | Female | 本科 | 1800 |
3 | ID4 | Male | 研究生 | 4900 |
4 | ID5 | Male | 本科 | 2000 |
5 | ID6 | Female | 研究生 | 3600 |
pd.get_dummies(df1["education"])
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
本科 | 研究生 | 高中 | |
---|---|---|---|
0 | 0 | 0 | 1 |
1 | 1 | 0 | 0 |
2 | 1 | 0 | 0 |
3 | 0 | 1 | 0 |
4 | 1 | 0 | 0 |
5 | 0 | 1 | 0 |
pd.get_dummies(df1, columns=["education"])
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | sex | amount | education_本科 | education_研究生 | education_高中 | |
---|---|---|---|---|---|---|
0 | ID1 | Female | 1000 | 0 | 0 | 1 |
1 | ID2 | Male | 2500 | 1 | 0 | 0 |
2 | ID3 | Female | 1800 | 1 | 0 | 0 |
3 | ID4 | Male | 4900 | 0 | 1 | 0 |
4 | ID5 | Male | 2000 | 1 | 0 | 0 |
5 | ID6 | Female | 3600 | 0 | 1 | 0 |
案例3-多个字段
df1
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | sex | education | amount | |
---|---|---|---|---|
0 | ID1 | Female | 高中 | 1000 |
1 | ID2 | Male | 本科 | 2500 |
2 | ID3 | Female | 本科 | 1800 |
3 | ID4 | Male | 研究生 | 4900 |
4 | ID5 | Male | 本科 | 2000 |
5 | ID6 | Female | 研究生 | 3600 |
pd.get_dummies(df1, columns=["sex","education"])
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | amount | sex_Female | sex_Male | education_本科 | education_研究生 | education_高中 | |
---|---|---|---|---|---|---|---|
0 | ID1 | 1000 | 1 | 0 | 0 | 0 | 1 |
1 | ID2 | 2500 | 0 | 1 | 1 | 0 | 0 |
2 | ID3 | 1800 | 1 | 0 | 1 | 0 | 0 |
3 | ID4 | 4900 | 0 | 1 | 0 | 1 | 0 |
4 | ID5 | 2000 | 0 | 1 | 1 | 0 | 0 |
5 | ID6 | 3600 | 1 | 0 | 0 | 1 | 0 |
OneHotEncoder()使用
官网案例
enc = OneHotEncoder()
enc.fit([[0,0,3],
[1,1,0],
[0,2,1],
[1,0,2]]) #这里一共有4个数据,3种特征
array = enc.transform([[0,1,3]]).toarray()
print(array)
[[1. 0. 0. 1. 0. 0. 0. 0. 1.]]
上面结果的解释:原数据是4行3列:4行记录,3个特征
特征1 | 特征2 | 特征3 | |
---|---|---|---|
1 | 0 | 0 | 3 |
2 | 1 | 1 | 0 |
3 | 0 | 2 | 1 |
4 | 1 | 0 | 2 |
可以看到:
- 特征1存在两种取值,独热码是2位
- 特征2存在两种取值,独热码是3位
- 特征3存在4种取值,独热码是4位
分析新的测试数据:0,1,3
- 0:是第一个特征的取值,出现在第一位,表示为10
- 1:是第二个特征的取值,出现在第二位,表示为010
- 3:是第三个特征的取值,出现在第四位,表示为0001
总体表示为:100100001
再来一个测试案例:
array = enc.transform([[1,2,1]]).toarray()
print(array)
[[0. 1. 0. 0. 1. 0. 1. 0. 0.]]
实际案例
df1
.dataframe tbody tr th:only-of-type {
vertical-align: middle;
}
.dataframe tbody tr th {
vertical-align: top;
}
.dataframe thead th {
text-align: right;
}
id | sex | education | amount | |
---|---|---|---|---|
0 | ID1 | Female | 高中 | 1000 |
1 | ID2 | Male | 本科 | 2500 |
2 | ID3 | Female | 本科 | 1800 |
3 | ID4 | Male | 研究生 | 4900 |
4 | ID5 | Male | 本科 | 2000 |
5 | ID6 | Female | 研究生 | 3600 |
enc = OneHotEncoder()
enc.fit(df1[["sex", "education"]])
OneHotEncoder()
enc.categories_
[array(['Female', 'Male'], dtype=object),
array(['本科', '研究生', '高中'], dtype=object)]
# 测试案例
enc.transform([["Male","研究生"]]).toarray()
array([[0., 1., 0., 1., 0.]])
解释为:
- Male在第二位:编码为01
- 研究生在第二位:编码为010
总体编码为01010