【问题标题】:Transforming dummy variables to single column in R将虚拟变量转换为 R 中的单列
【发布时间】:2022-01-05 04:48:36
【问题描述】:

我在 R 中有下表,其中列出了人的种族、性别、年龄和胆固醇测试。年龄和胆固醇测试显示为虚拟变量。年龄可分为低、中或高,而胆固醇测试可分为低或高。我想将年龄和胆固醇列转换为单列,其中低被归类为 1,中被归类为 2,高被归类为 3。如果一个人从未服用过胆固醇测试,胆固醇测试可能会接近低或高,应该是预期输出中的 N/A。 我希望解决方案是动态的,这样如果我有多个这种格式的列,代码仍然可以工作(即可能有一些新的测试,可以分为高、低或中作为虚拟变量)。

如何在 R 中做到这一点?

输入:

  race  gender age.low_tm1 age.medium_tm1 age.high_tm1 chol_test.low_tm1 chol_test.high_tm1
  <chr>  <int>       <int>          <int>        <int>             <int>              <int>
1 white      0           1              0            0                 0                  0
2 white      0           1              0            0                 0                  0
3 white      1           1              0            0                 0                  0
4 black      1           0              1            0                 0                  0
5 white      0           0              0            1                 0                  1
6 black      0           0              1            0                 1                  0

预期输出:

  race  gender   age  chol_test
1 white      0     1        n/a  
2 white      0     1        n/a
3 white      1     1        n/a
4 black      1     2        n/a
5 white      0     3          3
6 black      0     2          1

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    也许这有帮助

    library(dplyr)
    library(tidyr)
    library(stringr)
    df1 %>% 
       mutate(across(contains("_"),  ~  
       . * setNames(1:3, c("low", "medium", "high"))[
         str_extract(cur_column(), "low|medium|high")]))   %>%    
      rename_with(~ str_remove(., "_tm1")) %>% 
      pivot_longer(cols = -c(race, gender), 
        names_to = c(".value", "categ"), names_sep = "\\.") %>% 
      filter(age > 0|chol_test > 0) %>% 
      select(-categ) %>% 
      mutate(chol_test = na_if(chol_test, 0))
    

    -输出

    # A tibble: 7 × 4
      race  gender   age chol_test
      <chr>  <int> <int>     <int>
    1 white      0     1        NA
    2 white      0     1        NA
    3 white      1     1        NA
    4 black      1     2        NA
    5 white      0     3         3
    6 black      0     0         1
    7 black      0     2        NA
    

    数据

    df1 <- structure(list(race = c("white", "white", "white", "black", "white", 
    "black"), gender = c(0L, 0L, 1L, 1L, 0L, 0L), age.low_tm1 = c(1L, 
    1L, 1L, 0L, 0L, 0L), age.medium_tm1 = c(0L, 0L, 0L, 1L, 0L, 1L
    ), age.high_tm1 = c(0L, 0L, 0L, 0L, 1L, 0L), chol_test.low_tm1 = c(0L, 
    0L, 0L, 0L, 0L, 1L), chol_test.high_tm1 = c(0L, 0L, 0L, 0L, 1L, 
    0L)), class = "data.frame", row.names = c("1", "2", "3", "4", 
    "5", "6"))
    

    【讨论】:

    • 我不太了解这个解决方案。这并没有给我预期的输出,因为我得到了比预期更多的行
    • 第 5 列在预期输出中将 chol_test 显示为 3,因为在输入中 chol_test.high_tm1 为 1,表示它为高。预期的输出只是我的数据框的前几条记录。正如我在问题中所说,我想将“高”映射到 3。
    • 您在这个解决方案中的行数是否超过 6 行?
    【解决方案2】:

    我们可以首先定义一个自定义函数,允许我们根据变量名称重新编码虚拟变量,下面称为var_nm2value

    此函数将变量的值作为x 参数。在dplyr::across 中,这是.x 部分。它采用名称-值对列表作为value_ls 参数。该函数只是遍历名称-值对列表,检查是否在变量名称中找到value_ls 中的名称。为此,它在dplyr::cur_column() 上使用grepl。如果我们有匹配项,那么我们将所有1s 替换为来自value_ls 的值,并返回所有其他值,即零。

    然后我们可以在recode_ls下面定义一个重新编码值列表。

    最后,我们在dplyr::summarise 中使用purrr::map_dfc,其中我们使用要创建的变量字符串"age""chol_test",然后ii) select 仅包含该字符串的列,并且在每次迭代中我们 iii) 应用 dplyr::across 重新编码值,iv) 将结果通过管道传输到 do.call 以获取 max,最后 v) 将 0s 重新编码为 NA

    # custom function to recode  0/1 dummy variables based on their variable name an 
    var_nm2value <- function(x, values_ls) {
      for (val in seq_along(values_ls)) {
        if(grepl(names(values_ls)[val], dplyr::cur_column())) {
          return(ifelse(x == 1L, values_ls[[val]], x))
        } 
      }
    }
    
    # define list of recode values
    recode_ls <- list(low = 1, medium = 2, high = 3)
    
    library(tidyverse)
    
    # apply functions to data.frame
    df1 %>% 
      summarise(race = race,
                gender = gender,
                map_dfc(set_names(c("age", "chol_test")), # i)
                        function(x) { 
                          select(., contains(x)) %>% # ii)
                            summarise("{x}" := across(everything(), var_nm2value, recode_ls) %>% # iii)
                                        do.call("pmax", .) %>% # iv) 
                                        ifelse(. == 0, NA, .))} # v)
                )) 
    
    #>    race gender age chol_test
    #> 1 white      0   1        NA
    #> 2 white      0   1        NA
    #> 3 white      1   1        NA
    #> 4 black      1   2        NA
    #> 5 white      0   3         3
    #> 6 black      0   2         1
    

    reprex package (v0.3.0) 于 2022-01-03 创建

    【讨论】:

    • 你能解释一下你的自定义函数到底是做什么的吗?例如什么是 x 参数,什么是 values_ls 参数?
    • @Eisen:我添加了关于重新编码功能的更多说明。
    猜你喜欢
    • 2016-03-07
    • 1970-01-01
    • 2020-11-28
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 2020-08-02
    • 2018-03-11
    • 2021-01-01
    相关资源
    最近更新 更多