使用 step_naomit 进行预测并使用 tidymodels 保留 ID
Predict with step_naomit and retain ID using tidymodels
在使用随机森林模型进行预测以合并回原始数据帧时,我试图在行中保留一个 ID。我在配方中使用 step_naomit,它在我烘焙训练数据时删除了缺少数据的行,但也删除了测试数据中缺少数据的记录。不幸的是,我没有 ID 无法轻松知道删除了哪些记录,因此我无法准确地合并回预测。
我曾尝试向原始数据中添加一个 ID 列,但 bake 会删除公式中未包含的任何变量(而且我不想在公式中包含 ID)。我还以为我可以保留原始 table 中的 row.names 进行合并,但看起来 row.name 在烘烤时也被重置了。
我意识到我可以在配方之前删除 NA 值来解决这个问题,但是配方中 step_naomit 的意义何在?我还在 step_naomit 中尝试了 skip=TRUE,但随后在拟合模型时出现缺少数据的错误(仅适用于随机森林)。我觉得我在 tidymodels 中遗漏了一些可以让我在烘焙前保留所有行的东西?
参见示例:
## R 3.6.1 ON WINDOWS 10 MACHINE
require(tidyverse)
require(tidymodels)
require(ranger)
set.seed(123)
temp <- iris %>%
dplyr::mutate(Petal.Width = case_when(
round(Sepal.Width) %% 2 == 0 ~ NA_real_, ## INTRODUCE NA VALUES
TRUE ~ Petal.Width))
mySplit <- rsample::initial_split(temp, prop = 0.8)
myRecipe <- function(dataFrame) {
recipes::recipe(Petal.Width ~ ., data = dataFrame) %>%
step_naomit(all_numeric()) %>%
prep(data = dataFrame)
}
myPred <- function(mySplit,myRecipe) {
train_set <- training(mySplit)
test_set <- testing(mySplit)
train_prep <- myRecipe(train_set)
analysis_processed <- bake(train_prep, new_data = train_set)
model <- rand_forest(
mode = "regression",
mtry = 3,
trees = 50) %>%
set_engine("ranger", importance = 'impurity') %>%
fit(Sepal.Width ~ ., data=analysis_processed)
test_processed <- bake(train_prep, new_data = test_set)
test_processed %>%
bind_cols(myPrediction = unlist(predict(model,new_data=test_processed)))
}
getPredictions <- myPred(mySplit,myRecipe)
nrow(getPredictions)
## 21 ROWS
max(as.numeric(row.names(getPredictions)))
## 21
nrow(testing(mySplit))
## 29 ROWS
max(as.numeric(row.names(testing(mySplit))))
## 150
为了能够跟踪删除了哪些观测值,我们需要为原始数据集提供一个 id
变量。
temp <- iris %>%
dplyr::mutate(Petal.Width = case_when(
round(Sepal.Width) %% 2 == 0 ~ NA_real_, ## INTRODUCE NA VALUES
TRUE ~ Petal.Width),
id = row_number()) #<<<<
然后我们使用 update_role()
首先将其指定为 "id variable",然后将其作为预测变量删除,这样它就不会成为建模过程的一部分。就是这样。其他一切都应该像以前一样工作。下面是完全更新的代码,其中包含 #<<<< 以表示我的更改。
require(tidyverse)
#> Loading required package: tidyverse
require(tidymodels)
#> Loading required package: tidymodels
#> Registered S3 method overwritten by 'xts':
#> method from
#> as.zoo.xts zoo
#> ── Attaching packages ───────────────────── tidymodels 0.0.3 ──
#> ✔ broom 0.5.2 ✔ recipes 0.1.7
#> ✔ dials 0.0.3 ✔ rsample 0.0.5
#> ✔ infer 0.5.0 ✔ yardstick 0.0.4
#> ✔ parsnip 0.0.4
#> ── Conflicts ──────────────────────── tidymodels_conflicts() ──
#> ✖ scales::discard() masks purrr::discard()
#> ✖ dplyr::filter() masks stats::filter()
#> ✖ recipes::fixed() masks stringr::fixed()
#> ✖ dplyr::lag() masks stats::lag()
#> ✖ dials::margin() masks ggplot2::margin()
#> ✖ dials::offset() masks stats::offset()
#> ✖ yardstick::spec() masks readr::spec()
#> ✖ recipes::step() masks stats::step()
require(ranger)
#> Loading required package: ranger
set.seed(1234)
temp <- iris %>%
dplyr::mutate(Petal.Width = case_when(
round(Sepal.Width) %% 2 == 0 ~ NA_real_, ## INTRODUCE NA VALUES
TRUE ~ Petal.Width),
id = row_number()) #<<<<
mySplit <- rsample::initial_split(temp, prop = 0.8)
myRecipe <- function(dataFrame) {
recipes::recipe(Petal.Width ~ ., data = dataFrame) %>%
update_role(id, new_role = "id variable") %>% #<<<<
update_role(-id, new_role = 'predictor') %>% #<<<<
step_naomit(all_numeric()) %>%
prep(data = dataFrame)
}
myPred <- function(mySplit,myRecipe) {
train_set <- training(mySplit)
test_set <- testing(mySplit)
train_prep <- myRecipe(train_set)
analysis_processed <- bake(train_prep, new_data = train_set)
model <- rand_forest(
mode = "regression",
mtry = 3,
trees = 50) %>%
set_engine("ranger", importance = 'impurity') %>%
fit(Sepal.Width ~ ., data=analysis_processed)
test_processed <- bake(train_prep, new_data = test_set)
test_processed %>%
bind_cols(myPrediction = unlist(predict(model,new_data=test_processed)))
}
getPredictions <- myPred(mySplit, myRecipe)
getPredictions
#> # A tibble: 23 x 7
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species id myPrediction
#> <dbl> <dbl> <dbl> <dbl> <fct> <int> <dbl>
#> 1 4.6 3.1 1.5 0.2 setosa 4 3.24
#> 2 4.3 3 1.1 0.1 setosa 14 3.04
#> 3 5.1 3.4 1.5 0.2 setosa 40 3.22
#> 4 5.9 3 4.2 1.5 versico… 62 2.98
#> 5 6.7 3.1 4.4 1.4 versico… 66 2.92
#> 6 6 2.9 4.5 1.5 versico… 79 3.03
#> 7 5.7 2.6 3.5 1 versico… 80 2.79
#> 8 6 2.7 5.1 1.6 versico… 84 3.12
#> 9 5.8 2.6 4 1.2 versico… 93 2.79
#> 10 6.2 2.9 4.3 1.3 versico… 98 2.88
#> # … with 13 more rows
# removed ids
setdiff(testing(mySplit)$id, getPredictions$id)
#> [1] 5 28 47 70 90 132
由 reprex package (v0.3.0)
于 2019-11-26 创建
在 step_naomit()
配方规范中使用 skip = TRUE
,然后将配方包含在 workflow
中可能是正确的解决方案。例如,
myRecipe <- recipe(Petal.Width ~ ., data = dataFrame) %>%
step_naomit(all_numeric(), step = FALSE)`
# don't include the prep()
wflow <- workflow() %>%
add_model(model) %>%
add_recipe(myRecipe)
wflow_fit <- wflow %>%
fit(train_set)
preds <- predict(wflow_fit, new_data = (test_set))
在使用随机森林模型进行预测以合并回原始数据帧时,我试图在行中保留一个 ID。我在配方中使用 step_naomit,它在我烘焙训练数据时删除了缺少数据的行,但也删除了测试数据中缺少数据的记录。不幸的是,我没有 ID 无法轻松知道删除了哪些记录,因此我无法准确地合并回预测。
我曾尝试向原始数据中添加一个 ID 列,但 bake 会删除公式中未包含的任何变量(而且我不想在公式中包含 ID)。我还以为我可以保留原始 table 中的 row.names 进行合并,但看起来 row.name 在烘烤时也被重置了。
我意识到我可以在配方之前删除 NA 值来解决这个问题,但是配方中 step_naomit 的意义何在?我还在 step_naomit 中尝试了 skip=TRUE,但随后在拟合模型时出现缺少数据的错误(仅适用于随机森林)。我觉得我在 tidymodels 中遗漏了一些可以让我在烘焙前保留所有行的东西?
参见示例:
## R 3.6.1 ON WINDOWS 10 MACHINE
require(tidyverse)
require(tidymodels)
require(ranger)
set.seed(123)
temp <- iris %>%
dplyr::mutate(Petal.Width = case_when(
round(Sepal.Width) %% 2 == 0 ~ NA_real_, ## INTRODUCE NA VALUES
TRUE ~ Petal.Width))
mySplit <- rsample::initial_split(temp, prop = 0.8)
myRecipe <- function(dataFrame) {
recipes::recipe(Petal.Width ~ ., data = dataFrame) %>%
step_naomit(all_numeric()) %>%
prep(data = dataFrame)
}
myPred <- function(mySplit,myRecipe) {
train_set <- training(mySplit)
test_set <- testing(mySplit)
train_prep <- myRecipe(train_set)
analysis_processed <- bake(train_prep, new_data = train_set)
model <- rand_forest(
mode = "regression",
mtry = 3,
trees = 50) %>%
set_engine("ranger", importance = 'impurity') %>%
fit(Sepal.Width ~ ., data=analysis_processed)
test_processed <- bake(train_prep, new_data = test_set)
test_processed %>%
bind_cols(myPrediction = unlist(predict(model,new_data=test_processed)))
}
getPredictions <- myPred(mySplit,myRecipe)
nrow(getPredictions)
## 21 ROWS
max(as.numeric(row.names(getPredictions)))
## 21
nrow(testing(mySplit))
## 29 ROWS
max(as.numeric(row.names(testing(mySplit))))
## 150
为了能够跟踪删除了哪些观测值,我们需要为原始数据集提供一个 id
变量。
temp <- iris %>%
dplyr::mutate(Petal.Width = case_when(
round(Sepal.Width) %% 2 == 0 ~ NA_real_, ## INTRODUCE NA VALUES
TRUE ~ Petal.Width),
id = row_number()) #<<<<
然后我们使用 update_role()
首先将其指定为 "id variable",然后将其作为预测变量删除,这样它就不会成为建模过程的一部分。就是这样。其他一切都应该像以前一样工作。下面是完全更新的代码,其中包含 #<<<< 以表示我的更改。
require(tidyverse)
#> Loading required package: tidyverse
require(tidymodels)
#> Loading required package: tidymodels
#> Registered S3 method overwritten by 'xts':
#> method from
#> as.zoo.xts zoo
#> ── Attaching packages ───────────────────── tidymodels 0.0.3 ──
#> ✔ broom 0.5.2 ✔ recipes 0.1.7
#> ✔ dials 0.0.3 ✔ rsample 0.0.5
#> ✔ infer 0.5.0 ✔ yardstick 0.0.4
#> ✔ parsnip 0.0.4
#> ── Conflicts ──────────────────────── tidymodels_conflicts() ──
#> ✖ scales::discard() masks purrr::discard()
#> ✖ dplyr::filter() masks stats::filter()
#> ✖ recipes::fixed() masks stringr::fixed()
#> ✖ dplyr::lag() masks stats::lag()
#> ✖ dials::margin() masks ggplot2::margin()
#> ✖ dials::offset() masks stats::offset()
#> ✖ yardstick::spec() masks readr::spec()
#> ✖ recipes::step() masks stats::step()
require(ranger)
#> Loading required package: ranger
set.seed(1234)
temp <- iris %>%
dplyr::mutate(Petal.Width = case_when(
round(Sepal.Width) %% 2 == 0 ~ NA_real_, ## INTRODUCE NA VALUES
TRUE ~ Petal.Width),
id = row_number()) #<<<<
mySplit <- rsample::initial_split(temp, prop = 0.8)
myRecipe <- function(dataFrame) {
recipes::recipe(Petal.Width ~ ., data = dataFrame) %>%
update_role(id, new_role = "id variable") %>% #<<<<
update_role(-id, new_role = 'predictor') %>% #<<<<
step_naomit(all_numeric()) %>%
prep(data = dataFrame)
}
myPred <- function(mySplit,myRecipe) {
train_set <- training(mySplit)
test_set <- testing(mySplit)
train_prep <- myRecipe(train_set)
analysis_processed <- bake(train_prep, new_data = train_set)
model <- rand_forest(
mode = "regression",
mtry = 3,
trees = 50) %>%
set_engine("ranger", importance = 'impurity') %>%
fit(Sepal.Width ~ ., data=analysis_processed)
test_processed <- bake(train_prep, new_data = test_set)
test_processed %>%
bind_cols(myPrediction = unlist(predict(model,new_data=test_processed)))
}
getPredictions <- myPred(mySplit, myRecipe)
getPredictions
#> # A tibble: 23 x 7
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species id myPrediction
#> <dbl> <dbl> <dbl> <dbl> <fct> <int> <dbl>
#> 1 4.6 3.1 1.5 0.2 setosa 4 3.24
#> 2 4.3 3 1.1 0.1 setosa 14 3.04
#> 3 5.1 3.4 1.5 0.2 setosa 40 3.22
#> 4 5.9 3 4.2 1.5 versico… 62 2.98
#> 5 6.7 3.1 4.4 1.4 versico… 66 2.92
#> 6 6 2.9 4.5 1.5 versico… 79 3.03
#> 7 5.7 2.6 3.5 1 versico… 80 2.79
#> 8 6 2.7 5.1 1.6 versico… 84 3.12
#> 9 5.8 2.6 4 1.2 versico… 93 2.79
#> 10 6.2 2.9 4.3 1.3 versico… 98 2.88
#> # … with 13 more rows
# removed ids
setdiff(testing(mySplit)$id, getPredictions$id)
#> [1] 5 28 47 70 90 132
由 reprex package (v0.3.0)
于 2019-11-26 创建在 step_naomit()
配方规范中使用 skip = TRUE
,然后将配方包含在 workflow
中可能是正确的解决方案。例如,
myRecipe <- recipe(Petal.Width ~ ., data = dataFrame) %>%
step_naomit(all_numeric(), step = FALSE)`
# don't include the prep()
wflow <- workflow() %>%
add_model(model) %>%
add_recipe(myRecipe)
wflow_fit <- wflow %>%
fit(train_set)
preds <- predict(wflow_fit, new_data = (test_set))