【问题标题】:How can I use mutate() and case_when() in a for loop?如何在 for 循环中使用 mutate() 和 case_when()?
【发布时间】:2020-01-11 15:16:28
【问题描述】:

我正在编写一个闪亮的应用程序,用户将在其中输入样本条件的数据,脚本将“自动”将他们输入的条件与给定文件的样本名称相匹配。

为简单起见,我不会包含闪亮的代码,因为我只是在为实际的 R 实现而苦苦挣扎。

如果我已经知道潜在的条件是什么,我可以这样做:

library(tidyverse)
x <- data.frame(Samples = c('Low1', 'Low2', 'High1', 'High2', 
                           'Ctrl1', 'Ctrl2'))

x <- x %>% mutate(Conditions = case_when(
           str_detect(Samples, fixed("low", ignore_case = T)) ~ "low",
           str_detect(Samples, fixed("high", ignore_case = T)) ~ "high",
           str_detect(Samples, fixed("ctrl", ignore_case = T)) ~ "ctrl"))

我会得到我正在寻找的数据框,例如:

Samples    Conditions
   Low1           low
   Low2           low
  High1          high
  High2          high
  Ctrl1          ctrl
  Ctrl2          ctrl

但是,我想遍历潜在条件向量并执行以下操作:

library(tidyverse)
condition_options <- c('low', 'high', 'ctrl')

x <- data.frame(Samples = samplenames)
for (j in condition_options) {
   x <- x %>% mutate(Condition = case_when(
        str_detect(Samples, fixed(j, ignore_case = T)) ~ j)) 
    }

当我这样做时,Condition 列被重写,只给我匹配向量中的最后一个值。例如:

Samples    Conditions
   Low1         <NA>
   Low2         <NA>
  High1         <NA>
  High2         <NA>
  Ctrl1         ctrl
  Ctrl2         ctrl

【问题讨论】:

    标签: r tidyverse dplyr


    【解决方案1】:

    如果您使用元编程而不是循环来构建case_when 语句的所有部分,这可能会更容易。试试

    library(tidyverse)
    condition_options <- c('low', 'high', 'ctrl')
    
    conditions <- purrr::map(condition_options, 
                             ~quo(str_detect(Samples, fixed(!!.x, ignore_case = T))~!!.x))
    
    # check our work
    # cat(map_chr(conditions, quo_text), sep = "\n")
    # str_detect(Samples, fixed("low", ignore_case = T)) ~ "low"
    # str_detect(Samples, fixed("high", ignore_case = T)) ~ "high"
    # str_detect(Samples, fixed("ctrl", ignore_case = T)) ~ "ctrl"
    
    x <- data.frame(Samples = samplenames)
    x %>% mutate(Condition = case_when(!!!conditions) )
    
    #   Samples Condition
    # 1    Low1       low
    # 2    Low2       low
    # 3   High1      high
    # 4   High2      high
    # 5   Ctrl1      ctrl
    # 6   Ctrl2      ctrl
    

    map 在这里构建了您希望在 case_when 语句中拥有的所有不同公式。然后我们使用!!! 将它们插入到mutate 表达式中。

    【讨论】:

    • 这非常有效!我对元编程很陌生,所以我不得不查找 !!!!!... 我认为学习如何将它们合并到我的代码中是我的下一个 R“学习”。
    【解决方案2】:
    library(purrr)
    x <- data.frame(Samples = c('Low1', 'Low2', 'High1', 'High2', 
                                'Ctrl1', 'Ctrl2'))
    condition_options <- c('low', 'high', 'ctrl')
    
    # iterate through all provided `condition_options `, returns corresponding condition if a match is found, otherwise returns NA
    matched_values <- map(condition_options,function(condition_name){
        ifelse(
            str_detect(x$Samples,fixed(condition_name,ignore_case = TRUE)),
            condition_name,
            NA_character_
        )
    })
    
    # if all values are NA, still return NA, otherwise return matched value, it will throw an error if multiple matches are found.
    x["Conditions"] <- pmap_chr(values, function(...){
        values <- unlist(list(...))
        if(all(is.na(values))){
            return(NA)
        } else {
            return(values[!is.na(values)])
        }
    })
    
    
    > x
      Samples Conditions
    1    Low1        low
    2    Low2        low
    3   High1       high
    4   High2       high
    5   Ctrl1       ctrl
    6   Ctrl2       ctrl
    

    【讨论】:

      【解决方案3】:

      我认为您不需要循环来执行此操作。我们可以使用str_extract 提取任何与condition_options 中的模式匹配的值

      x$Conditions <- stringr::str_extract(tolower(x$Samples), 
                               paste0(condition_options, collapse = "|"))
      
      x
      #  Samples Conditions
      #1    Low1        low
      #2    Low2        low
      #3   High1       high
      #4   High2       high
      #5   Ctrl1       ctrl
      #6   Ctrl2       ctrl
      

      在base R中,我们也可以使用paste0动态生成正则表达式

      x$Conditions <- sub(paste0(".*(", paste0(condition_options, collapse = "|"), ").*"),
                      "\\1", tolower(x$Samples))
      

      在哪里

      paste0(".*(", paste0(condition_options, collapse = "|"), ").*") #gives
      #[1] ".*(low|high|ctrl).*"
      

      【讨论】:

        猜你喜欢
        • 2022-01-12
        • 2022-10-13
        • 2021-10-10
        • 1970-01-01
        • 1970-01-01
        • 2020-12-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多