library(tidyverse)
library(tidymodels)
tidymodels::tidymodels_prefer()
data(ames)
ames <- ames |>
mutate(Sale_Price = log10(Sale_Price))
set.seed(502)
ames_split <- initial_split(ames, prop = 0.80, strata = Sale_Price)
ames_train <- training(ames_split)
ames_test <- testing(ames_split)
lm_model <- linear_reg() |>
set_engine("lm")
lm_wflow <- workflow() |>
add_model(lm_model) |>
add_variables(outcomes = Sale_Price, predictors = c(Longitude, Latitude))
lm_fit <- fit(lm_wflow, ames_train)29 用recipe实现特征工程
本章介绍了如何使用recipe包来实现特征工程。特征工程是机器学习中非常重要的一步,它涉及到对原始数据进行处理和转换,以便更好地适应模型的需求。通过合理的特征工程,我们可以提高模型的性能和预测能力。关于recipe包的更多内容,请参考 章节 36 章节:嵌入法,以及 章节 34 , 章节 35,?sec-sec-missing-data。
一些常用的、可能的预处理步骤包括:
- 通过特征提取或删除一些变量来降低预测变量之间的相关性。
- 当一些预测变量存在缺失值时,使用一些特定的方法来进行插补。
- 针对一些需要使用方差的模型1,对一些偏态分布的预测变量进行转换使其呈现对称分布可能有助于模型的表现。
29.1 建立简单recipe对象
recipe包通过step_**()系列函数定义预处理步骤,这个步骤不会立即执行,只是声明了步骤的内容。
时刻注意,要在recpie外对结果列进行转换,尽量不要在配方中对结果列进行预处理
library(tidymodels)
tidymodels::tidymodels_prefer()
simple_ames <- recipe(
Sale_Price ~ Neighborhood + Gr_Liv_Area + Year_Built + Bldg_Type,
data = ames_train
) |>
step_log(Gr_Liv_Area, base = 10) |> # 对Gr_Liv_Area变量进行对数转换
step_dummy(all_nominal_predictors()) # 将所有名义变量(因子或字符)转换为定量格式
simple_ames使用recipe包进行预处理比直接使用公式或原始变量更有优势:
- 相关的预处理计算可以在不同模型间重复使用,因为预处理步骤和建模函数是分开的。
- 可以提供比公式丰富非常多的数据预处理方法。
- 语法更加紧凑,例如以上代码中使用的
all_nominal_predictors()。 - 所有的预处理步骤都可以储存在同一个的
recipe对象中。
29.2 使用recipe
将recipe对象加入workflow对象中进行使用。但要注意,同一时间只能使用同一个Preprocessor,当一个workflow中已经包含了Preprocessor,则需要先将其删除后再添加recipe。
lm_wflow <- lm_wflow |>
remove_variables() |>
add_recipe(simple_ames)
lm_wflow══ Workflow ════════════════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()
── Preprocessor ────────────────────────────────────────────────────────────────
2 Recipe Steps
• step_log()
• step_dummy()
── Model ───────────────────────────────────────────────────────────────────────
Linear Regression Model Specification (regression)
Computational engine: lm
接下来就可以使用一些列函数对workflow对象进行操作。
# A tibble: 3 × 1
.pred
<dbl>
1 5.08
2 5.32
3 5.28
# 获取拟合后的模型对象
lm_fit |>
extract_fit_parsnip() |>
tidy() |>
slice(1:5)# A tibble: 5 × 5
term estimate std.error statistic p.value
<chr> <dbl> <dbl> <dbl> <dbl>
1 (Intercept) -0.669 0.231 -2.90 3.80e- 3
2 Gr_Liv_Area 0.620 0.0143 43.2 2.63e-299
3 Year_Built 0.00200 0.000117 17.1 6.16e- 62
4 Neighborhood_College_Creek 0.0178 0.00819 2.17 3.02e- 2
5 Neighborhood_Old_Town -0.0330 0.00838 -3.93 8.66e- 5
# 获取拟合后的recipe对象
lm_fit |>
extract_recipe(estimated = TRUE)29.3 如何在workflow对象之外使用和调试recipe
- recipes 包可以脱离 tidymodels 的其他组件独立使用,专门用于数据预处理。
- 它的核心逻辑是:
library(recipes)
library(dplyr)
# 示例数据
set.seed(123)
df <- tibble(
y = rnorm(100),
x1 = rnorm(100, mean = 10, sd = 2),
x2 = rnorm(100, mean = 100, sd = 20),
x3 = sample(c("A", "B", "C"), 100, replace = TRUE),
x4 = rnorm(100)
) |>
mutate(x4 = ifelse(runif(100) < 0.1, NA, x4)) # 人为制造缺失值
# 1. 定义recipe
rec <- recipe(y ~ ., data = df) |>
step_impute_knn(all_predictors(), neighbors = 5) |> # KNN插补缺失值
step_normalize(all_numeric_predictors()) |> # 标准化数值变量
step_dummy(all_nominal_predictors()) # 编码分类变量
rec
# 2. 在训练数据上使用`prep()`
rec_prep <- prep(rec, training = df)
rec_prep
# 3. 查看学习到的参数
tidy(rec_prep)# A tibble: 3 × 6
number operation type trained skip id
<int> <chr> <chr> <lgl> <lgl> <chr>
1 1 step impute_knn TRUE FALSE impute_knn_rRRBu
2 2 step normalize TRUE FALSE normalize_Es22X
3 3 step dummy TRUE FALSE dummy_APj0i
Rows: 100
Columns: 6
$ x1 <dbl> -0.62344169, 0.37687234, -0.14389556, -0.24818937, -0.87288879, 0…
$ x2 <dbl> 2.188010597, 1.254841839, -0.405957160, 0.445034541, -0.563024425…
$ x4 <dbl> 1.50201503, -0.40962976, 2.35363219, -2.96308860, -0.97263894, 0.…
$ y <dbl> -0.56047565, -0.23017749, 1.55870831, 0.07050839, 0.12928774, 1.7…
$ x3_B <dbl> 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0,…
$ x3_C <dbl> 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0,…
例如方差分析,或使用方差作为特征重要性的模型。↩︎