Let's say I define an S4 class 'foo'
with two slots 'a'
and 'b'
, and define an object x
of class '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"
Then I want to remove slot 'a'
from the definition of 'foo'
setClass(Class = 'foo', slots = c(
b = 'character'
))
slotNames(x) # slot 'a' automatically removed!! wow!!!
I see that R automatically take cares my object x
and have the slot 'a'
removed. Nice! But wait, the size of object x
is not reduced.
format(object.size(x), units = 'auto') # still "16.5 Kb"
format(object.size(new(Class = 'foo', x)), units = 'auto') # still "16.5 Kb"
Right.. Somehow 'a'
is still there but I just cannot do anything to it
head(x@a) # `'a'` is still there
rm(x@a) # error
x@a <- NULL # error
So question: how can I really remove slot 'a'
from x
and have its size reduced (which is my primary concern)?
My deepest gratitude to all answers!
The following solution is inspired by 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"
The following solution is inspired by Robert Hijmans
setClass('foo1', contains = 'foo')
format(object.size(y2 <- as(x, 'foo1')), units = 'auto') # "8.5 Kb"
method::as
probably does some comprehensive checks, so it's quite slow though
library(microbenchmark)
microbenchmark(trimS4slot(x), as(x, 'foo1')) # ?methods::as 10 times slower
What @dww suggests is nifty, and answers your question. But isn't the point of a class that you are guaranteed that its members (the slots) will always be there? If you don't care about that you can use the anything goes S3
classes instead? With S4
, I would suggest a more formal approach like this:
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"
And if this is just about size, why not just do
x <- new('foo', a = rnorm(1e3L), b = rep('A', times = 1e3L))
x@b <- ""
format(object.size(x), units = 'auto')
#[1] "8.7 Kb"
To me this is clearly the best solution because of its simplicity.