【发布时间】:2019-01-07 23:18:28
【问题描述】:
如何为dplyr::do 内部的函数实现方法分派?
我已经阅读了 GitHub 问题 #719、#3558 和 #3429,它们提供了有关如何为 dplyr 动词创建方法的有用信息,但没有什么特别适用于 dplyr::do - 这是排序“特殊”的意义在于调度不仅需要发生在dplyr:do 本身,还需要发生在dplyr::do 内部调用的函数(或者至少这是我所追求的)
这是我尝试过的:
预赛
library(dplyr)
#>
#> Attache Paket: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
# Example data ------------------------------------------------------------
df <- tibble::tibble(
id = c(rep("A", 5), rep("B", 5)),
x = 1:10
)
df_custom <- df
class(df_custom) <- c("tbl_df_custom", class(df_custom))
# Reclass function --------------------------------------------------------
reclass <- function(x, result) {
UseMethod('reclass')
}
reclass.default <- function(x, result) {
class(result) <- unique(c(class(x)[[1]], class(result)))
attr(result, class(x)[[1]]) <- attr(x, class(x)[[1]])
result
}
第 1 步:尝试为 dplyr 动词定义方法
# Custom method for summarize ---------------------------------------------
summarise.tbl_df_custom <- function (.data, ...) {
message("Custom method for `summarise`")
result <- NextMethod("summarise")
ret <- reclass(.data, result)
print(class(ret))
ret
}
ret <- df_custom %>%
summarise(y = mean(x))
#> Custom method for `summarise`
#> [1] "tbl_df_custom" "tbl_df" "tbl" "data.frame"
ret %>% class()
#> [1] "tbl_df_custom" "tbl_df" "tbl" "data.frame"
第 2 步:尝试为另一个 dplyr 动词定义一个方法来测试更长的管道
# Custom method for group_by ----------------------------------------------
group_by.tbl_df_custom <- function (.data, ..., add = FALSE) {
message("Custom method for `group_by`")
result <- NextMethod("group_by")
ret <- reclass(.data, result)
print(class(ret))
ret
}
ret <- df_custom %>%
group_by(id) %>%
summarise(y = mean(x))
#> Custom method for `group_by`
#> [1] "tbl_df_custom" "grouped_df" "tbl_df" "tbl"
#> [5] "data.frame"
#> Custom method for `summarise`
#> [1] "tbl_df_custom" "tbl_df" "tbl" "data.frame"
ret %>% class()
#> [1] "tbl_df_custom" "tbl_df" "tbl" "data.frame"
第 3 步:对 do 尝试相同的操作
# Custom method for do ----------------------------------------------------
do.tbl_df_custom <- function (.data, ...) {
message("custom method for `do`")
result <- NextMethod("do")
ret <- reclass(.data, result)
print(class(ret))
ret
}
foo <- function(df) {
UseMethod("foo")
}
foo.default <- function(df) {
message("Default method for `foo`")
df %>%
summarise(y = mean(x))
}
foo.tbl_df_custom <- function(df) {
message("Custom method for `foo`")
df %>%
summarise(y = mean(x) * 100)
}
ret <- df_custom %>%
group_by(id) %>%
do(foo(.))
#> Custom method for `group_by`
#> [1] "tbl_df_custom" "grouped_df" "tbl_df" "tbl"
#> [5] "data.frame"
#> custom method for `do`
#> Default method for `foo`
#> Default method for `foo`
#> [1] "tbl_df_custom" "grouped_df" "tbl_df" "tbl"
#> [5] "data.frame"
ret
#> # A tibble: 2 x 2
#> # Groups: id [2]
#> id y
#> <chr> <dbl>
#> 1 A 3
#> 2 B 8
ret %>% class()
#> [1] "tbl_df_custom" "grouped_df" "tbl_df" "tbl"
#> [5] "data.frame"
虽然这看起来不错,但问题是调用了 default 而不是 foo 的 custom 方法。
由reprex package (v0.2.1) 于 2019 年 1 月 8 日创建
【问题讨论】:
-
令人着迷的问题,我无法找到问题所在,但这里有一些可能有用的信息。自定义
do函数适用于未分组的数据帧:df_custom %>% do(foo(.))。只有当您group_by时才会出现问题。dplyr内部对分组数据帧的处理方式不同。请注意代码如何两次打印“foo的默认方法”。这是因为do.grouped_df函数内部有一个双for循环,它按组获取数据帧的切片。当这些切片被取走时,你定义的特殊类就丢失了。查看class(df_custom[1,]) -
group_by调用ungroup,另一个泛型,剥离自定义类。也许需要一个ungroup.tbl_df_custom方法。