如何将tidyverse链式处理转换为recipes包的预处理步骤?
在recipes建模流程中结合tidyverse分组操作
背景
我正在阅读Bradley Boehmke与Brandon Greenwell所著的《Hands-On Machine Learning with R》,书中推荐用recipes包搭建预处理流程。但我习惯先用tidyverse做数据探索(比如分组、过滤),而recipes的step_mutate不支持分组操作,想知道如何在建模流程中同时结合两者,这里以Kaggle的Spaceship Titanic数据集为例。
示例数据集
train <- structure(list(PassengerId = c("0001_01", "0002_01", "0003_01", "0003_02", "0004_01", "0005_01"), HomePlanet = c("Europa", "Earth", "Europa", "Europa", "Earth", "Earth"), CryoSleep = c("False", "False", "False", "False", "False", "False"), Cabin = c("B/0/P", "F/0/S", "A/0/S", "A/0/S", "F/1/S", "F/0/P"), Destination = c("TRAPPIST-1e", "TRAPPIST-1e", "TRAPPIST-1e", "TRAPPIST-1e", "TRAPPIST-1e", "PSO J318.5-22" ), Age = c(39, 24, 58, 33, 16, 44), VIP = c("False", "False", "True", "False", "False", "False"), RoomService = c(0, 109, 43, 0, 303, 0), FoodCourt = c(0, 9, 3576, 1283, 70, 483), ShoppingMall = c(0, 25, 0, 371, 151, 0), Spa = c(0, 549, 6715, 3329, 565, 291), VRDeck = c(0, 44, 49, 193, 2, 0), Name = c("Maham Ofracculy", "Juanna Vines", "Altark Susent", "Solam Susent", "Willy Santantines", "Sandie Hinetthews" ), Transported = c("False", "True", "False", "False", "True", "True")), row.names = c(NA, 6L), class = "data.frame")
已有的tidyverse处理逻辑
我用dplyr实现了提取乘客组并计算组内人数的操作:
library(tidyverse) library(recipes) train2 <- train %>% mutate(PassengerGroup = as.numeric(str_sub(PassengerId, 1, 4))) %>% group_by(PassengerGroup) %>% add_count(PassengerGroup, name = "PassengerGroupSize") %>% ungroup()
recipes中遇到的局限
目前在recipes里只能完成第一步的变量提取,无法实现分组计数:
my_recipe <- recipe(Transported ~ ., data = train) %>% step_mutate( PassengerGroup = as.numeric(str_sub(PassengerId, 1, 4)) )
可行方案
方案1:利用dplyr的.by参数直接实现分组计算
从dplyr 1.1.0版本开始,mutate支持.by参数,可直接指定分组变量,无需手动执行group_by/ungroup,这一特性可以直接在step_mutate中使用:
my_recipe <- recipe(Transported ~ ., data = train) %>% step_mutate( # 提取乘客组ID PassengerGroup = as.numeric(str_sub(PassengerId, 1, 4)), # 按乘客组分组计算组内人数 PassengerGroupSize = n(), .by = PassengerGroup ) # 验证处理结果 prepped_recipe <- prep(my_recipe) baked_data <- bake(prepped_recipe, new_data = train)
处理后的baked_data与tidyverse链式处理的train2结果完全一致。
方案2:自定义recipes步骤封装复杂分组逻辑
如果需要处理更复杂的分组操作(比如嵌套变换、多步骤分组计算),可以自定义一个recipes步骤来封装整个tidyverse流程:
1. 定义数据处理函数
custom_group_process <- function(data) { data %>% mutate(PassengerGroup = as.numeric(str_sub(PassengerId, 1, 4))) %>% group_by(PassengerGroup) %>% add_count(name = "PassengerGroupSize") %>% ungroup() }
2. 创建并使用自定义步骤
# 注册自定义步骤 step_custom_group <- function(recipe, ...) { step( recipe, operation = function(x, ...) custom_group_process(x), trained = FALSE, inputs = NULL, ... ) } # 在recipe中调用自定义步骤 my_recipe <- recipe(Transported ~ ., data = train) %>% step_custom_group() # 验证结果 prepped_recipe <- prep(my_recipe) baked_data <- bake(prepped_recipe, new_data = train)
这种方式可以灵活整合任意复杂的tidyverse数据处理逻辑到recipes流程中。
内容的提问来源于stack exchange,提问作者GreenManXY
相关产品推荐
相关产品推荐

