原创

机器学习手册04---处理分类数据


nominal:本身没有内在顺序的类别,如:蓝色、红色;男性、女性等。

ordinal:天然拥有内在顺序的类别,如:低、中、高;同意、中立、反对等。

1.对nominal型分类特征编码

方法一:利用sklearn中的LabelBinarizer对特征进行one-hot编码(独热编码)。

import numpy as np
from sklearn.preprocessing import LabelBinarizer, MultiLabelBinarizer

feature = np.array([['Texas'],
                    ['California'],
                    ['Texas'],
                    ['Delaware'],
                    ['Texas']])

# 创建one-hot编码器
one_hot = LabelBinarizer()

# 对特征进行oune-hot编码
print(one_hot.fit_transform(feature))
[[0 0 1]
 [1 0 0]
 [0 0 1]
 [0 1 0]
 [0 0 1]]
# 查看特征分类
print(one_hot.classes_)

# oue-hot编码逆转换
print(one_hot.inverse_transform(one_hot.transform(feature)))
['California' 'Delaware' 'Texas']
['Texas' 'California' 'Texas' 'Delaware' 'Texas']

方法二:利用pandas进行one-hot编码

import numpy as np
import pandas as pd

# 创建虚拟变量
print(pd.get_dummies(feature[:,0]))
   California  Delaware  Texas
0           0         0      1
1           1         0      0
2           0         0      1
3           0         1      0
4           0         0      1

其中,sklearn还能处理多个分类情况,利用MultiLabelBinarizer:

multiclass_feature = [('Texas', 'Florida'),
                      ('California', 'Alabama'),
                      ('Delware', 'Florida'),
                      ('Texas', 'Alabama')]

# 创建能处理多个分类的one-hot编码器
one_hot_mlticlass = MultiLabelBinarizer()

#  对特征进行one-hot编码
print(one_hot_mlticlass.fit_transform(multiclass_feature))

# 查看分类
print(one_hot_mlticlass.classes_)
[[0 0 0 1 1]
 [1 1 0 0 0]
 [0 0 1 1 0]
 [1 0 0 0 1]]

 ['Alabama' 'California' 'Delware' 'Florida' 'Texas']

【说明】:有的人会认为给每一个类别赋予数值型的值(如:1、2、3.....),但是由于这些分类没有内在顺序,使用这样的数值会错误的赋予分类一个并不存在的顺序。所以,正确的策略是为原特征中的每一分类都创建一个二元特征,这个方法成为“one-hot编码”或“虚拟变量”。另外值得注意的是,在one-hot编码后,最好从结果矩阵中删除一个one-hot编码的特征,以避免线性依赖。

2.对ordinal型分类特征编码

利用pandas数据帧的replace方法将字符串转换成相应的数字:

import pandas as pd

# 创建特征
dataframe = pd.DataFrame({'Score': ['Low', 'Low', 'Medium', 'Medium', 'High']})

# 创建映射器
scale_mapper = {'Low':1, 'Medium':2, 'High':3}

# 使用映射器来替换特征
print(dataframe['Score'].replace(scale_mapper))
0    1
1    1
2    2
3    2
4    3
Name: Score, dtype: int64

由此可见,将分类的字符串标签映射为一个数字,然后将其映射在特征上,是个不错的想法。但是关键的一点是,要弄清楚ordinal分类的顺序信息,才能决定每个分类对应的值。在绝大多数场景下是没有问题的,但是如果分类之间的间隔不相等,问题就出现了:解决方案是通过间隔的大小,适当调整数字间隔,来间接的表示分类间隔的问题。

# 创建特征
dataframe = pd.DataFrame({'Score': ['Low', 'Low', 'Medium', 'Medium', 'High','Barely More Than Medium']})
# 创建映射器
scale_mapper = {'Low':1, 'Medium':2, 'Barely More Than Medium':2.1,'High':3}
# 使用映射器来替换特征
print(dataframe['Score'].replace(scale_mapper))
0    1.0
1    1.0
2    2.0
3    2.0
4    3.0
5    2.1
Name: Score, dtype: float64

3.对特征字典编码

from sklearn.feature_extraction import DictVectorizer

# 创建一个字典
data_dict  = [{'Red': 2, 'Blue': 4},
              {'Red': 4, 'Blue': 3},
              {'Red': 1, 'Yellow': 2},
              {'Red': 2, 'Yellow': 2}]

# 创建字典向量化器
dictvectorizer = DictVectorizer(sparse=False)

# 将字典转换成特征矩阵
features = dictvectorizer.fit_transform(data_dict)

print(features)

# 查看特征的名字
features_names = dictvectorizer.get_feature_names()
print(features_names)
[[4. 2. 0.]
 [3. 4. 0.]
 [0. 1. 2.]
 [0. 2. 2.]]
['Blue', 'Red', 'Yellow']

默认情况下,Dictvectorizer会输出一个稀疏矩阵,如果矩阵很庞大(在自然语言处理中,大矩阵很常见),这么做有助于节省内存。通过sparse=False能强制输出一个稠密矩阵。

import pandas as pd

# 从特征中创建数据帧
print(pd.DataFrame(features, columns=features_names))
   Blue  Red  Yellow
0   4.0  2.0     0.0
1   3.0  4.0     0.0
2   0.0  1.0     2.0
3   0.0  2.0     2.0

4.填充缺失的分类值

有一个分类特征中包含缺失值,需要用预测值来填充,最理想的解决方案是训练一个机器学习分类器来预测缺失值,通常使用KNN分类器

import numpy as np
from sklearn.neighbors import KNeighborsClassifier

# 用分类特征创建特征矩阵
X = np.array([[0, 2.10, 1.45],
              [1, 1.18, 1.33],
              [0, 1.22, 1.27],
              [1, -0.21, -1.19]])

# 创建带缺失值的特征矩阵
X_with_nan= np.array([[np.nan, 0.87, 1.31],
                     [np.nan, -0.67, -0.22]])

# 训练KNN分类器
clf = KNeighborsClassifier(3, weights='distance')
trained_model = clf.fit(X[:,1:], X[:,0])

# 预测缺失值的分类
imputed_values = trained_model.predict(X_with_nan[:,1:])

# 将所预测的分类和它们的其他特征连接起来
X_with_imputed = np.hstack((imputed_values.reshape(-1,1), X_with_nan[:,1:]))

# 连接两个特征矩阵
print(np.vstack((X_with_imputed, X)))
[[ 0.    0.87  1.31]   //预测结果为0
 [ 1.   -0.67 -0.22]   //预测结果为1
 [ 0.    2.1   1.45]
 [ 1.    1.18  1.33]
 [ 0.    1.22  1.27]
 [ 1.   -0.21 -1.19]]

另一个解决方案:用特征中出现次数最多的值来填充缺失值:

from sklearn.preprocessing import Imputer

# 连接两个特征矩阵
X_complete = np.vstack((X_with_nan, X))

imputer = Imputer(strategy='most_frequent', axis=0)

print(imputer.fit_transform(X_complete))
[[ 0.    0.87  1.31]     //次数最多的值0
 [ 0.   -0.67 -0.22]     //次数最多的值0
 [ 0.    2.1   1.45]
 [ 1.    1.18  1.33]
 [ 0.    1.22  1.27]
 [ 1.   -0.21 -1.19]]

【说明】:当分类特征中存在缺失值时,最好的解决方案是利用机器学习算法预测缺失值,将带缺失值的特征作为目标向量,将其他特征作为特征矩阵,就能完成预测。常用的算法是KNN(以后的篇幅里会专门写),它将k个最近的观察之的中位数作为缺失值的补充值。另外,可以用特征中出现次数最多的分类来填充缺失值,虽然比KNN差一些,但是它更容易扩展到大数据集上。

5.处理不均衡分类

处理一个分类极度不均衡的目标向量,主要有三种方案:

(1)收集更多数据

(2)改变评估模型的衡量标准

(3)考虑嵌入分类权重参数的模型、下采样或上采样

下面我们将利用经典数据集费雪鸢尾花来进行练习:

import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris

# 加载费雪鸢尾花数据集
iris = load_iris()

# 创建特征矩阵
features = iris.data

# 创建目标向量
target = iris.target

# 移除前40个观察值
features = features[40:, :]
target = target[40:]

# 创建二元目标向量来标识是否为分类0
target = np.where((target == 0), 0, 1)

# 查看不均衡的目标向量
print(target)
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1]

scikit-learn的很多算法都提供了一个参数,可以在训练时对分类进行加权,以此抵消分类数据不均衡带来的影响。参数class_weight:显式指定分类权重:

# 创建权重
weights = {0:.9, 1: 0.1}

# 创建带权重的随机森林分类器
print(RandomForestClassifier(class_weight=weights))
RandomForestClassifier(bootstrap=True, ccp_alpha=0.0,
                       class_weight={0: 0.9, 1: 0.1}, criterion='gini',
                       max_depth=None, max_features='auto', max_leaf_nodes=None,
                       max_samples=None, min_impurity_decrease=0.0,
                       min_impurity_split=None, min_samples_leaf=1,
                       min_samples_split=2, min_weight_fraction_leaf=0.0,
                       n_estimators=100, n_jobs=None, oob_score=False,
                       random_state=None, verbose=0, warm_start=False)
# 训练一个带均衡分类权重的随机森林分类器
RandomForestClassifier(class_weight='balanced')

【说明】:处理不均衡的分类解决方案优解顺序:

(1)收集更多数据

(2)选择更适用于评估不均衡数据的标准

(3)分类权重参数

(4)下采样、上采样

  • 作者:李延松(联系作者)
  • 发表时间:2020-07-20 12:57
  • 版本声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 公众号转载:请在文末添加作者公众号二维码

评论

留言