【问题标题】:Writing 'as.data.frame' method for custom S3 objects为自定义 S3 对象编写“as.data.frame”方法
【发布时间】:2016-01-29 18:27:50
【问题描述】:

在许多变体中,类似的问题被问了很多次...... 但我没有找到明确的建议:

“将 S3 方法导出为函数”

我用 roxygen2 编写了一个自定义 S3 类,称之为 'my_item'。 这是构造函数:

my_item <- function(n) structure(list(n=n),class='my_item')

我需要的是一种定义"list of my_items =&gt; data.frame" 转换函数的方法:

 #' @method as.data.frame my_item
 #' @export

 as.data.frame.my_item <- function(x) ...

只要我以这种方式用 my_item 调用它,就可以了:

as.data.frame(my_item('a'))

但是对对象列表应用相同的调用是行不通的,因为class(list) 是空的:

as.data.frame(list(my_item('a'),my_item('b')))

这也不起作用,因为函数/方法没有导出

as.data.frame.my_item(list(my_item('a'),my_item('b')))

这不适用于显式命名空间限定:

my_pkg::as.data.frame.my_item(...)

Error: 'as.data.frame.my_item' is not an exported object from 'namespace:my_pkg'

zoo 包中,plot 通用函数可以做到这一点。

参见plot.zoo,一种 S3 方法,导出为函数

zoo::NAMESPACE

export(
   ...
   "plot.zoo"
   ...
)
S3method("plot", "zoo")
S3method("as.data.frame", "zoo")

生成的包范围是:

library(zoo)

methods('as.data.frame')

[1] as.data.frame.aovproj*        as.data.frame.array           as.data.frame.AsIs           
[4] as.data.frame.character       as.data.frame.chron*          as.data.frame.complex        
[7] as.data.frame.data.frame      as.data.frame.data.table*     as.data.frame.Date           
[10] as.data.frame.dates*          as.data.frame.default         as.data.frame.difftime       
[13] as.data.frame.factor          as.data.frame.ftable*         as.data.frame.integer        
[16] as.data.frame.ITime*          as.data.frame.list            as.data.frame.logical        
[19] as.data.frame.logLik*         as.data.frame.matrix          as.data.frame.model.matrix   
[22] as.data.frame.noquote         as.data.frame.numeric         as.data.frame.numeric_version
[25] as.data.frame.ordered         as.data.frame.POSIXct         as.data.frame.POSIXlt        
[28] as.data.frame.raw             as.data.frame.shingle*        as.data.frame.table          
[31] as.data.frame.times*          as.data.frame.ts              as.data.frame.vector         
[34] as.data.frame.yearmon*        as.data.frame.yearqtr*        as.data.frame.zoo*           
see '?methods' for accessing help and source code

methods('plot')

[1] plot.acf*           plot.data.frame*    plot.decomposed.ts* plot.default        plot.dendrogram*   
[6] plot.density*       plot.ecdf           plot.factor*        plot.formula*       plot.function      
[11] plot.hclust*        plot.histogram*     plot.HoltWinters*   plot.isoreg*        plot.lm*           
[16] plot.medpolish*     plot.mlm*           plot.ppr*           plot.prcomp*        plot.princomp*     
[21] plot.profile.nls*   plot.raster*        plot.shingle*       plot.spec*          plot.stepfun       
[26] plot.stl*           plot.table*         plot.times*         plot.trellis*       plot.ts            
[31] plot.tskernel*      plot.TukeyHSD*      plot.zoo           
see '?methods' for accessing help and source code

这说明plot.zoo被导出,as.data.frame.zoo*没有被导出


所以问题可能是错误的。

更好的是:

“在使用 'lists-of-list-based-S3-objects' 时如何实现 cast-protocol ('as-...') p>

【问题讨论】:

  • 你发帖后有看这个吗?请格式化您的代码
  • 我发帖时正在关机;)
  • 这可能是一种特殊情况,因为函数本身(即as.data.frame)中有一个句点 - 与导出带有 Roxygen 句点的函数的情况相反与 S3 方法混淆。如果您对没有句点的函数执行此操作(例如,定义 plotsummary 方法),它是否有效?
  • 我没有检查,但我的印象是名称中的点不是问题的根源。我在 NAMESPACE 中看到的只是一个条目:S3method(as.data.frame,my_item),但没有明确的export(as.data.frame.my_item)。顺便说一句,methods('as.data.frame') 表明它仅对 base 包公开,但对其他包隐藏。请参阅 as.data.frame.listas.data.frame.data.table*(未导出)
  • 该方法可以使用:::命名空间运算符作为函数访问:参见thishelp(':::')

标签: r vectorization roxygen2


【解决方案1】:

我采取了不同的方法:

  • 通过as.my_item_list() 函数将'my_item'+'_list' 类添加到通用列表中
  • 定义一个在我的“custom”列表上工作的as.data.frame.my_item_list方法
  • 使用泛型调用 DF 转换:as.data.frame(as.my_item_list(some_list))

在以下示例中,我将 my_item 类名替换为更短的 ob


定义

# ---( the 'ob' S3 constructor )---------------------------------------------

ob <- function(a,b) structure(list(a=a,b=b), class='ob')
is.ob <- function(x) inherits(x,'ob')

#---( the 'ob_list' cast for lists )-----------------------------------------

as.ob_list <- function(x, ...) UseMethod('as.ob_list')
as.ob_list.list <- function(x, ...) {
    stopifnot(all(sapply(x,is.ob)))
    class(x) <- append(class(x),'ob_list',after=0)
    x
}

#---( as.data.frame.* methods )----------------------------------------------

as.data.frame.ob <- function(x, ...) data.frame(t(as.matrix(unlist(x))))
as.data.frame.ob_list <- function(x, ...) do.call('rbind', lapply(x,as.data.frame))

测试

o1 <- ob(1,10)
o2 <- ob(2,20)
o3 <- ob(3,30)

ol <- list(o1,o2,o3)

df <- as.data.frame(as.ob_list(ol))

print(df)

  a  b
1 1 10
2 2 20
3 3 30

看起来不错!

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-11-21
  • 2018-03-23
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 2015-07-20
  • 2019-04-06
  • 1970-01-01
相关资源
最近更新 更多