【问题标题】:dplyr pass NULL to group_bydplyr 将 NULL 传递给 group_by
【发布时间】:2019-03-20 15:42:11
【问题描述】:

这可能已经在某个地方得到了回答,但我找不到答案...标记为重复并按你喜欢的方式投反对票,但请有人帮助我:)

小问题

如何在函数内部将NULL 传递给dplyr::group_by

library(dplyr)

dt <- data.frame(a = sample(LETTERS[1:2], 100, replace = TRUE), b = sample(LETTERS[3:4], 100, replace = TRUE), value = rnorm(100,5,1))

f1 <- function(dt, a, b, c) {
  dt %>% group_by(a, b, c) %>% summarise(mean = mean(value))
}

f1(dt, a = "a", b = "b", c = NULL)

# Error in grouped_df_impl(data, unname(vars), drop) : 
#  Column `c` is unknown 

详细解释

我正在编写一个函数,其中“b”列可以指定为NULL,这意味着该函数应忽略此列。如果“b”列作为字符给出,则函数应使用该列来汇总数据。像这样:

f2 <- function(dt, a, b) {
  if(is.null(b)) {
    dt %>% group_by(a) %>% summarize(mean = mean(value))
  } else {
    dt %>% group_by(a, b) %>% summarize(mean = mean(value))
  }
}

实际函数相当长且复杂,并且使用 dplyr 管道使所有汇总代码更短。我有多个条件导致不同的输出和总结备选方案,因此我通过首先分组并在单独的步骤中总结来缩短 if else 语句:

f3 <- function(dt, a, b, type = "mean") {
  if(is.null(b)) {
    tmp <- dt %>% group_by(a) 
  } else {
    tmp <- dt %>% group_by(a, b)
  }

  if(type == "mean") {
    tmp %>% summarize(mean = mean(value))
  } else {
    tmp %>% summarise(sum = sum(value))
  }
}

如果可以将NULL 传递给group_by 函数,我可以大大缩短我的代码(NULL 无论如何都应该是空的,这样的传递适用于许多函数,例如来自同一个函数的reshape2::melt作者)。

【问题讨论】:

  • 如果字符串"NULL" 没有出现在您的数据中,您能否将NULL 替换为其等效字符串?
  • C 总是 NULL 吗?
  • @RandallHelms 并非总是如此,与长篇解释中的想法相同。
  • 我想我的例子在翻译中有点丢失了。原则是有的,但他们缺乏理由。我首先从一个自定义类在我的函数中创建了一个对象,对其进行了很多修改,然后我将对象中的一个 data.frame 传递给那些有条件的dplyr 管道。 NULLs 实际上是控制是否应该绘制某些元素的参数。
  • 这是一篇关于使用 NULLNA r-bloggers.com/r-na-vs-null 的好帖子。 c 不应列为 NULL 而c 应为 NA,试试这个,看看哪个有效:group_by(mtcars, cyl, mpg, NA) group_by(mtcars, cyl, mpg, NULL)

标签: r if-statement dplyr


【解决方案1】:

我不确定这是否涵盖了您的所有用例,但是使用整洁评估的函数(请参阅programming with dplyr vignette)会更灵活,因为您不必担心有多少分组变量你可以传递一个任意的函数向量来总结。希望这样可以避免跟踪NULL 列或使用ifelse 来选择汇总函数。

例如,在下面的代码中,... 是任意数量的分组列,包括根本没有分组列。 type 参数允许您通过一个或多个任意函数进行汇总:

library(tidyverse)
library(rlang)

set.seed(2)
dt <- data.frame(a = sample(LETTERS[1:2], 100, replace = TRUE), 
                 b = sample(LETTERS[3:4], 100, replace = TRUE), 
                 value = rnorm(100,5,1))

f1 = function(data, value.var, ...,  type="mean") {

  groups = enquos(...)
  value.var = enquo(value.var)

  names(type) = paste0(type, "_", quo_text(value.var))
  type = syms(type)

  data %>% group_by(!!!groups) %>% 
    summarise_at(vars(!!value.var), funs(!!!type))
}

f1(dt, value, a, b)
  a     b     mean_value
  <fct> <fct>      <dbl>
1 A     C           5.01
2 A     D           5.05
3 B     C           4.95
4 B     D           5.13
f1(dt, value)
  mean_value
       <dbl>
1       5.03
weird_func = function(x) {
  paste(round(cos(x),1)[1:3], collapse="/")
}

f1(dt, value, a, b, type=c("mean", "min", "median", "max", "weird_func"))
  a     b     mean_value min_value median_value max_value weird_func_value
  <fct> <fct>      <dbl>     <dbl>        <dbl>     <dbl> <chr>           
1 A     C           5.01      3.26         5.07      7.08 1/-0.1/1        
2 A     D           5.05      2.90         5.33      6.36 -0.4/0.9/0      
3 B     C           4.95      3.66         4.73      7.11 0.5/-0.5/0.7    
4 B     D           5.13      2.98         5.46      7.05 0/0.7/0.7
f1(mtcars, mpg, cyl, type=c("mean", "median"))
    cyl mean_mpg median_mpg
  <dbl>    <dbl>      <dbl>
1     4     26.7       26  
2     6     19.7       19.7
3     8     15.1       15.2

【讨论】:

    【解决方案2】:

    我认为您需要首先将其从 NULL 转换为 NA,就像这样(从您的答案中,您只需要传递值而不涉及计算)

    library(dplyr)
    
    dt <- data.frame(a = sample(LETTERS[1:2], 100, replace = TRUE), b = sample(LETTERS[3:4], 100, replace = TRUE), value = rnorm(100,5,1))
    
    f1 <- function(dt, a, b, c) {
      dt %>% 
        mutate(c = ifelse(is_empty(c)==TRUE,NA,c)) %>% 
        group_by(a, b,c) %>% 
        summarise(mean = mean(value))
    }
    
    f1(dt, a = "a", b = "b",c=NULL)
    

    结果:

    # A tibble: 4 x 4
    # Groups:   a, b [?]
      a     b     c      mean
      <fct> <fct> <lgl> <dbl>
    1 A     C     NA     5.27
    2 A     D     NA     5.18
    3 B     C     NA     5.27
    4 B     D     NA     5.49
    

    【讨论】:

    • 不错的技巧!有人可以称之为笨拙的编程(我的错),但它可以满足我的要求(或者更准确地说:如果您添加一条语句来删除只有 NA 的列,但使用filter 很容易做到这一点)。我学到了一些新东西。谢谢!
    猜你喜欢
    • 2018-05-15
    • 2021-01-19
    • 1970-01-01
    • 2015-07-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 2015-12-13
    • 2018-09-08
    相关资源
    最近更新 更多