【问题标题】:Use 'case_when' to assign several new variables at once for a given condition使用 \'case_when\' 为给定条件一次分配多个新变量
【发布时间】:2023-01-11 00:56:53
【问题描述】:

我有一个带有两个变量的数据框dfdf$soildf$use。我想根据条件向我的数据集添加两个新变量 df$ef1df$ef2。我正在使用“case_when”执行此操作:

ef1_grassl_mineral <- 0.2
ef1_grassl_peat    <- 0.3
ef1_arable_mineral <- 0.4
ef1_arable_peat    <- 0.5

ef2_grassl_mineral <- 2.3
ef2_grassl_peat    <- 3.4
ef2_arable_mineral <- 4.5
ef2_arable_peat    <- 5.6


df <- data.frame(soil = c('mineral', 'peat', 'mineral', 'peat'),
                 use  = c('grassl', 'arable', 'arable', 'grassl'))

df <- df %>% mutate (
  ef1 = case_when((soil=='mineral' & use=='grassl') ~ ef1_grassl_mineral,
                  (soil=='peat'    & use=='grassl') ~ ef1_grassl_peat,
                  (soil=='mineral' & use=='arable') ~ ef1_arable_mineral,
                  (soil=='peat'    & use=='arable') ~ ef1_arable_peat),
  ef2 = case_when((soil=='mineral' & use=='grassl') ~ ef2_grassl_mineral,
                  (soil=='peat'    & use=='grassl') ~ ef2_grassl_peat,
                  (soil=='mineral' & use=='arable') ~ ef2_arable_mineral,
                  (soil=='peat'    & use=='arable') ~ ef2_arable_peat))

以上工作正常,但我必须为每个变量重复条件,这使得代码冗长。 因此,我想知道是否有一种方法可以仅指定一次条件(例如,soil=='mineral' &amp; use=='arable'),然后同时定义df$ef1df$ef2。(语法:IF (soil=='mineral' & use=='可耕地') 然后 ef1=ef1_arable_mineral 和 ef2=ef2_arable_mineral )

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    改为使用查找表和连接

    lookup = tribble(
      ~soil, ~use, ~ef1, ~ef2,
      "mineral", "grassl", 0.2, 2.3,
      "peat", "grassl", 0.3, 3.4,
      "mineral", "arable", 0.4, 4.5,
      "peat", "arable", 0.5, 5.6
    )
    

    然后,如果您有更大的数据框,则需要在现有的soiluse 列的基础上添加ef1ef2 列,您可以执行bigger_data %&gt;% left_join(lookup, by = c("soil", "use"))

    我最喜欢这样的查找表的特点是它们很容易审计/调试。如果其他人需要检查值,您可以将查找表存储为平面文件(CSV 或类似文件),即使对非技术人员来说也很清楚。

    【讨论】:

    • 我肯定会推荐一个查找表作为我的好习惯!
    【解决方案2】:

    您可以使用list() 存储多列的值,然后传递给tidyr::unnest_wider()

    library(tidyverse)
    
    df %>%
      mutate(ef = case_when(
        (soil == 'mineral' & use == 'grassl') ~ list(c(0.2, 2.3)),
        (soil == 'peat'    & use == 'grassl') ~ list(c(0.3, 3.4)),
        (soil == 'mineral' & use == 'arable') ~ list(c(0.4, 4.5)),
        (soil == 'peat'    & use == 'arable') ~ list(c(0.5, 5.6)))
      ) %>%
      unnest_wider(ef, names_sep = '')
    
    # # A tibble: 4 × 4
    #   soil    use      ef1   ef2
    #   <chr>   <chr>  <dbl> <dbl>
    # 1 mineral grassl   0.2   2.3
    # 2 peat    arable   0.5   5.6
    # 3 mineral arable   0.4   4.5
    # 4 peat    grassl   0.3   3.4
    

    【讨论】:

      【解决方案3】:

      可能不是最简洁的解决方案,但另一种有趣的方法是将所有 ef1ef2 查找值放在一个列表中,并通过连接您的列来调用它们:

      library(tidyverse)
      
      ef1 <- ef2 <- list()
      
      ef1$grassl_mineral <- 0.2
      ef1$grassl_peat    <- 0.3
      ef1$arable_mineral <- 0.4
      ef1$arable_peat    <- 0.5
      
      ef2$grassl_mineral <- 2.3
      ef2$grassl_peat    <- 3.4
      ef2$arable_mineral <- 4.5
      ef2$arable_peat    <- 5.6
      
      
      df <- data.frame(soil = c('mineral', 'peat', 'mineral', 'peat'),
                       use  = c('grassl', 'arable', 'arable', 'grassl'))
      
      df |> 
        mutate(ef1 = ef1[paste(use, soil, sep = "_")],
               ef2 = ef2[paste(use, soil, sep = "_")])
      #      soil    use ef1 ef2
      # 1 mineral grassl 0.2 2.3
      # 2    peat arable 0.5 5.6
      # 3 mineral arable 0.4 4.5
      # 4    peat grassl 0.3 3.4
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-27
        • 2017-06-28
        • 2018-10-18
        • 1970-01-01
        • 2011-07-13
        • 1970-01-01
        相关资源
        最近更新 更多