tbl_df is transformed as list in S4 class

2020-07-20 05:16发布

问题:

When I tried to use tbl_df in S4 classes, tbl_df slots seems to be transformed into list.

library('tibble')
setOldClass(c('tbl_df', 'tbl', 'data.frame'))
setClass(Class = 'TestClass', slots = c(name = 'character'), contains = 'tbl_df')

tmp1 <- new('TestClass', tibble(x = 1:5, y = 1, z = x ^ 2 + y), name = 'firsttest')
tmp1@.Data
[[1]]
[1] 1 2 3 4 5

[[2]]
[1] 1 1 1 1 1

[[3]]
[1]  2  5 10 17 26

Can I visit the tmp1@.Data just like a tbl_df object? like

tmp1@.Data
# A tibble: 5 x 3
      x     y     z
* <int> <dbl> <dbl>
1     1     1     2
2     2     1     5
3     3     1    10
4     4     1    17
5     5     1    26

回答1:

S3 objects, just for simplification, are lists with an special attribute "class" which is used to call the correct generic function. print is a generic function that is being called when R outputs the tibble object.

library(tibble)
tb <- tibble(x = 1:5, y = 1, z = x ^ 2 + y)

dput(tb)
#structure(list(x = 1:5, y = c(1, 1, 1, 1, 1), z = c(2, 5, 10, 
#17, 26)), row.names = c(NA, -5L), class = c("tbl_df", "tbl", 
#"data.frame"))

attributes(tb)
#$`names`
#[1] "x" "y" "z"
#
#$row.names
#[1] 1 2 3 4 5
#
#$class
#[1] "tbl_df"     "tbl"        "data.frame"

When you create an S4 class using an S3 parent class, R only store the list on the .Data slot. R still keeps the attributes of the S3 objects but not in the .Data slot. When you print the TestClass, you get the tibble output plus the S4 slots. If just want the S3 object you can use as(object,"S3").

setOldClass(c('tbl_df', 'tbl', 'data.frame'))
setClass(Class = 'TestClass', slots = c(name = 'character'), contains = 'tbl_df')
tmp1 <- new('TestClass', tibble(x = 1:5, y = 1, z = x ^ 2 + y), name = 'firsttest1')
tmp1
#Object of class "TestClass"
## A tibble: 5 x 3
#      x     y     z
#* <int> <dbl> <dbl>
#1     1     1     2
#2     2     1     5
#3     3     1    10
#4     4     1    17
#5     5     1    26
#Slot "name":
#[1] "firsttest1"

attributes(tmp1)
#$`names`
#[1] "x" "y" "z"
#
#$row.names
#[1] 1 2 3 4 5
#
#$.S3Class
#[1] "tbl_df"     "tbl"        "data.frame"
#
#$name
#[1] "firsttest1"
#
#$class
#[1] "TestClass"
#attr(,"package")
#[1] ".GlobalEnv"

as(tmp1,"S3")
## A tibble: 5 x 3
#      x     y     z
#* <int> <dbl> <dbl>
#1     1     1     2
#2     2     1     5
#3     3     1    10
#4     4     1    17
#5     5     1    26


回答2:

Use contains = class(tibble()) in setClass(). More details see https://github.com/tidyverse/tibble/issues/618



标签: r s4 tibble