R:如何真正从 S4 对象中删除 S4 插槽(附有解决方案!)
R: How to truly remove an S4 slot from an S4 object (Solution attached!)
假设我定义了一个带有两个插槽 'a'
和 'b'
的 S4 class 'foo'
,并定义了 [=56= 的对象 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"
然后我想从 'foo'
的定义中删除插槽 'a'
setClass(Class = 'foo', slots = c(
b = 'character'
))
slotNames(x) # slot 'a' automatically removed!! wow!!!
我看到 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"
对..不知何故 'a'
仍然存在,但我无法对它做任何事情
head(x@a) # `'a'` is still there
rm(x@a) # error
x@a <- NULL # error
所以问题:我怎样才能 真正 从 x
中删除插槽 'a'
并 减小其大小 (这是我最关心的问题)?
非常感谢所有的回答!
以下解决方案的灵感来自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"
以下解决方案的灵感来自Robert Hijmans
setClass('foo1', contains = 'foo')
format(object.size(y2 <- as(x, 'foo1')), units = 'auto') # "8.5 Kb"
method::as
可能会做一些全面的检查,所以速度很慢
library(microbenchmark)
microbenchmark(trimS4slot(x), as(x, 'foo1')) # ?methods::as 10 times slower
插槽存储为属性。我们有几个选项可以将插槽转换为 NULL
.
选项 1:您可以使用 slot<-
中的 check=FALSE
参数将插槽分配为 NULL 而不会触发错误。
slot(x, 'a', check=FALSE) <- NULL
setClass(Class = 'foo', slots = c(b = 'character'))
format(object.size(x), units = 'auto')
# [1] "8.7 Kb"
但是,该属性并未完全 删除(它仍然存在,值为 [=15=]1NULL[=15=]1
)。发生这种情况是因为 C 函数 R_do_slot_assign
中的一行具有: if(isNull(value)) value = pseudo_NULL;
其中 pseudo_NULL 是“一个对象,用于表示为 NULL 的槽(这是一个属性不能)".
还应注意 ?slot
中的建议,即“用户不应在正常使用中设置 check=FALSE
,因为生成的对象可能无效。”在这种情况下应该不会引起任何问题,因为插槽会在之后立即被删除。尽管如此,在使用 check=False 标志时要谨慎,除非你确定你明白你在做什么。
选项2:更彻底的移除可以通过在class定义中移除槽后直接移除属性来实现:
setClass(Class = 'foo', slots = c(
b = 'character'
))
attr(x, 'a') <- NULL
format(object.size(x), units = 'auto')
# [1] "8.7 Kb"
但是,删除插槽是个好主意吗?
删除插槽有点像 hack,可能会在以后导致错误,例如如果调用的方法假定插槽存在。您可能会在自己的机器上针对特定用例执行此操作。但是将其作为生产代码发布到野外并不是一个好主意。在那种情况下,@RobertHijmans 的回答中的方法就是可行的方法。
@dww 的建议很巧妙,可以回答您的问题。但是 class 的意义不是保证它的成员(插槽)将永远存在吗?如果你不关心,你可以使用 anything goes S3
classes 代替?对于 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"
如果这只是大小问题,为什么不直接做呢
x <- new('foo', a = rnorm(1e3L), b = rep('A', times = 1e3L))
x@b <- ""
format(object.size(x), units = 'auto')
#[1] "8.7 Kb"
对我来说,这显然是最好的解决方案,因为它很简单。
假设我定义了一个带有两个插槽 'a'
和 'b'
的 S4 class 'foo'
,并定义了 [=56= 的对象 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"
然后我想从 'foo'
'a'
setClass(Class = 'foo', slots = c(
b = 'character'
))
slotNames(x) # slot 'a' automatically removed!! wow!!!
我看到 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"
对..不知何故 'a'
仍然存在,但我无法对它做任何事情
head(x@a) # `'a'` is still there
rm(x@a) # error
x@a <- NULL # error
所以问题:我怎样才能 真正 从 x
中删除插槽 'a'
并 减小其大小 (这是我最关心的问题)?
非常感谢所有的回答!
以下解决方案的灵感来自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"
以下解决方案的灵感来自Robert Hijmans
setClass('foo1', contains = 'foo')
format(object.size(y2 <- as(x, 'foo1')), units = 'auto') # "8.5 Kb"
method::as
可能会做一些全面的检查,所以速度很慢
library(microbenchmark)
microbenchmark(trimS4slot(x), as(x, 'foo1')) # ?methods::as 10 times slower
插槽存储为属性。我们有几个选项可以将插槽转换为 NULL
.
选项 1:您可以使用 slot<-
中的 check=FALSE
参数将插槽分配为 NULL 而不会触发错误。
slot(x, 'a', check=FALSE) <- NULL
setClass(Class = 'foo', slots = c(b = 'character'))
format(object.size(x), units = 'auto')
# [1] "8.7 Kb"
但是,该属性并未完全 删除(它仍然存在,值为 [=15=]1NULL[=15=]1
)。发生这种情况是因为 C 函数 R_do_slot_assign
中的一行具有: if(isNull(value)) value = pseudo_NULL;
其中 pseudo_NULL 是“一个对象,用于表示为 NULL 的槽(这是一个属性不能)".
还应注意 ?slot
中的建议,即“用户不应在正常使用中设置 check=FALSE
,因为生成的对象可能无效。”在这种情况下应该不会引起任何问题,因为插槽会在之后立即被删除。尽管如此,在使用 check=False 标志时要谨慎,除非你确定你明白你在做什么。
选项2:更彻底的移除可以通过在class定义中移除槽后直接移除属性来实现:
setClass(Class = 'foo', slots = c(
b = 'character'
))
attr(x, 'a') <- NULL
format(object.size(x), units = 'auto')
# [1] "8.7 Kb"
但是,删除插槽是个好主意吗?
删除插槽有点像 hack,可能会在以后导致错误,例如如果调用的方法假定插槽存在。您可能会在自己的机器上针对特定用例执行此操作。但是将其作为生产代码发布到野外并不是一个好主意。在那种情况下,@RobertHijmans 的回答中的方法就是可行的方法。
@dww 的建议很巧妙,可以回答您的问题。但是 class 的意义不是保证它的成员(插槽)将永远存在吗?如果你不关心,你可以使用 anything goes S3
classes 代替?对于 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"
如果这只是大小问题,为什么不直接做呢
x <- new('foo', a = rnorm(1e3L), b = rep('A', times = 1e3L))
x@b <- ""
format(object.size(x), units = 'auto')
#[1] "8.7 Kb"
对我来说,这显然是最好的解决方案,因为它很简单。