Chapter 5. Handling Categorical Data
- 本笔记是典型的人工智能算法课程Machine Learning with Python Cookbook的学习笔记
- 所学的实战代码都放在代码压缩包里
- 实战代码的运行环境是python3.9 numpy 1.23.1
上一章:(89条消息) Machine Learning with Python Cookbook 学习笔记 第4章_五舍橘橙博客-CSDN博客 代码笔记仓库(qaq):yy6768/Machine-Learning-with-Python-Cookbook-notebook: 典型的人工智能算法笔记 (github.com)
5.0 Introduction
- 对象通常非常有用,而不是根据数量,而是根据某些质量
- 在数据中,分类信息通常表示为向量或字符串列(例如)Maine”、“Texas”、“Delaware)。问题是大多数机器学习算法需要输入值。
- 我们的目标是转换类别中的信息(序数、类别之间的相对间隔等)。在本章中,我们将介绍这种转换技术,以及在处理分类数据时经常遇到的其他挑战。
5.1 Encoding Nominal Categorical Feature
对类别特征编码
encodingCategory.py
import numpy as np from sklearn.preprocessing import LabelBinarizer, MultiLabelBinarizer feature = np.array([ ["Texas"], ["California"], ["Texas"], ["Delaware"], ["Texas"] ]) # 创建 one-hot encoder one_hot = LabelBinarizer() # one-hot 对feature 编码 print(one_hot.fit_transform(feature)) # 输出各种类别 print(one_hot.classes_) # 解码编码结果 print(one_hot.inverse_transform(one_hot.transform(feature))) # 方法2 使用pandas # Import library import pandas as pd # 从feature中创建dummy编码 print(pd.get_dummies(feature[:, 0])) multiclass_feature = [("Texas", "Florida"), ("California"
,
"Alabama"
)
,
(
"Texas"
,
"Florida"
)
,
(
"Delware"
,
"Florida"
)
,
(
"Texas"
,
"Alabama"
)
]
# Create 多类别编码 one_hot_multiclass
= MultiLabelBinarizer
(
)
# One-hot encode multiclass feature
print
(one_hot_multiclass
.fit_transform
(multiclass_feature
)
)
# 查看所有类别
print
(one_hot_multiclass
.classes_
)
结果:
[[0 0 1]
[1 0 0]
[0 0 1]
[0 1 0]
[0 0 1]]
['California' 'Delaware' 'Texas']
['Texas' 'California' 'Texas' 'Delaware' 'Texas']
California Delaware Texas
0 0 0 1
1 1 0 0
2 0 0 1
3 0 1 0
4 0 0 1
[[0 0 0 1 1]
[1 1 0 0 0]
[0 0 0 1 1]
[0 0 1 1 0]
[1 0 0 0 1]]
['Alabama' 'California' 'Delware' 'Florida' 'Texas']
Discussion
- encode的策略并不是为每个类分配一个数值
- encode的正确策略是为每一个类创建一个2元特征;通常这个特征被称为独热编码(one-hot)
- 查阅资料:[(88条消息) 详细详解One Hot编码-附代码_chary8088的博客-CSDN博客_onehot解码代码](https://blog.csdn.net/chary8088/article/details/79032223
- One-Hot编码,又称为一位有效编码,主要是采用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候只有一位有效。
- One-Hot不适合原本有序的序列,因为会破坏其原本的顺序
5.2 Encoding Ordinal Categorical Features
编码有序序列
有效的方式是使用pandas DataFrame中的replace函数
encodeOrdinal.py
# Load library
import pandas as pd
# Create features
dataframe = pd.DataFrame({
"Score": ["Low", "Low", "Medium", "Medium", "High"]})
# 创建mapper
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
Discussion
- 顺序序列是我们较为常见的序列,当需要对这些序列进行编码的时候,最好用数值去替换表示顺序的属性
- 当需要表示不同顺序序列之间的距离(程度)时,可以在mapper中凸显出来,例如
scale_mapper = {
"Low":1,
"Medium":2,
"Barely More Than Medium": 2.1, # 表示和medium类很贴近
"High":3}
dataframe["Score"].replace(scale_mapper)
5.3 Encoding Dictionaries of Features
对字典类型的特征进行encode,通常使用DictVectorizer
dictVectorizerExample.py
# Import library from sklearn.feature_extraction import DictVectorizer # Create dictionary data_dict = [{ "Red": 2, "Blue": 4}, { "Red": 4, "Blue": 3}, { "Red": 1, "Yellow": 2}, { "Red": 2, "Yellow": 2}] # 创建 dictionary vectorizer dictvectorizer = DictVectorizer(sparse=False) # 默认是生成一个产生稀疏
矩阵的dicvectorizer2 dictvectorizer2 = DictVectorizer() # 把字典转换为特征 features = dictvectorizer.fit_transform(data_dict) features2 = dictvectorizer2.fit_transform(data_dict) # 查看 feature matrix print(features) print(features2) # 查看特征的名字 dictvectorizer.get_feature_names即将被遗弃 feature_names = dictvectorizer.get_feature_names_out() # 查看 print(feature_names) # 还可以使用pandas import pandas as pd # 通过列来表明不同的特征 print(pd.DataFrame(features, columns=feature_names))
结果:
[[4. 2. 0.]
[3. 4. 0.]
[0. 1. 2.]
[0. 2. 2.]]
(0, 0) 4.0
(0, 1) 2.0
(1, 0) 3.0
(1, 1) 4.0
(2, 1) 1.0
(2, 2) 2.0
(3, 1) 2.0
(3, 2) 2.0
['Blue' 'Red' 'Yellow']
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
Discussion
-
字典是一个比较常用的数据结构,特别时在自然语言处理中,但是我们最后都需要转换成矩阵的格式
-
字典类型的矩阵非常庞大,如何处理这样的矩阵也是我们的目标(可以使用稀疏矩阵)
-
dictvectorizer.get_feature_names即将被遗弃,需要替换成dictvectorizer.get_feature_names_out()
5.4 Imputing Missing Class Values
填充丢失的类别值
最常用的方法仍然是KNN
imputing.py
# load libraries
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 learner
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)))
# 方法2 用最常用的值去替换缺失值
# impute已经被替代
from sklearn.impute import SimpleImputer
# Join the two feature matrices
X_complete = np.vstack((X_with_nan, X))
imputer = SimpleImputer(strategy='most_frequent')
print(imputer.fit_transform(X_complete))
结果
[[ 0. 0.87 1.31]
[ 1. -0.67 -0.22]
[ 0. 2.1 1.45]
[ 1. 1.18 1.33]
[ 0. 1.22 1.27]
[ 1. -0.21 -1.19]]
[[ 0. 0.87 1.31]
[ 0. -0.67 -0.22]
[ 0. 2.1 1.45]
[ 1. 1.18 1.33]
[ 0. 1.22 1.27]
[ 1. -0.21 -1.19]]
Discussion
- KNN是最常用的方法,测量值比较精准,但是在大数据面前表现比较差
- 用最常见的特征值没有KNN准确,但是它在大数据情况时可拓展性比KNN好,而且没有KNN那么复杂
5.5 Handling Imbalanced Classes
我们如果遇到极度不平衡的类的目标向量,如何解决
-
作者认为首先应该去搜集更多的数据
-
如果搜集的数据已经足够多的话,应该更改评估模型的指标
-
再然后如果还不能解决就要使用内置的类权重参数、下采样或者上采样
-
评估模型指标在之后的章节会介绍,这一节关注类权重参数、下采样和上采样的方法
handleImbalance.py
# Load libraries
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和非类0的observation分为两类
target = np.where((target == 0), 0, 1)
# 打印
print(target)
# 第一种处理,提供类权重
# 创建权重
weights = {
0: .9, 1: 0.1}
# 根据权重创建随机森岭
print(RandomForestClassifier(class_weight=weights))
# Train a random forest with balanced class weights
print(RandomForestClassifier(class_weight="balanced"))
# 处理方式2 进行下采样
# Indicies of each class' observations
i_class0 = np.where(target == 0)[0]
i_class1 = np.where(target == 1)[0]
# 读取类的大小
n_class0 = len(i_class0)
n_class1 = len(i_class1)
# For every observation of class 0, randomly sample
# 随机取样,使得class1 Observation数量和class0一样多
i_class1_downsampled = np.random.choice(i_class1, size=n_class0, replace=False)
# 连接
# 下采样
print(np.hstack((target[i_class0], target[i_class1_downsampled])))
print(np.vstack((features[i_class0,:], features[i_class1_downsampled,:]))[0:5])
# 处理方式3 上采样
# 对于class0来说,随机创建样本直至数量和class1一样
i_class0_upsampled = np.random.choice(i_class0, size=n_class1, replace=True)
# 目标向量
print(np.concatenate((target[i_class0_upsampled], target[i_class1])))
# 特征矩阵
print(np.vstack((features[i_class0_upsampled,:], features[i_class1,:]))[0:5])
[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]
RandomForestClassifier(class_weight={
0: 0.9, 1: 0.1})
RandomForestClassifier(class_weight='balanced')
[0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1]
[[5. 3.5 1.3 0.3]
[4.5 2.3 1.3 0.3]
[4.4 3.2 1.3 0.2]
[5. 3.5 1.6 0.6]
[5.1 3.8 1.9 0.4]]
[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 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 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 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]
[[5.1 3.8 1.9 0.4]
[5.1 3.8 1.6 0.2]
[5.3 3.7 1.5 0.2]
[5.3 3.7 1.5 0.2]
[4.5 2.3 1.3 0.3]]
Discussion
- 在现实世界中,不平衡的课程无处不在——大多数访问者不会点击购买按钮,幸好许多类型的癌症很少见。出于这个原因,处理不平衡类是机器学习中的常见活动。
- 策略优先级从高到低分别是
- 收集更多的观察结果——尤其是少数群体的观察结果。
- 使用更适合不平衡类的模型评估指标。我们在后面的章节中讨论的一些更好的指标是混淆矩阵、精度、召回、F1 分数和 ROC 曲线。
- 使用包含在某些模型实现中的类权重参数。这使我们可以让算法针对不平衡的类进行调整。幸运的是,许多 scikit-learn 分类器都有一个 class_weight 参数,使其成为一个不错的选择。
- 下采样和上采样。通常我们应该尝试两者来查看哪个产生更好的结果。
下一章:(93条消息) Machine Learning with Python Cookbook 学习笔记 第6章_五舍橘橘的博客-CSDN博客