加利福尼亚大学伯克利分校的杰出统计学家莱奥·布莱曼Leo Breiman once said “live with your data before you plunge into modeling.”
保证输入数据的质量不言而喻,从数据收集完成到应用模型训练的中间需要对数据进行初步的整理加工,已达到适合模型要求、提高建模效率等目的。本节将从两大方面概述数据预处理的方法(专业术语称之为特征工程)。
![]()
0、示例演示数据及主要R包
- 示例数据:房价数据
ames <- AmesHousing::make_ames()
dim(ames)
set.seed(123)
library(rsample)
split <- initial_split(ames, prop = 0.7,
strata = "Sale_Price")
ames_train <- training(split)
# [1] 2049 81
ames_test <- testing(split)
# [1] 881 81
- 主要R包:
recipes
包专门用于处理建模前的数据处理,具体分为三步(function
):
(1)recipe: where you define your feature engineering steps to create your blueprint. 记录对数据哪些特征进行哪些处理(按照先后顺序)
(2)prepare: estimate feature engineering parameters based on training data. 有些数据处理需要根据具体数据计算参数,而这一步主要基于训练数据完成(注意这一步还没有对数据进行实质的处理)
(3)bake: apply the blueprint to new data. 真正处理数据,得到clean train、test datasets
library(recipes)
# 将特征分为预测变量与目标变量两类
ames_recipe <- recipe(Sale_Price ~ ., data = ames_train)
# Recipe
#
# Inputs:
#
# role #variables
# outcome 1
# predictor 80
一、数据预处理的方式
Part 1、low quality data
低质量数据包括两方面(1)对于某些样本的某些指标没有收集到数据,即缺失值(NA
);(2)另一方面即使收集到数据,样本的某些特征信息的变异度很小,对建模的贡献很小,应该考虑予以筛除。
1.1 缺失值
缺失值可以分为两类:informative missingness 与 missingness at random
(1)informative missingness:缺失值本身也提供了某些信息
- 例如:房子的特征之一为后花园面积大小(numeric)/后花园种花的类型(categorical),而有的房子(样本)没有后花园,因此被直接记录为
NA
缺失值;但根据预测目的(预测房价)来说,将NA值替换为0
/None
可能更合适; - 因此对于这种缺失值,需要我们对于特征含义有清楚的理解,认识到缺失值可能本身就有很大的意义。
(2)missingness at random:就是因为收集过程的问题,没有采集到的数据,特征为随机分布;
- 最简单方法就是直接删除该样本的信息,但会丢失掉该样本的其它特征的数据,需要做好权衡;
- 另一种方法就是为该NA值计算模拟可能的值,主要根据所有样本该特征分布的特点或者最相似样本的该特征值,具体方法各有侧重;例如中位数、KNN、Tree-based
# 对 Gr_Liv_Area 特征列的缺失值用中位数模拟
ames_recipe %>%
step_medianimpute(Gr_Liv_Area)
# 所有特征变量的缺失值用KNN模拟
ames_recipe %>%
step_knnimpute(all_predictors(), neighbors = 6)
# 所有特征变量的缺失值用Tree-baesd 模拟
ames_recipe %>%
step_bagimpute(all_predictors())
-
由于这里的房价数据没有缺失值,但推荐使用后两种方法,如下图是对人为制作的缺失值的模拟结果:
1.2 low variance feature
-
举一个简单的例子:房子的特征之一:是不是给人住的(0/1),很显然类似特征提供的信息量是很小的。对于大量的non-informative feature,有些模型算法可能降低了模拟效率,也有可能降低模型精度。
image.png
-
目前认为如果特征列内容符合以下两个条件,那么就可以认为是一个low variance feature
(1)The fraction of unique values over the sample size is low (例如 ≤10%)
(2)The ratio of the frequency of the most prevalent value to the frequency of the second most prevalent value is large(例如 ≥ 20%)
例如一列的内容有1000个值,其中999个都是1,剩余一个为0;那么 index1 = 2/1000=0.002;index2 = (999/1000) / (1/1000) = 999,均满足上述条件,就可以认为是non-informative predictor
ames_recipe %>%
step_nzv(all_nominal())
# step_nzv() 默认参数 freq_cut(index1) = 95/5; unique_cut(index2) = 10
# Nominal variables include both character and factor.
Part 2、variable transform
2.1 Target engineering
- 针对有监督学习建模过程中的目标变量进行的处理;
- 适用于回归问题中对目标变量分布特征符合正态分布假设(线性回归),但实际为偏态分布(skewed,长尾)的数据。log转换是很有效的转换方式。

ames_recipe %>%
step_log(all_outcomes())
-
Box Cox transformation
相比直接log可以将输入数据灵活转为符合正太分布;Yeo-Johnson transformation
适合于目标数据存在负值,不能直接log的情况。
ames_recipe %>%
step_BoxCox(all_outcomes())
ames_recipe %>%
step_YeoJohnson(all_outcomes())

2.2 Feature engineering
(1)numeric feature
Normalization:类似上面1.1的log转换
- 虽然这种处理主要针对有参模型,调整原始数据中可能的偏态分布;但即使应用于无参模型,不会影响建模结果。Consequently, when in doubt, normalize.
recipe(Sale_Price ~ ., data = ames_train) %>%
step_YeoJohnson(all_numeric())
Standardization:将所有数值类variable的均值与变异度水平拉到统一水平(
zero mean and unit variance)

ames_recipe %>%
step_center(all_numeric(), -all_outcomes()) %>% #中心化 a mean of zero.
step_scale(all_numeric(), -all_outcomes()) #归一化 a standard deviation of one.
(2)categorical feature
A:合并频数很低的类别为一类
count(ames_train, Neighborhood) %>% arrange(n)
## # A tibble: 28 x 2
## Neighborhood n
## <fct> <int>
## 1 Landmark 1
## 2 Green_Hills 2
## 3 Greens 7
## 4 Blueste 9
## 5 Northpark_Villa 17
## 6 Briardale 18
## 7 Veenker 20
## 8 Bloomington_Heights 21
## 9 South_and_West_of_Iowa_State_University 30
## 10 Meadow_Village 30
## # … with 18 more rows
#将所有频率小于0.01的类别合并为other类
recipe(Sale_Price ~ ., data = ames_train) %>%
step_other(Neighborhood, threshold = 0.01,
other = "other")
B:将分类变量转为数值变量,有One-Hot Encoding与Dummy Encoding两种

recipe(Sale_Price ~ ., data = ames_train) %>%
step_dummy(all_nominal(), one_hot = TRUE)
# Nominal variables include both character and factor.
- 如上两种方法,适合于类别数不多的转换,否则会极大的增大维度;
- 如果分类变量有很多类,并且是有序的(ordered),那么可以按照下面的方法进行转换
C: Label encoding
count(ames_train, MS_SubClass) %>% head()
# # A tibble: 6 x 2
# MS_SubClass n
# <fct> <int>
# 1 One_Story_1946_and_Newer_All_Styles 756
# 2 One_Story_1945_and_Older 94
# 3 One_Story_with_Finished_Attic_All_Ages 4
# 4 One_and_Half_Story_Unfinished_All_Ages 13
# 5 One_and_Half_Story_Finished_All_Ages 203
# 6 Two_Story_1946_and_Newer 404
recipe(Sale_Price ~ ., data = ames_train) %>%
step_integer(MS_SubClass)
# 转换处理后的结果如下图所示,16个有序类别转换为1~16的数值,还是1列

Part 3、降维
- 降维是降低输入数据维度的有效方法,适用于数值型变量(Standardization)
- 在后续的机器学习算法学习过程中,会更加深入了解PCA
recipe(Sale_Price ~ ., data = ames_train) %>%
step_center(all_numeric()) %>%
step_scale(all_numeric()) %>%
step_pca(all_numeric(), threshold = .95)
# A fraction of the total variance that should be covered by the components.
二、方法整合
1、推荐处理顺序
- 虽然针对数据的不同,算法模型的不同,有不同的处理方法组合;但大体推荐的顺序如下:
(1) Filter out zero or near-zero variance features.
(2) Perform imputation if required.
(3) Normalize to resolve numeric feature skewness.
(4) Standardize (center and scale) numeric features.
(5) Perform dimension reduction (e.g., PCA) on numeric features.
(6) One-hot or dummy encode categorical features.
2、数据处理示例
#step1: 选择合适处理方式组合
blueprint <- recipe(Sale_Price ~ ., data = ames_train) %>%
step_nzv(all_nominal()) %>%
step_integer(matches("Qual|Cond|QC|Qu")) %>%
step_center(all_numeric(), -all_outcomes()) %>%
step_scale(all_numeric(), -all_outcomes()) %>%
step_pca(all_numeric(), -all_outcomes())
blueprint
#step2: train this blueprint on some training data
prepare <- prep(blueprint, training = ames_train)
prepare
#step3: apply our blueprint to new data(the training data or future test data)
baked_train <- bake(prepare, new_data = ames_train)
baked_test <- bake(prepare, new_data = ames_test)
baked_train
3、结合caret
包建模的使用方式
- KNN算法预测房价:涉及K值的确定
- 和
caret
包搭配使用时,只需要提供第一步结果就可以了。
blueprint <- recipe(Sale_Price ~ ., data = ames_train) %>%
step_nzv(all_nominal()) %>%
step_integer(matches("Qual|Cond|QC|Qu")) %>%
step_center(all_numeric(), -all_outcomes()) %>%
step_scale(all_numeric(), -all_outcomes()) %>%
step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE)
library(caret)
# Specify resampling plan
cv <- trainControl(
method = "repeatedcv",
number = 10,
repeats = 5
)
# Construct grid of hyperparameter values
hyper_grid <- expand.grid(k = seq(2, 25, by = 1))
# Tune a knn model using grid search
knn_fit2 <- train(
blueprint,
data = ames_train,
method = "knn",
trControl = cv,
tuneGrid = hyper_grid,
metric = "RMSE"
)
knn_fit2
# plot cross validation results
ggplot(knn_fit2)

突然联想到之前学习单细胞测序数据分析流程时,很多数据预处理方式与这里提到的知识点类似~
网友评论