处理分类数据

简介

本身没有内在顺序的类别称为nominal,如果一组分类天然拥有内在的顺序性就称之ordinal。

一、对nominal型分类特征编码

利用scikit-learn的LabelBinarizer对特征进行one-hot编码(独热编码):

import numpy as np
from sklearn.preprocessing import LabelBinarizer,MultiLabelBinarizer
feature=np.array([["Texas"],
                  ["California"],
                  ["Texas"],
                  ["Delaware"],
                  ["Texas"]])
one_hot=LabelBinarizer()
one_hot.fit_transform(feature)

可以使用classes_方法输出分类:

#查看特征的分类
one_hot.classes_

如果希望对one-hot编码进行逆转换,可以使用inverse_transform:

one_hot.inverse_transform(one_hot.transform(feature))

我们甚至可以使用pandas对特征进行one-hot编码:

import pandas as pd
#创建虚拟变量
pd.get_dummies(feature[:,0])

 

scikit-learn还能处理每个观察值有多个分类的情况:

multiclass_feature=[("Texas","Florida"),
                    ("California","Alabama"),
                    ("Texas","Florida"),
                    ("Delware","Florida"),
                    ("Texas","Alabama")]
one_hot_multiclass=MultiLabelBinarizer()
one_hot_multiclass.fit_transform(multiclass_feature)

二、对ordinal分类特征编码

使用pandas的DataFrame的replace方法将字符串标签转换成相应的数字:

import pandas as pd
dataframe=pd.DataFrame({"Score":["Low","Low","Medium","Medium","High"]})
scaler_mapper={"Low":1,
               "Medium":2,
               "High":3}
dataframe["Score"].replace(scaler_mapper)

只有知道ordinal分类的顺序信息,才能决定每个分类对应的数值。如果分类之间的间隔不相等,就有问题:

dataframe=pd.DataFrame({"Score":["Low",
                                 "Low",
                                 "Medium",
                                 "Medium",
                                 "High",
                                 "Barely More Than Medium"]})
scale_mapper={"Low":1,
              "Medium":2,
              "Barely More Than Medium":3,
              "High":4}
dataframe["Score"].replace(scale_mapper)

三、对特征字典进行编码

使用DictVectorizer:

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)
features

默认情况下,DictVectorizer会输出一个稀疏矩阵来存储0以外的元素。如果矩阵很庞大,这么做有助于节省内存。通过指定sparse=False能强制DictVectorizer输出一个稠密矩阵。

使用get_feature_names_out方法可以获取所生成的特征的名字:

feature_names=dictvectorizer.get_feature_names_out()
feature_names

四、填充缺失的分类值

最理想的解决方案是训练一个机器学习分类器来预测缺失值,通常会使用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]])
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:]))
np.vstack((X_with_imputed,X))

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

from sklearn.impute import SimpleImputer
X_complete=np.vstack((X_with_nan,X))
simpleimputer=SimpleImputer(strategy='most_frequent')
simpleimputer.fit_transform(X_complete)

不管用哪个方案,都最好添加一个二元特征来标识观察值中是否包含填充值。

五、处理不均衡分类

可以收集更多的数据。如果做不到,就改变评估模型的衡量标准。次之,可以考虑使用嵌入分类权重参数的模型、下采样或上采样。

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)
#查看不均衡的目标向量
target

scikit-learn的很多算法都提供了一个参数,可以在训练时对分类进行加权,依次抵消分类数据不均衡带来的影响。RandomForestClassifier是一个流行的分类算。它也有一个class_weight参数。这个参数可以显式地指定想要的分类权重:

weights={0:.9,1:0.1}
#创建带权重的随机森林分类器
RandomForestClassifier(class_weight=weights)

还可以传入参数balanced,它会自动创建与分类的频数成反比的权重:

#训练一个带均衡分类权重的随机森林分类器
RandomForestClassifier(class_weight="balanced")

如果占少数的分类有10个观察值,那么就从占多数的分类中随机无放回地选取10个观察值,然后用这20个观察值作为数据集

#给每个分类的观察值打标签
i_class0=np.where(target==0)[0]
i_class1=np.where(target==1)[0]
#确定每个分类的观察值的数量
n_class0=len(i_class0)
n_class1=len(i_class1)
#对于每个分类为0的观察值,从分类为1的数据中进行无放回的随机采样
i_class1_downsampled=np.random.choice(i_class1,size=n_class0,replace=False)
#将分类为0的目标向量和下采样的分类为1的目标向量连接起来
np.hstack((target[i_class0],target[i_class1_downsampled]))

#将分类为0的特征矩阵和下采样的分类为1的特征矩阵连接起来
np.vstack((features[i_class0],features[i_class1_downsampled]))[0:5]

另一个选择是对占多数的分类进行上采样。在上采样中,针对占多数的分类,从占少数的分类中进行有放回的随机采样。上采样和下采样的实现方式类似,只是反转了一下:

#对于每个分类为1的观察值,从分类为0的数据中进行有放回的随机采样
i_class0_unsampled=np.random.choice(i_class0,size=n_class1,replace=True)
#将上采样得到的分类为0的目标向量和分类为1的目标向量连接起来
np.concatenate((target[i_class0_unsampled],target[i_class1]))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值