R:如何真正从 S4 对象中移除 S4 插槽(附有解决方案!)

use*_*814 6 r slot s4

假设我定义了一个'foo'带有两个插槽'a'和的 S4 类'b',并定义了一个x类的对象'foo'

setClass(Class = 'foo', slots = c(
  a = 'numeric',
  b = 'character'
))
x <- new('foo', a = rnorm(1e3L), b = rep('A', times = 1e3L))
format(object.size(x), units = 'auto') # "16.5 Kb"
Run Code Online (Sandbox Code Playgroud)

然后我想'a'从定义中删除插槽'foo'

setClass(Class = 'foo', slots = c(
  b = 'character'
))
slotNames(x) # slot 'a' automatically removed!! wow!!!
Run Code Online (Sandbox Code Playgroud)

我看到 R 会自动处理我的对象x'a'移除插槽。好的!但是等等,对象的大小x并没有减少。

format(object.size(x), units = 'auto') # still "16.5 Kb"
format(object.size(new(Class = 'foo', x)), units = 'auto') # still "16.5 Kb"
Run Code Online (Sandbox Code Playgroud)

对.. 不知何故'a'仍然存在,但我无法对它做任何事情

head(x@a) # `'a'` is still there
rm(x@a) # error
x@a <- NULL # error
Run Code Online (Sandbox Code Playgroud)

所以问题:我如何才能真正去除插槽'a'x有它的尺寸缩小(这是我的主要关注的)?


我最深切的感谢所有的答案!

以下解决方案的灵感来自 dww

trimS4slot <- function(x) {
  nm0 <- names(attributes(x))
  nm1 <- names(getClassDef(class(x))@slots) # ?methods::.slotNames
  if (any(id <- is.na(match(nm0, table = c(nm1, 'class'))))) attributes(x)[nm0[id]] <- NULL  # ?base::setdiff
  return(x)
}
format(object.size(y1 <- trimS4slot(x)), units = 'auto') # "8.5 Kb"
Run Code Online (Sandbox Code Playgroud)

以下解决方案的灵感来自 Robert Hijmans

setClass('foo1', contains = 'foo')
format(object.size(y2 <- as(x, 'foo1')), units = 'auto') # "8.5 Kb"
Run Code Online (Sandbox Code Playgroud)

method::as 可能会做一些全面的检查,所以速度很慢

library(microbenchmark)
microbenchmark(trimS4slot(x), as(x, 'foo1')) # ?methods::as 10 times slower
Run Code Online (Sandbox Code Playgroud)

Rob*_*ans 2

@dww 的建议很漂亮,回答了你的问题。但是,类的重点难道不是保证其成员(槽位)始终存在吗?如果您不关心这一点,您可以使用“一切皆有可能”S3课程来代替吗?对于S4,我建议采用更正式的方法,如下所示:

setClass(Class = 'bar', slots = c(b = 'character'))

setClass(Class = 'foo', contains='bar', slots = c(a = 'numeric'))

x <- new('foo', a = rnorm(1e3L), b = rep('A', times = 1e3L))
format(object.size(x), units = 'auto')
#[1] "16.5 Kb"

x <- as(x, "bar")  
format(object.size(x), units = 'auto')
#[1] "8.5 Kb"
Run Code Online (Sandbox Code Playgroud)

如果这只是关于尺寸,为什么不直接做

x <- new('foo', a = rnorm(1e3L), b = rep('A', times = 1e3L))
x@b <- ""
format(object.size(x), units = 'auto')
#[1] "8.7 Kb"
Run Code Online (Sandbox Code Playgroud)

对我来说,这显然是最好的解决方案,因为它很简单。