【问题标题】:Userfunction with optional grouping argument and if else using piping in R具有可选分组参数的用户函数,如果在 R 中使用管道
【发布时间】:2021-04-01 11:34:38
【问题描述】:

我最近开始编写自己的函数来加速标准和重复性任务,同时使用 R 分析数据。

目前我正在处理一个带有三个参数的函数,但遇到了一个我还无法解决的挑战。我想要一个可选的分组参数。在此过程中,函数应检查是否有分组参数,然后继续使用子函数 1 或 2。

但如果分组参数不是 NA,我总是会收到错误“找不到对象”。我该怎么做?

编辑:在我的情况下,过滤器通常用于过滤某些有效或无效的年份。如果有分组参数,则管道中的步骤将比没有分组参数时更多。

require(tidyverse)

Data <- mpg

userfunction <- function(DF,Filter,Group) {
  
  without_group <- function(DF) {
    DF %>% 
      count(year)
  }
  
  with_group <- function(DF) {
    DF %>% 
      group_by({{Group}}) %>% 
      count(year) %>% 
      pivot_wider(names_from=year, values_from=n) %>%
      ungroup() %>% 
      mutate(across(.cols=2:ncol(.),.fns=~replace_na(.x, 0))) %>% 
      mutate(Mittelwert=round(rowMeans(.[,2:ncol(.)],na.rm=TRUE),2))
  }
  
  Obj <- DF %>% 
    ungroup() %>% 
    {if(Filter!=FALSE) filter(.,eval(rlang::parse_expr(Filter))) else filter(.,.$year==.$year)} %>%
    {if(is.na(Group)) without_group(.) else with_group(.)} 
  
  return(Obj)
    
}

对于 NA,它已经有效:

> Data %>% 
+   userfunction(FALSE,NA)
# A tibble: 2 x 2
   year     n
  <int> <int>
1  1999   117
2  2008   117

使用参数它不起作用:

> Data %>% 
+   userfunction(FALSE,manufacturer)
 Error in DF %>% ungroup() %>% { : object 'manufacturer' not found

编辑: 我对上述函数的期望是以下输出:

> Data %>% userfunction_exp(FALSE,manufacturer)
# A tibble: 15 x 4
   manufacturer `1999` `2008` Mittelwert
   <chr>         <dbl>  <dbl>      <dbl>
 1 audi              9      9        9  
 2 chevrolet         7     12        9.5
 3 dodge            16     21       18.5
 4 ford             15     10       12.5
 5 honda             5      4        4.5
 6 hyundai           6      8        7  
 7 jeep              2      6        4  
 8 land rover        2      2        2  
 9 lincoln           2      1        1.5
10 mercury           2      2        2  
11 nissan            6      7        6.5
12 pontiac           3      2        2.5
13 subaru            6      8        7  
14 toyota           20     14       17  
15 volkswagen       16     11       13.5

 Data %>% userfunction_exp("cyl==4",manufacturer)
# A tibble: 9 x 4
  manufacturer `1999` `2008`  mean
  <chr>         <dbl>  <dbl> <dbl>
1 audi              4      4   4  
2 chevrolet         1      1   1  
3 dodge             1      0   0.5
4 honda             5      4   4.5
5 hyundai           4      4   4  
6 nissan            2      2   2  
7 subaru            6      8   7  
8 toyota           11      7   9  
9 volkswagen       11      6   8.5

2021-04-01 14:55:编辑添加一些信息,并为函数 with_group 添加一些步骤。

【问题讨论】:

标签: r


【解决方案1】:

您好,这是个好问题!

正如前面的答案所指出的,有多种方法可以实现这一目标。在 tidyverse 中做到这一点的一种方法是 tidy evaluation

省略您的过滤器功能(您可以更详细地解释...)

 my_summary <- function(df, grouping_var) {
  grp_var <- enquo(grouping_var) #capture group variable
  df %>% my_group_by(grp_var)
}


my_group_by <- function(df, grouping_var){
  # Check if group is supplied 
  if(rlang::quo_is_missing(grouping_var)) {
    df %>% without_group()
  } else {
    df %>% with_group(grouping_var)
  }
  
}


without_group <- function(df) {
  # do whatever without group
  df %>% 
    count(year)
}

with_group <- function(df, grouping_var) {
  # do whatever with group
  df %>% 
    group_by(!!grouping_var) %>% #Note the !!
    count(year) %>% 
    pivot_wider(names_from=year, values_from=n)
}

这会给你没有任何争论

> mpg %>% my_summary()
# A tibble: 2 x 2
   year     n
  <int> <int>
1  1999   117
2  2008   117

将组传递给管道

> mpg %>% my_summary(model)
# A tibble: 38 x 3
# Groups:   model [38]
   model              `1999` `2008`
   <chr>               <int>  <int>
 1 4runner 4wd             4      2
 2 a4                      4      3
 3 a4 quattro              4      4
 4 a6 quattro              1      2
 5 altima                  2      4
 6 c1500 suburban 2wd      1      4
 7 camry                   4      3
 8 camry solara            4      3
 9 caravan 2wd             6      5
10 civic                   5      4
# ... with 28 more rows

【讨论】:

  • 非常感谢,这行得通!我仍然把它放在一个带有子函数的函数中。我也只在 if else 函数中使用 enquoted grp_var 进行条件检查,然后在后续步骤中使用 {{grouping_var}},因为它在函数中仍然可用。
【解决方案2】:

我不知道Filter 参数有什么用,所以我暂时保留它。

group_by(A) %&gt;% count(B)count(A, B) 相同,因此您可以将函数更改为:

library(tidyverse)

userfunction <- function(DF,Filter,Group = NULL) {
  DF %>% 
    count(year, {{Group}}) %>% 
    pivot_wider(names_from=year, values_from=n)
}

Data %>% userfunction(FALSE)

#   `1999` `2008`
#   <int>  <int>
#1    117    117

Data %>% userfunction(FALSE,manufacturer)
# A tibble: 15 x 3
#   manufacturer `1999` `2008`
#   <chr>         <int>  <int>
# 1 audi              9      9
# 2 chevrolet         7     12
# 3 dodge            16     21
# 4 ford             15     10
# 5 honda             5      4
# 6 hyundai           6      8
# 7 jeep              2      6
# 8 land rover        2      2
# 9 lincoln           2      1
#10 mercury           2      2
#11 nissan            6      7
#12 pontiac           3      2
#13 subaru            6      8
#14 toyota           20     14
#15 volkswagen       16     11

请注意,我已将默认值分配给 GroupNULL,因此当您不提及任何内容时,它会忽略该参数。

【讨论】:

  • 谢谢,在我的 MWE 的情况下,您的解决方案肯定会奏效。但是在我的用例中,如果有分组参数而不是没有分组参数,则管道中将遵循更多步骤。所以我需要在那里工作。如果我使用 is.null() 检查 NULL,我会遇到与 is.na() 相同的问题。当列名作为参数传递时,它返回“找不到对象”。
  • 过滤器将用于过滤年,因为我的数据集通常包含两年以上,有时会因为丢失的数据太多而过滤年。
  • 您能否更新您的帖子以包含该函数的一些用例,以显示它将如何被调用以及对应的预期输出是什么?
猜你喜欢
  • 1970-01-01
  • 2015-09-12
  • 1970-01-01
  • 2019-03-20
  • 2015-11-07
  • 2021-09-05
  • 2022-06-20
  • 1970-01-01
  • 2012-02-08
相关资源
最近更新 更多