【问题标题】:dplyr mutate new dynamic variables with case_whendplyr 使用 case_when 改变新的动态变量
【发布时间】:2018-08-02 02:02:41
【问题描述】:

我知道herehere 有类似的问题,但我无法为我的具体情况找出正确的解决方案。我发现的一些解决方案是使用mutate_ 等的解决方案,但我知道这些现在已经过时了。我是 dplyr 动态用法的新手。

我有一个数据框,其中包含一些具有两个不同前缀(alpha 和 beta)的变量:

df <- data.frame(alpha.num = c(1, 3, 5, 7),
             alpha.char = c("a", "c", "e", "g"),
             beta.num = c(2, 4, 6, 8),
             beta.char = c("b", "d", "f", "h"),
             which.to.use = c("alpha", "alpha", "beta", "beta"))

我想创建带有前缀“chosen”的新变量。它们是“alpha”或“beta”列的副本,具体取决于“which.to.use”列中为该行命名的名称。期望的输出是:

desired.df <- data.frame(alpha.num = c(1, 3, 5, 7),
                     alpha.char = c("a", "c", "e", "g"),
                     beta.num = c(2, 4, 6, 8),
                     beta.char = c("b", "d", "f", "h"),
                     which.to.use = c("alpha", "alpha", "beta", "beta"),
                     chosen.num = c(1, 3, 6, 8),
                     chosen.char = c("a", "c", "f", "h"))

我的失败尝试:

varnames <- c("num", "char")
df %<>%
  mutate(as.name(paste0("chosen.", varnames)) := case_when(
    which.to.use == "alpha" ~ paste0("alpha.", varnames),
    which.to.use == "beta" ~ pasteo("beta.", varnames)
  ))

我更喜欢纯 dplyr 解决方案,更好的是可以包含在修改 df 的更长管道中(即无需停止创建“varnames”)。感谢您的帮助。

【问题讨论】:

    标签: r dynamic dplyr


    【解决方案1】:

    使用一些有趣的rlang stuff & purrr

    library(rlang)
    library(purrr)
    library(dplyr)
    
    df <- data.frame(alpha.num = c(1, 3, 5, 7),
                     alpha.char = c("a", "c", "e", "g"),
                     beta.num = c(2, 4, 6, 8),
                     beta.char = c("b", "d", "f", "h"),
                     which.to.use = c("alpha", "alpha", "beta", "beta"),
                     stringsAsFactors = F)
    
    c("num", "char") %>% 
        map(~ mutate(df, !!sym(paste0("chosen.", .x)) := 
          case_when(
              which.to.use == "alpha" ~ !!sym(paste0("alpha.", .x)),
              which.to.use == "beta" ~ !!sym(paste0("beta.", .x))
                    ))) %>% 
        reduce(full_join)
    

    结果:

      alpha.num alpha.char beta.num beta.char which.to.use chosen.num chosen.char
    1         1          a        2         b        alpha          1           a
    2         3          c        4         d        alpha          3           c
    3         5          e        6         f         beta          6           f
    4         7          g        8         h         beta          8           h
    

    没有reduce(full_join)

    c("num", "char") %>% 
      map_dfc(~ mutate(df, !!sym(paste0("chosen.", .x)) := 
                     case_when(
                       which.to.use == "alpha" ~ !!sym(paste0("alpha.", .x)),
                       which.to.use == "beta" ~ !!sym(paste0("beta.", .x))
                     ))) %>% 
      select(-ends_with("1"))
    
    
    
    alpha.num alpha.char beta.num beta.char which.to.use chosen.num chosen.char
    1         1          a        2         b        alpha          1           a
    2         3          c        4         d        alpha          3           c
    3         5          e        6         f         beta          6           f
    4         7          g        8         h         beta          8           h
    

    说明:
    (注意:我没有完全甚至有点得到rlang。也许其他人可以给出更好的解释;)。)

    单独使用paste0 会产生一个字符串,当我们需要mutate 的裸名才能知道它指的是变量名时。

    如果我们将paste0 包装在sym 中,它的计算结果是一个裸名:

    > x <- varrnames[1]
    > sym(paste0("alpha.", x))
      alpha.num
    

    mutate 不知道评估,而是将其作为符号读取:

    > typeof(sym(paste0("alpha.", x)))
    [1] "symbol"
    

    “砰砰”!! 运算符计算 sym 函数。比较:

    > expr(mutate(df, var = sym(paste0("alpha.", x))))
    mutate(df, var = sym(paste0("alpha.", x)))
    
    > expr(mutate(df, var = !!sym(paste0("alpha.", x))))
    mutate(df, var = alpha.num)
    

    所以对于!!sym,我们可以使用 paste 来动态调用 dplyr 的变量名。

    【讨论】:

    • 谢谢。我的实际数据非常大(既高又宽),而 full_join 让事情停了下来……我想知道是否有办法使用 mutate_at 或 mutate_if 来做到这一点……
    • 查看我的nest()/map() 答案以获得更快的tidyverse 选项
    • @Nate 不错。使用map_dfc 可以让您摆脱full_join
    【解决方案2】:

    这是一个nest()/map() 策略,应该很快。它停留在tidyverse,但不会进入rlang 土地。

    library(tidyverse)
    
    df %>% 
        nest(-which.to.use) %>%
        mutate(new_data = map2(data, which.to.use,
                           ~ select(..1, matches(..2)) %>%
                               rename_all(funs(gsub(".*\\.", "choosen.", .) )))) %>%
        unnest()
    
      which.to.use alpha.num alpha.char beta.num beta.char choosen.num choosen.char
    1        alpha         1          a        2         b           1            a
    2        alpha         3          c        4         d           3            c
    3         beta         5          e        6         f           6            f
    4         beta         7          g        8         h           8            h
    

    它抓取所有列,而不仅仅是numchar,不是which.to.use。但这似乎是您(我)想要的 IRL。如果您只想提取特定变量,可以在调用 nest() 之前添加 select(matches('(var1|var2|etc')) 行。

    编辑: 我最初使用select() 删除不需要的列的建议将导致稍后执行join 将它们带回来。如果改为调整 nest 参数,则只能在某些列上实现。

    我在此处添加了新的 bool 列,但对于“选择”选择,它们将被忽略:

    new_df <- data.frame(alpha.num = c(1, 3, 5, 7),
                     alpha.char = c("a", "c", "e", "g"),
                     alpha.bool = FALSE,
                     beta.num = c(2, 4, 6, 8),
                     beta.char = c("b", "d", "f", "h"),
                     beta.bool = TRUE,
                     which.to.use = c("alpha", "alpha", "beta", "beta"),
                     stringsAsFactors = FALSE)
    
    new_df %>% 
        nest(matches("num|char")) %>% # only columns that match this pattern get nested, allows you to save others
        mutate(new_data = map2(data, which.to.use,
                               ~ select(..1, matches(..2)) %>%
                                   rename_all(funs(gsub(".*\\.", "choosen.", .) )))) %>%
        unnest()
    
      alpha.bool beta.bool which.to.use alpha.num alpha.char beta.num beta.char choosen.num choosen.char
    1      FALSE      TRUE        alpha         1          a        2         b           1            a
    2      FALSE      TRUE        alpha         3          c        4         d           3            c
    3      FALSE      TRUE         beta         5          e        6         f           6            f
    4      FALSE      TRUE         beta         7          g        8         h           8            h
    

    【讨论】:

    • 我只想对特定变量执行此操作。添加一个 select 语句会减少 df,所以我必须稍后再加入以恢复其他变量(如果我理解正确的话)。一定有更好的方法吧?
    • 我进行了编辑,以展示一种更好的方法,只需调整 nest() 参数,如果您有这样的想法,请告诉我
    • 太棒了!效果很好。我不太了解这里发生的一切,但这对我来说是功课。谢谢。
    【解决方案3】:

    使用applymargin = 1 的基本R 方法,其中我们根据which.to.use 列中的值为每一行选择列,并从该行的对应列中获取值。

    df[c("chosen.num", "chosen.char")] <- 
              t(apply(df, 1, function(x) x[grepl(x["which.to.use"], names(df))]))
    
    df
    #  alpha.num alpha.char beta.num beta.char which.to.use chosen.num chosen.char
    #1         1          a        2         b        alpha          1           a
    #2         3          c        4         d        alpha          3           c
    #3         5          e        6         f         beta          6           f
    #4         7          g        8         h         beta          8           h
    

    【讨论】:

      【解决方案4】:

      您也可以尝试gather/spread 方法

      df %>% 
        rownames_to_column() %>% 
        gather(k,v,-which.to.use,-rowname) %>% 
        separate(k,into = c("k1", "k2"), sep="[.]") %>% 
        filter(which.to.use == k1) %>% 
        mutate(k1="chosen") %>% 
        unite(k, k1, k2,sep=".") %>% 
        spread(k,v) %>%
        select(.,chosen.num, chosen.char) %>% 
        bind_cols(df, .)
          alpha.num alpha.char beta.num beta.char which.to.use chosen.num chosen.char
       1         1          a        2         b        alpha          1           a
       2         3          c        4         d        alpha          3           c
       3         5          e        6         f         beta          6           f
       4         7          g        8         h         beta          8           h
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-05-06
        • 2019-12-22
        • 2022-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-16
        相关资源
        最近更新 更多