【问题标题】:Saving then loading a `tibble` causes it to be not recognised by S4 methods保存然后加载“tibble”会导致 S4 方法无法识别它
【发布时间】:2021-12-26 13:24:46
【问题描述】:

我在 R 中有一个涉及 S4 类系统的晦涩问题。

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

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

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

然后,我定义了一个在data.frametibble 是其子类)上调度的 S4 方法:

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 正在正确保存和加载,并且它保留了它的类,但是它不再正确分派。有趣的是,如果您在我们创建df 的第一个 R 会话中定义并调用这些相同的方法,这些方法会正确分派。

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

如果这是一个特定于操作系统的错误,这里是来自sessionInfo 的一些输出:

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

【问题讨论】:

    标签: r tibble s4 rdata


    【解决方案1】:

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

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

    在一个干净的新会话中,但在创建 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 本身,确实我在新会话中看到了错误,但在我运行loadNamespace("tibble") 之后没有看到(它得到了第二个列表中的所有内容)。所以解决办法是:

    要让你的数据框方法在小标题上工作,你可以运行

    loadNamespace("tibble")
    

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

    或者,如果你只是想让你的代码像数据帧一样在 tibbles 上工作,你可以做 tibble 包所做的事情,然后运行

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

    这不会加载任何包,但它会告诉 S4 系统 tibbles 从数据帧继承。

    【讨论】:

    • 很好的答案,谢谢,我没想到要检查loadedNamespaces()。值得强调的是,只需在第二个脚本中的任何位置运行 loadNamespace("tibble") 即可解决问题。
    • 我现在已经更明确了。
    • ... 并添加了一个轻量级的解决方案:告诉 S4 tibbles 继承自数据帧,而不加载所有其他 tibble 支持代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 2014-04-30
    相关资源
    最近更新 更多