【问题标题】:mutate(foo = bar + bla) but sometimes bar or bla don't exist which results in error messagemutate(foo = bar + bla) 但有时 bar 或 bla 不存在导致错误消息
【发布时间】:2021-09-03 21:46:52
【问题描述】:

我有一个通常如下所示的数据框:

structure(list(date = structure(18780, class = "Date"), bar = 1L, 
    Sessions = 2990L, `bla` = 20L), row.names = c(NA, 
-1L), class = c("tbl_df", "tbl", "data.frame"))

看起来像:

    # A tibble: 1 x 4
  date         bar Sessions   bla
  <date>     <int>    <int> <int>
1 2021-06-02     1     2990    20

有了这个我变异了:

mydf %>% mutate(foo = bar + bla)
# A tibble: 1 x 5
  date         bar Sessions   bla   foo
  <date>     <int>    <int> <int> <int>
1 2021-06-02     1     2990    20    21

但是,这是在带有用户过滤器的闪亮应用的上下文中。有时,用户输入后生成的数据框有一个没有 bar 或 bla 字段的数据框。因此,当我在 mutate 期间添加它们时,我得到了

Error: Problem with `mutate()` input `foo`.
x object 'bar' not found
ℹ Input `foo` is ``bar` + `bla``

在 bar 或 bla 不存在的情况下,我仍然希望我的 mutate 中剩余的总和。否则只需创建新功能 foo 但将其值设为 0。在 R 英语中类似于:

mydf %>% mutate(foo = if(bar exists then bar else 0) + if(bla exists then bla else 0))

有没有一种“不错”或优雅的方式来做到这一点?也许是 tidyverse 方法?

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    也许,这可能有助于使用any_of -

    library(dplyr)
    cols <- c('bar', 'bla')
    
    df %>%  mutate(foo = rowSums(select(., any_of(cols))))
    
    # A tibble: 1 x 5
    #  date         bar Sessions   bla   foo
    #  <date>     <int>    <int> <int> <dbl>
    #1 2021-06-02     1     2990    20    21
    

    如果bar 不存在,这仍然有效 -

    df %>%  
      select(-bar) %>%
      mutate(foo = rowSums(select(., any_of(cols))))
    
    #  date       Sessions   bla   foo
    #  <date>        <int> <int> <dbl>
    #1 2021-06-02     2990    20    20
    

    【讨论】:

    • 漂亮,是的,非常感谢,到时候接受!奖金,无论如何使用'tidyselect',如果那是正确的短语? IE。而不必在 any_of 中使用字符串引用的变量名?
    • 我认为这需要一种不同的方法,因为any_of 仅适用于字符变量。
    • 提供相同结果的替代方法是mydf %&gt;% mutate(foo = sum(c_across(any_of(c("bar", "bla")))))
    【解决方案2】:

    使用 apply 函数的另一种方法

    library(dplyr)
    mydf %>%
      mutate(foo =apply(select(.,"bar","bla"), 1, sum))
    
    # A tibble: 1 x 5
      date         bar Sessions   bla   foo
      <date>     <int>    <int> <int> <int>
    1 2021-06-02     1     2990    20    21
    

    【讨论】:

      【解决方案3】:

      我们可以使用tidyverse 方法

      library(dplyr)
      library(purrr)
      df1 %>%
           select(-bar) %>% 
           mutate(foo = select(cur_data(), any_of(cols)) %>%
                coalesce(., tibble(!! cols[1] := 0)) %>%
                reduce(`+`))
      # A tibble: 1 x 4
      #  date       Sessions   bla   foo
      #  <date>        <int> <int> <int>
      #1 2021-06-02     2990    20    20
      

      如果两列都不存在

      df1 %>%
           select(-all_of(cols)) %>% 
           mutate(foo = select(cur_data(), any_of(cols)) %>%
                coalesce(., tibble(!! cols[1] := 0)) %>%
                reduce(`+`))
      # A tibble: 1 x 3
        date       Sessions   foo
        <date>        <int> <dbl>
      1 2021-06-02     2990     0
      

      【讨论】:

      • 我喜欢这种语法。如果 'bar' 和 'bla' 都缺失,这会返回 0 吗?
      • @DougFir 谢谢。你可以做df1 %&gt;% select(-all_of(cols)) %&gt;% mutate(foo = select(cur_data(), any_of(cols)) %&gt;% {if(ncol(.) &gt; 0) reduce(., +) else 0})
      • @DougFir 另外,在 cmets 中,您可以使用 df1 %&gt;% select(-all_of(cols)) %&gt;% rowwise %&gt;% mutate(foo = sum(c_across(any_of(c("bar", "bla")))))。在该评论中,未指定 rowwise。对于评论有效的单行数据集
      • @DougFir 我更新了一个更紧凑的方法。
      • 这太好了,感谢akrun的回答!
      【解决方案4】:

      我在我的一个包中创建了这个函数来做你最后描述的事情。它会在具有特定值的数据集中添加一列 - 但前提是它缺失。

      add_missing_column <- function(.data, ..., .before = NULL, .after = NULL, .name_repair = c("check_unique", "unique", "universal", "minimal")) {
        .dots <- rlang::enquos(...)
        
        cols_to_add <- .dots[!names(.dots) %in% names(.data)]
        tibble::add_column(.data, !!!cols_to_add, .before = .before, .after = .after, .name_repair = .name_repair)
      }
      

      所以在这种情况下,我会做这样的事情。

      library(dplyr)
      
      df %>% 
        add_missing_column(bar = 0L,
                           bla = 0L) %>% 
        mutate(foo = bar + bla)
      

      或者更多时候,我将列设置在列表中。

      missing_cols <- list(bar = 0L, bla = 0L)
      
      df %>% 
        add_missing_column(!!!missing_cols) %>% 
        mutate(foo = bar + bla)
      

      您可以将默认设置为任何您想要的,因为它只是转发到add_column()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-04
        • 2014-03-20
        • 1970-01-01
        • 1970-01-01
        • 2017-06-23
        • 2017-07-25
        • 1970-01-01
        相关资源
        最近更新 更多