【问题标题】:Condense multiple mutate functions into a single function将多个 mutate 函数压缩为一个函数
【发布时间】:2019-11-19 11:42:47
【问题描述】:

我在 R 中有一个表格和一个向量列表,如下所示:

> table
  ID value
1  1     B
2  2     D
3  3     H
4  4     A
5  5     F
> categories
$catA
[1] "A" "B" "C"

$catB
[1] "D" "E" "F"

$catC
[1] "G" "H" "A"

此时,我有一系列 mutate 函数来为每个类别添加一列,如果值在类别中,则为 TRUE,否则为 NA。

> table %>%
+      mutate(catA = if_else(value %in% categories$catA, T, NA)) %>%
+      mutate(catB = if_else(value %in% categories$catB, T, NA)) %>%
+      mutate(catC = if_else(value %in% categories$catC, T, NA))

  ID value catA catB catC
1  1     B TRUE   NA   NA
2  2     D   NA TRUE   NA
3  3     H   NA   NA TRUE
4  4     A TRUE   NA TRUE
5  5     F   NA TRUE   NA

但是,实际上我有更多类别,并且必须单独创建每个列并不理想。我试图将这些压缩为一个函数,但我正在努力遍历类别列表并适当地命名新列。我怀疑我需要使用 quosures,但是在阅读了 dplyr vignette 中的编程之后,我仍然在努力让它们工作。

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    这里的想法是将您的类别列表转换为 data.frame,然后执行简单的连接操作以将您的值表与其各自的类别合并。

    为了让它工作,我们从 3 个类别(“宽格式”)概括为一个长格式的 data.frame,其中 1 列用于类别,1 列用于其各自的值。为此,我们使用tidyrpivot_longer。在这种情况下,您的 3 个类别变成了一个有 9 行的 data.frame。

    当合并在一起时,我们可以使用pivot_wider 将其转回。

    library(dplyr)
    library(tidyr)
    
    table <- data.frame(ID=1:5, value=c('B','D','H','A','F'))
    categories <- list(catA=c('A','B','C'), catB=c('D','E','F'), catC=LETTERS[c(7,8,1)])
    
    bind_cols(categories) %>% 
      pivot_longer(cols=everything()) %>%
      right_join(table, by=c('value')) %>%
      pivot_wider(names_from=name, values_from=value)
    

    【讨论】:

    • 谢谢,但我对如何使用函数来缩短方法特别感兴趣。
    【解决方案2】:

    如果你设置了一个函数,最简单的方法是设计一个递归函数,像这样发生变异,我在每次调用函数时添加你的类别列表的第一个成员,它还没有在传递的 tibble 中,直到类别列表中没有不在您的列中的项目。请注意列名的 bang-bang (!!) 以及任何 := 的调用,这允许我们在 mutate 的左侧使用字符串。由于我们提取了用于命名类别的字符串,因此不需要引号(因为它已经被引用了)。

    recursive_add = function(df, cat_list){
      cat_list = cat_list[!names(cat_list) %in% names(df)]
    
      if (length(cat_list) == 0) {
        return(df)
      } else {
        cat_name = names(cat_list)[[1]]
        df %>% 
          mutate(
            !!cat_name := if_else(value %in% cat_list[[cat_name]], T, NA)
          ) %>% 
          recursive_add(., cat_list)
      }
    }
    
    table %>% 
      recursive_add(., categories)
    

    【讨论】:

    • 谢谢!我设法使用您的答案来修复我最初的尝试,使用 for 循环遍历列表中的每个项目,我认为这更容易理解,我自己将其添加为答案。如果没有你的回答,我是不会明白的!
    【解决方案3】:

    感谢@GenesRus 朝正确方向推动,我设法让我的原始功能正常工作。

    classify <- function(data, categories){
         for (i in 1:length(categories)){
              cat_name <- names(categories)[[i]]
              data <- mutate(data, !!cat_name := if_else(value %in% categories[[cat_name]], T, NA))
         }
         return(data)
    }
    

    运行 classify(table, categories) 然后提供我想要的输入,并使其易于添加到我的管道中。

    【讨论】:

      【解决方案4】:

      我不确定下面的代码是不是你想要的,它只在base R 中使用了sapply()match()

      r <- cbind(table,with(table,sapply(categories, function(v) match(value,v)>0)))
      

      屈服:

      > r
        ID value catA catB catC
      1  1     B TRUE   NA   NA
      2  2     D   NA TRUE   NA
      3  3     H   NA   NA TRUE
      4  4     A TRUE   NA TRUE
      5  5     F   NA TRUE   NA
      

      【讨论】:

        猜你喜欢
        • 2022-11-07
        • 1970-01-01
        • 2022-01-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-07-08
        相关资源
        最近更新 更多