在data.table中为多个列设置不同的属性

5

data.table 有优雅的 setattr 方法来向列中加入单个属性。有没有一种优雅的方式可以一步完成多个属性的叠加? 例如,假设一个data.table有很多列,我想给列x1分配两个属性,给列x3分配三个属性,就像下面的list中指定的那样:

a <- list(x1=list(label='X1', units='mm'),
          x3=list(label='X3', comment='collected remotely', format='type 3'))

我可以轻松编写处理 a 并调用 setattr 5 次以完成此操作的代码。但我希望有更好的方法。

2个回答

2
我不知道下面的代码是否非常优雅,但它能够工作。这是一个双重的*apply循环。
引用问题:

我可以轻松地编写处理a并调用setattr 5次以完成此操作的代码。但我希望有更好的方法。

问题在于setattr中的name必须是一个长度为1的字符字符串,因此setattr将始终需要被调用5次。在下面的代码中,这是通过一个双重循环来实现的。 示例data.table来自于help("setattr")中的第3个DT
library(data.table)

DT <- data.table(x1 = 1:3, y = 4:6, x3 = 7:9)
a <- list(x1=list(label='X1', units='mm'),
          x3=list(label='X3', comment='collected remotely', format='type 3'))

mapply(function(x, a){
  lapply(names(a), function(na) setattr(DT[[x]], na, a[[na]]))
}, names(a), a)

attributes(DT$x1)
#$label
#[1] "X1"
#
#$units
#[1] "mm"

attributes(DT$x3)
#$label
#[1] "X3"
#
#$comment
#[1] "collected remotely"
#
#$format
#[1] "type 3"

注意。为了避免循环输出的丑陋结果,将它们包裹在invisible中:

invisible(
  mapply(function(x, a){
    lapply(names(a), function(na) setattr(DT[[x]], na, a[[na]]))
  }, names(a), a)
)

编辑

以下代码更为简单易懂。

lapply(names(a), function(x){
  lapply(names(a[[x]]), function(y) setattr(DT[[x]], y, a[[x]][[y]]))
})

1
如果你或其他人想要一个额外的挑战:如果其中一个属性是“class”,并且存在列的类,则我们希望执行class(x) <- unique(c(class(x), newclass)) - Frank Harrell

2

这可能与您想要的输出有很大偏差,但是只是提出一个想法:因为setattr接受data.table,另一种选择可能是在data.table级别上设置属性,作为指向各个列的命名列表:

setattr(d, "all_attr", a)
str(d)
# Classes ‘data.table’ and 'data.frame':    3 obs. of  3 variables:
# $ x1: int  1 2 3
# $ y : int  4 5 6
# $ x3: int  7 8 9
# - attr(*, ".internal.selfref")=<externalptr> 
#   - attr(*, "all_attr")=List of 2
# ..$ x1:List of 2
# .. ..$ label: chr "X1"
# .. ..$ units: chr "mm"
# ..$ x3:List of 3
# .. ..$ label  : chr "X3"
# .. ..$ comment: chr "collected remotely"
# .. ..$ format : chr "type 3"

如果您想在单独的列级别设置属性,并且可以接受将属性作为嵌套列表,那么循环遍历列可能就足够了。
lapply(names(a), function(x) setattr(d[[x]], x, a[[x]]))
str(d)
# Classes ‘data.table’ and 'data.frame':    3 obs. of  3 variables:
# $ x1: int  1 2 3
# ..- attr(*, "x1")=List of 2
# .. ..$ label: chr "X1"
# .. ..$ units: chr "mm"
# $ y : int  4 5 6
# $ x3: int  7 8 9
# ..- attr(*, "x3")=List of 3
# .. ..$ label  : chr "X3"
# .. ..$ comment: chr "collected remotely"
# .. ..$ format : chr "type 3"
# - attr(*, ".internal.selfref")=<externalptr>

library(data.table)
d = data.table(x1 = 1:3, y = 4:6, x3 = 7:9)

尽管我已经为Hmisc包处理变量labelunits付出了很多努力,但有时我认为像你所做的将这些子集不变属性分离可能是一个更好的想法。这是一个值得深思的问题。 - Frank Harrell
谢谢您的反馈,Frank!也许您可以在问题中具体说明/阐述您的要求,例如使用答案中的玩具数据?祝好! - Henrik
这是一个非常不同的问题,但有时属性最好作为单独的对象保留,甚至不要放置在“外部”data.table属性中。例如,我有时认为变量标签应该在整个分析脚本中针对变量名称进行特定定义,而不管变量来自哪个数据表。然后,我会像上面的a一样定义一个对象,并让各种图形和制表函数在a中查找labelunits以形成轴标签和表行或列标题。 - Frank Harrell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接