【问题标题】:one-hot encoding in R without using mltools, caret [duplicate]R中的one-hot编码而不使用mltools,插入符号[重复]
【发布时间】:2021-08-31 16:07:40
【问题描述】:

我想通过 tidyverse 在 R 中进行 one-hot 编码,而不是使用 caret、mltools 等包。

## Load vcd package
library(vcd)

## Load Arthritis dataset (data frame)
data(Arthritis)


Arthritis[1:5, ][2:5]

  Treatment  Sex Age Improved
1   Treated Male  27     Some
2   Treated Male  29     None
3   Treated Male  30     None
4   Treated Male  32   Marked
5   Treated Male  46   Marked

在 tidyverse 中是否有一种简单的方法可以做到这一点,我为每个分类列保留 n-1 个值?例如,性别在这个数据集中是二进制的,所以我只需要一个用于男性或女性的单热编码列。年龄特征将被忽略。

【问题讨论】:

  • 我不完全理解您所说的“保留 n-1 个值” 是什么意思。您介意明确并显示我们可以提出的任何代码的预期输出吗?谢谢!
  • 例如,Sex 有两个值(男性和女性),我只想将其编码为 1 列(n - 1 = 2 -1 = 1),例如可以将其命名为 Sex_Male。
  • 你说的是添加虚拟变量吗?
  • 是的,本质上是虚拟变量,很抱歉造成混淆
  • 我将此标记为骗子(为了使第一个其他问题保持在顶部),但这仍然是一个好问题。随意接受其中一个答案。

标签: r machine-learning


【解决方案1】:

对于您的具体示例,您可以这样做:

library(dplyr)

Arthritis |> 
  as_tibble() |> # not necessary, just using it for output readability
  mutate(sex_male = as.numeric(Sex) - 1)
#> # A tibble: 84 × 6
#>       ID Treatment Sex     Age Improved sex_male
#>    <int> <fct>     <fct> <int> <ord>       <dbl>
#>  1    57 Treated   Male     27 Some            1
#>  2    46 Treated   Male     29 None            1
#>  3    77 Treated   Male     30 None            1
#>  4    17 Treated   Male     32 Marked          1
#>  5    36 Treated   Male     46 Marked          1
#>  6    23 Treated   Male     58 Marked          1
#>  7    75 Treated   Male     59 None            1
#>  8    39 Treated   Male     59 Marked          1
#>  9    33 Treated   Male     63 None            1
#> 10    55 Treated   Male     63 None            1
#> # … with 74 more rows

这仅适用于Sex 是具有两个级别/不同值的factor 变量。除非您愿意使用包中的函数,否则更复杂的变量将需要更多关注。

您要求tidyverse 解决方案。 recipes 包是 tidymodels 的一部分。

library(recipes)

Arthritis |> 
  recipe(Improved ~ .) |> 
  step_dummy(Sex, Treatment) |> 
  prep() |> 
  bake(Arthritis)
#> # A tibble: 84 × 5
#>       ID   Age Improved Sex_Male Treatment_Treated
#>    <int> <int> <ord>       <dbl>             <dbl>
#>  1    57    27 Some            1                 1
#>  2    46    29 None            1                 1
#>  3    77    30 None            1                 1
#>  4    17    32 Marked          1                 1
#>  5    36    46 Marked          1                 1
#>  6    23    58 Marked          1                 1
#>  7    75    59 None            1                 1
#>  8    39    59 Marked          1                 1
#>  9    33    63 None            1                 1
#> 10    55    63 None            1                 1
#> # … with 74 more rows

【讨论】:

  • 哇,一个新的管道用户!
【解决方案2】:

我同意直到 recipes 是去这里的方式。但是如果你想要一个完全来自 tidyverse 的解决方案,你可以这样做:

library(vcd)
library(tidyverse)


Arthritis %>%
  as_tibble() %>%
  mutate(d = map_dfc(unique(Improved) %>%
                        set_names(.),
                      ~ Improved == .x
                      ) %>% 
           .[-1]
         )
#> # A tibble: 84 × 6
#>       ID Treatment Sex     Age Improved d$None $Marked
#>    <int> <fct>     <fct> <int> <ord>    <lgl>  <lgl>  
#>  1    57 Treated   Male     27 Some     FALSE  FALSE  
#>  2    46 Treated   Male     29 None     TRUE   FALSE  
#>  3    77 Treated   Male     30 None     TRUE   FALSE  
#>  4    17 Treated   Male     32 Marked   FALSE  TRUE   
#>  5    36 Treated   Male     46 Marked   FALSE  TRUE   
#>  6    23 Treated   Male     58 Marked   FALSE  TRUE   
#>  7    75 Treated   Male     59 None     TRUE   FALSE  
#>  8    39 Treated   Male     59 Marked   FALSE  TRUE   
#>  9    33 Treated   Male     63 None     TRUE   FALSE  
#> 10    55 Treated   Male     63 None     TRUE   FALSE  
#> # … with 74 more rows

【讨论】:

    【解决方案3】:

    您可以为此使用pivot_longer 和pivot_wider 的组合。

    Arthritis %>%
      as_tibble() %>% # not neccessary, for better viewing
      mutate(across(everything(), as.character)) %>% 
      pivot_longer(c(Sex, Treatment, Improved), names_to = 'variable', values_to = 'value') %>% # specify the columns to encode here
      mutate(ind = 1) %>%
      unite(col_name, variable, value) %>%
      pivot_wider(values_from = ind, names_from = col_name, values_fill = 0)
    

    对于 n-1,一旦数据为长格式,您可以过滤掉其中一个值

    long_format <- Arthritis %>%
      as_tibble() %>%
      mutate(across(everything(), as.character)) %>%
      pivot_longer(c(Sex, Treatment, Improved), names_to = 'variable', values_to = 'value') %>%
      mutate(ind = 1)
    
    # for the n-1
    values_to_keep <- long_format %>%
      count(variable, value) %>%
      group_by(variable) %>%
      slice(-1) %>%
      pull(value)
    
    long_format %>%
      filter(value %in% values_to_keep) %>%
      unite(col_name, variable, value) %>%
      pivot_wider(values_from = ind, names_from = col_name, values_fill = 0)
    
    # A tibble: 78 x 6
       ID    Age   Sex_Male Treatment_Treated Improved_Some Improved_None
       <chr> <chr>    <dbl>             <dbl>         <dbl>         <dbl>
     1 57    27           1                 1             1             0
     2 46    29           1                 1             0             1
     3 77    30           1                 1             0             1
     4 17    32           1                 1             0             0
     5 36    46           1                 1             0             0
     6 23    58           1                 1             0             0
     7 75    59           1                 1             0             1
     8 39    59           1                 1             0             0
     9 33    63           1                 1             0             1
    10 55    63           1                 1             0             1
    

    【讨论】:

      【解决方案4】:

      您也许可以只使用 model.matrix 来完成此操作。我对您的示例数据进行了一些更改,以确保所有人都有 2 个或更多级别:

      dat <- structure(list(Treatment = c("Treated", "Treated", "UnTreated", "Treated", "Treated"), Sex = c("Male", "Male", "Male", "FeMale", "Male"), Age = c(27L, 29L, 30L, 32L, 46L), Improved = c("Some", "None", "None", "Marked", "Marked")), class = "data.frame", row.names = c("1", "2", "3", "4", "5"))
      dat
      #   Treatment    Sex Age Improved
      # 1   Treated   Male  27     Some
      # 2   Treated   Male  29     None
      # 3 UnTreated   Male  30     None
      # 4   Treated FeMale  32   Marked
      # 5   Treated   Male  46   Marked
      

      从那里,

      isnum <- sapply(dat, is.numeric)
      iscat <- !isnum & lengths(lapply(dat, unique)) > 1
      paste("~ 0 +", paste(names(dat)[iscat], collapse = " + "))
      # [1] "~ 0 + Treatment + Sex + Improved"
      cbind(dat[, !iscat, drop=FALSE],
            model.matrix(formula(paste("~ 0 +", paste(names(dat)[iscat], collapse = " + "))), data = dat))
      #   Age TreatmentTreated TreatmentUnTreated SexMale ImprovedNone ImprovedSome
      # 1  27                1                  0       1            0            1
      # 2  29                1                  0       1            1            0
      # 3  30                0                  1       1            1            0
      # 4  32                1                  0       0            0            0
      # 5  46                1                  0       1            0            0
      

      【讨论】:

        猜你喜欢
        • 2019-06-28
        • 1970-01-01
        • 2020-07-18
        • 2020-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-06
        相关资源
        最近更新 更多