【问题标题】:Predict with step_naomit and retain ID using tidymodels使用 step_naomit 进行预测并使用 tidymodels 保留 ID
【发布时间】:2020-03-21 09:58:43
【问题描述】:

在使用随机森林模型进行预测以合并回原始数据帧时,我试图在行上保留一个 ID。我在配方中使用 step_naomit,它在我烘焙训练数据时删除了缺少数据的行,但也删除了测试数据中缺少数据的记录。不幸的是,我没有 ID 可以轻松知道哪些记录被删除,因此我可以准确地合并回预测。

我试图在原始数据中添加一个 ID 列,但烘焙会删除公式中未包含的任何变量(并且我不想在公式中包含 ID)。我还认为我可以保留原始表中的 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 列,但是烘焙会删除公式中未包含的任何变量(并且我不想在公式中包含 ID)。”你检查recipe函数中的roles参数了吗?您可以使用该参数来指定配方中每个变量的作用(即 ID、权重、预测变量或响应)。更多详情:tidymodels.github.io/recipes/articles/Roles.html

标签: r random-forest predict recipe tidymodels


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    为了能够跟踪删除了哪些观察,我们需要为原始数据集提供一个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 变量”,然后将其作为预测变量移除,这样它就不会成为建模过程的一部分。就是这样。其他一切都应该像以前一样工作。下面是完全更新的代码,#

    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 日创建

    【讨论】:

    • 谢谢你,埃米尔。你能够回答我的问题。但是,我认为您可能想要更新您的答案以删除适合的 ID 变量。在我自己的代码上运行后,我意识到 id 变量仍然被合并到模型拟合中,并且在运行 model$fit$variable.importance 时显示为最重要的变量。
    猜你喜欢
    • 2021-06-12
    • 2013-01-23
    • 2016-11-25
    • 2015-09-17
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多