r - 在 R 包中定义自定义 dplyr 方法

标签 r dplyr r-package tidyverse r-s3

我有一个定制的包裹 summary() , print()具有特定类的对象的方法。这个包还用了精彩dplyr用于数据操作的包 - 我希望我的用户编写同时使用我的包和 dplyr 的脚本。
一个障碍,已被其他人注意到 herehere是 dplyr 动词不保留自定义类 - 这意味着 ungroup命令可以剥离我的自定义类的 data.frames,从而搞砸 summary 的方法调度, 等等。
Hadley 说“正确执行此操作取决于您 - 您需要为每个 dplyr 方法为您的类定义一个方法,以正确恢复所有类和属性”,我正在尝试使用 advice - 但我不知道如何正确包装 dplyr 动词。
这是一个简单的玩具示例。假设我定义了一个 cars类,我有一个自定义 summary为了它。
这有效

library(tidyverse)

class(mtcars) <- c('cars', class(mtcars))

summary.cars <- function(x, ...) {
  #gather some summary stats
  df_dim <- dim(x)
  quantile_sum <- map(mtcars, quantile)
  
  cat("A cars object with:\n")
  cat(df_dim[[1]], 'rows and ', df_dim[[2]], 'columns.\n')
  
  print(quantile_sum)

}

summary(mtcars)
问题来了
small_cars <- mtcars %>% filter(cyl < 6)
summary(small_cars)
class(small_cars)
那个summary调用 small_cars只是给了我通用摘要,而不是我的自定义方法,因为 small_cars不再保留 cars dplyr 过滤后的类。
我试过的
首先,我尝试在 filter 周围编写自定义方法( filter.cars )。那没有用,因为 filter实际上是 filter_ 周围的包装器允许进行非标准评估。
所以我写了一个自定义filter_ cars 的方法对象,试图实现 @jwdink 的 advice
filter_.cars <- function(df, ...) {
  
  old_classes <- class(df)
  out <- dplyr::filter_(df, ...)
  new_classes <- class(out)
  
  class(out) <- c(new_classes, old_classes) %>% unique()
  
  out
}
那不起作用 - 我得到一个无限递归错误:
Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)?
我想要做的就是获取传入 df 上的类,移交给 dplyr,然后返回具有与 dplyr 调用之前相同的类名的对象。 如何更改我的 filter_包装器来完成那个? 谢谢!

最佳答案

更新:
自从我最初的回答以来,有些事情发生了变化:

  • 许多 dplyr 动词不再删除自定义类;例如,dplyr::filter保持类。然而,有些——比如 dplyr::group_by - 仍然删除类(class),所以这个问题仍然存在。
  • 在 R 3.5 及更高版本中,方法查找更改了其范围规则
  • 不推荐使用尾随下划线版本的动词

  • 最近由于第二个子弹遇到了一个难以解决的问题,所以只想举一个更完整的例子。假设您正在使用一个自定义类,名称为 custom_class ,并且您想添加一个 groupby 方法。假设您使用的是 roxygen:
    #' group_by.custom_class
    #' 
    #' @description Preserve the class of a `custom_class` object.
    #' @inheritParams dplyr::group_by
    #'
    #' @importFrom dplyr group_by
    #'
    #' @export
    #' @method group_by custom_class
    group_by.custom_class <- function(.data, ...) {
      result <- NextMethod()
      return(reclass(.data, result))
    }
    
    (有关 reclass 函数的定义,请参阅原始答案)
    强调:
  • 您需要 @method group_by custom_class添加 S3method(group_by,custom_class)到命名空间
  • 您需要 @importFrom dplyr group_by添加 importFrom(dplyr,group_by)到您的命名空间

  • 我相信 R < 3.5 你可以只用第二个就可以逃脱,但现在你需要两者。

    旧答案:
    the thread 中提供了进一步的建议所以我想我会更新似乎是最佳实践,即使用 NextMethod() .
    filter_.cars <- function(.data, ...) {
       result <- NextMethod()
       reclass(.data, result)
    }
    
    哪里reclass是你写的;它只是一个泛型,(至少)重新添加了原始类:
    reclass <- function(x, result) {
      UseMethod('reclass')
    }
    
    reclass.default <- function(x, result) {
      class(result) <- unique(c(class(x)[[1]], class(result)))
      result
    }
    

    关于r - 在 R 包中定义自定义 dplyr 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41967700/

    相关文章:

    r - 检查间隔开始和结束时间是否重叠

    r - 如何在 R 中创建一个变量来明确另一个变量的差异?

    R 包 : writing internal data, 但不是一次全部

    r - 在构建和重新加载 R 包时显示小插图链接

    r - 在 R 包中测试与用户的交互

    r - 使用 httr 包,设置标题名称是变量的标题

    r - 为多个标记变量设置缺失值

    r - 合并数据帧,优先级高于另一个

    r - 如何使用ggplot在x轴上只显示年份

    r - 使用 Shiny 写入和保存到 excel 文件