【问题标题】:Combine: rowwise(), mutate(), across(), for multiple functions组合:rowwise()、mutate()、cross(),用于多个函数
【发布时间】:2021-07-24 14:14:57
【问题描述】:

这与question 有某种关联: 原则上,我尝试了解rowwisemutate 在多个列中应用超过1 个函数(如(mean()sum()min() 等)的工作原理。

我了解到 across 完成这项工作,而不是 c_across。 我了解到函数 mean() 与函数 min() 不同,因为 mean() 不适用于数据帧,我们需要将其更改为可以使用 unlist 或 as.matrix 完成的向量 ->向罗纳克·沙阿学习hereUnderstanding rowwise() and c_across()

现在以我的实际情况为例:我能够完成这项任务,但我丢失了一列 d。在此设置中如何避免d 列松动。

我的 df:

df <- structure(list(a = 1:5, b = 6:10, c = 11:15, d = c("a", "b", 
"c", "d", "e"), e = 1:5), row.names = c(NA, -5L), class = c("tbl_df", 
"tbl", "data.frame"))

无效:

df %>% 
  rowwise() %>% 
  mutate(across(a:e), 
         avg = mean(unlist(cur_data()), na.rm = TRUE),
         min = min(unlist(cur_data()), na.rm = TRUE), 
         max = max(unlist(cur_data()), na.rm = TRUE)
  )

# Output:
      a     b     c d         e   avg min   max  
  <int> <int> <int> <chr> <int> <dbl> <chr> <chr>
1     1     6    11 a         1    NA 1     a    
2     2     7    12 b         2    NA 12    b    
3     3     8    13 c         3    NA 13    c    
4     4     9    14 d         4    NA 14    d    
5     5    10    15 e         5    NA 10    e 

工作,但我失去了专栏d

df %>% 
  select(-d) %>% 
  rowwise() %>% 
  mutate(across(a:e), 
         avg = mean(unlist(cur_data()), na.rm = TRUE),
         min = min(unlist(cur_data()), na.rm = TRUE), 
         max = max(unlist(cur_data()), na.rm = TRUE)
  )

      a     b     c     e   avg   min   max
  <int> <int> <int> <int> <dbl> <dbl> <dbl>
1     1     6    11     1  4.75     1    11
2     2     7    12     2  5.75     2    12
3     3     8    13     3  6.75     3    13
4     4     9    14     4  7.75     4    14
5     5    10    15     5  8.75     5    15

【问题讨论】:

    标签: r dplyr rowwise across


    【解决方案1】:

    使用purrr 中的pmap() 可能更可取,因为您只需选择一次数据,就可以使用选择助手:

    df %>% 
     mutate(pmap_dfr(across(where(is.numeric)),
                     ~ data.frame(max = max(c(...)),
                                  min = min(c(...)),
                                  avg = mean(c(...)))))
    
          a     b     c d         e   max   min   avg
      <int> <int> <int> <chr> <int> <int> <int> <dbl>
    1     1     6    11 a         1    11     1  4.75
    2     2     7    12 b         2    12     2  5.75
    3     3     8    13 c         3    13     3  6.75
    4     4     9    14 d         4    14     4  7.75
    5     5    10    15 e         5    15     5  8.75
    

    或者加上tidyr:

    df %>% 
     mutate(res = pmap(across(where(is.numeric)),
                       ~ list(max = max(c(...)),
                              min = min(c(...)),
                              avg = mean(c(...))))) %>%
     unnest_wider(res)
    

    【讨论】:

    【解决方案2】:

    编辑:

    最好的出路

    df %>%
      rowwise() %>% 
      mutate(min = min(c_across(a:e & where(is.numeric)), na.rm = TRUE),
             max = max(c_across(a:e & where(is.numeric)), na.rm = TRUE), 
             avg = mean(c_across(a:e & where(is.numeric)), na.rm = TRUE)
      )
    
    # A tibble: 5 x 8
    # Rowwise: 
          a     b     c d         e   min   max   avg
      <int> <int> <int> <chr> <int> <int> <int> <dbl>
    1     1     6    11 a         1     1    11  4.75
    2     2     7    12 b         2     2    12  5.75
    3     3     8    13 c         3     3    13  6.75
    4     4     9    14 d         4     4    14  7.75
    5     5    10    15 e         5     5    15  8.75
    

    较早的答案 您的 this will work 甚至无法正常工作,如果您更改输出顺序,请参阅

    df %>% 
      select(-d) %>% 
      rowwise() %>% 
      mutate(across(a:e), 
             min = min(unlist(cur_data()), na.rm = TRUE),
             max = max(unlist(cur_data()), na.rm = TRUE), 
             avg = mean(unlist(cur_data()), na.rm = TRUE)
      )
    
    # A tibble: 5 x 7
    # Rowwise: 
          a     b     c     e   min   max   avg
      <int> <int> <int> <int> <int> <int> <dbl>
    1     1     6    11     1     1    11  5.17
    2     2     7    12     2     2    12  6.17
    3     3     8    13     3     3    13  7.17
    4     4     9    14     4     4    14  8.17
    5     5    10    15     5     5    15  9.17
    
    

    因此,建议这样做-

    df %>% 
      select(-d) %>% 
      rowwise() %>% 
      mutate(min = min(c_across(a:e), na.rm = TRUE),
             max = max(c_across(a:e), na.rm = TRUE), 
             avg = mean(c_across(a:e), na.rm = TRUE)
      )
    
    # A tibble: 5 x 7
    # Rowwise: 
          a     b     c     e   min   max   avg
      <int> <int> <int> <int> <int> <int> <dbl>
    1     1     6    11     1     1    11  4.75
    2     2     7    12     2     2    12  5.75
    3     3     8    13     3     3    13  6.75
    4     4     9    14     4     4    14  7.75
    5     5    10    15     5     5    15  8.75
    

    另一种选择是

    cols <- c('a', 'b', 'c', 'e')
    df %>%
      rowwise() %>% 
      mutate(min = min(c_across(cols), na.rm = TRUE),
             max = max(c_across(cols), na.rm = TRUE), 
             avg = mean(c_across(cols), na.rm = TRUE)
      )
    
    # A tibble: 5 x 8
    # Rowwise: 
          a     b     c d         e   min   max   avg
      <int> <int> <int> <chr> <int> <int> <int> <dbl>
    1     1     6    11 a         1     1    11  4.75
    2     2     7    12 b         2     2    12  5.75
    3     3     8    13 c         3     3    13  6.75
    4     4     9    14 d         4     4    14  7.75
    5     5    10    15 e         5     5    15  8.75
    

    在这些情况下,即使@Sinh 建议的 group_by 方法也无法正常工作。

    【讨论】:

    • 非常感谢 AnilGoyal。我非常感谢您的努力,我理解您的所有想法并同意您的看法。但不知何故我不满意重复c_across(a:e &amp; where(is.numeric)), na.rm = TRUE) 三遍。我想应该可以防止这种重复并与across一起使用一次。
    • TarJae,我担心你试图用 dplyr 做的事情可能是不可能的,mutate(across.. 的工作方式略有不同。它会改变其中的所有现有列。即使您使用cur_data,它也会包含新添加的列,如上所示。此外,与mutate(across 不同,您仍然需要再次重复cur_data..bla..bla n 次。但是,这可能通过purrr 中的许多方法实现 - 如答案所示。
    【解决方案3】:

    如果我们想将特定列设置为行名属性 (column_to_rownames),然后在转换后返回该属性,这里有一种方法可以保留 mutate 中的 data.frame 属性

    library(dplyr)
    library(tibble)
    library(purrr)
    df %>% 
       column_to_rownames('d') %>%
       mutate(max = reduce(., pmax), min = reduce(., pmin), 
             avg = rowMeans(.)) %>% 
       rownames_to_column('d')
    #  d a  b  c e max min  avg
    #1 a 1  6 11 1  11   1 4.75
    #2 b 2  7 12 2  12   2 5.75
    #3 c 3  8 13 3  13   3 6.75
    #4 d 4  9 14 4  14   4 7.75
    #5 e 5 10 15 5  15   5 8.75
    

    【讨论】:

    • 亲爱的 Arun,是否可以使用 invoke_mapexec 在每一行上应用所有三个函数?
    • 好的,谢谢。我还将mean 包含在函数列表中,但由于它是按列操作,因此无法获得所需的结果。然而pminpmax 使得获得每行的最大值和最小值成为可能,因为它们的功能。我只是好奇。
    • @AnoushiravanR 函数 pmin/pmaxmean 具有不同的行为。 pmin/pmax 并行作用于行,其中mean 需要一个向量并且它没有被向量化。您可能期望rowMeans 可以工作,但该函数的参数只是一个参数,即x,它可以是矩阵或data.frame/tibble。在pmax/pmin 中,它是... 即。它可能会有所不同。所以invoke_map(list(pmax, pmin), list(df %&gt;% select(-d))) 工作的参数是 data.frame 列
    • 非常感谢。参数匹配是我需要在这样的时刻考虑的一个重要因素。但很高兴自己终于熟悉了平行最大值和最小值。
    猜你喜欢
    • 2016-01-10
    • 1970-01-01
    • 2018-02-16
    • 1970-01-01
    • 2016-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多