保存然后加载 `tibble` 会导致 S4 方法无法识别它

Saving then loading a `tibble` causes it to be not recognised by S4 methods

我在 R 中遇到一个涉及 S4 class 系统的模糊问题。

首先我创建了一个 tibble 并保存它:

df = tibble::tibble(a=1:5, b=2:6)
save(df, file="foo.Rdata")

接下来,我关闭 R 会话,并启动一个新会话(出于某种原因,这很重要)。

然后,我定义了一个 S4 方法,它在 data.frame 上调度(tibble 是 class 的子class):

setGeneric("sumTheColumns", function(target){    
  standardGeneric("sumTheColumns")    
})    
setMethod("sumTheColumns", signature(target="data.frame"), function(target){    
    colSums(target)    
})    
     
load("foo.Rdata")    
print(df)    
print(class(df))    
print(showMethods(sumTheColumns))    
sumTheColumns(df)   

据此,R 输出:

[1] "sumTheColumns"
  a b
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
[1] "tbl_df"     "tbl"        "data.frame"
Function: sumTheColumns (package .GlobalEnv)
target="data.frame"

Error in (function (classes, fdef, mtable)  : 
  unable to find an inherited method for function ‘sumTheColumns’ for signature ‘"tbl_df"’
Calls: sumTheColumns -> <Anonymous>
Execution halted

如您所见,tibble 已正确保存和加载,并且保留了 classes,但不再正确发送。有趣的是,如果您在我们创建 df 的第一个 R 会话中定义并调用这些相同的方法,这些方法会正确调度。

为什么会发生这种情况,我该如何解决?

如果这是 OS 特定的错误,这里是 sessionInfo 的一些输出:

> sessionInfo()
R version 4.1.1 (2021-08-10)
Platform: x86_64-conda-linux-gnu (64-bit)
Running under: Ubuntu 21.04

比较两种情况下的loadedNamespaces()条目。我明白了

> loadedNamespaces()
[1] "compiler"  "graphics"  "utils"     "grDevices" "stats"     "datasets"  "methods"   "base"    

在全新的 session 中,但在创建 df 之后,它增长到

> loadedNamespaces()
 [1] "compiler"  "magrittr"  "ellipsis"  "graphics"  "pillar"    "glue"      "utils"    
 [8] "tibble"    "grDevices" "crayon"    "stats"     "utf8"      "fansi"     "datasets" 
[15] "vctrs"     "methods"   "lifecycle" "pkgconfig" "rlang"     "base"    

所以这些新软件包之一解决了这个问题。我猜是 tibble 本身,事实上我在新的 session 中看到了错误,但在我 运行 loadNamespace("tibble") 之后没有看到(它在第二个列表中得到了所有内容) .所以一个解决方案是:

要让您的数据框方法适用于 tibbles,您可以 运行

loadNamespace("tibble")

在给他们打电话之前。如果您打算将 tibbles 视为其他地方的 tibbles,则您会想要这样做,例如让他们以 tibbles 打印的方式打印。

或者,如果您只是想让您的代码像处理数据帧一样处理 tibbles,您可以执行 tibble 程序包的操作,并且 运行

methods::setOldClass(c("tbl_df", "tbl", "data.frame"))

这不会加载任何包,但会告诉 S4 系统小标题继承自数据帧。