【问题标题】:Setting multiple and different attributes for columns of a data.table为 data.table 的列设置多个不同的属性
【发布时间】:2021-07-01 06:50:09
【问题描述】:

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 次的代码来完成此操作。但我希望有更好的方法。

【问题讨论】:

    标签: r attributes data.table attr


    【解决方案1】:

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

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

    问题是setattr 中的name 必须是长度为1 的字符串,所以setattr 总是要被调用5 次。在下面的代码中,这是伪装成双循环完成的。

    例子data.table来自help("setattr")中的第三个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]]))
    })
    

    【讨论】:

    • 如果您或其他任何人想要一个额外的挑战:如果其中一个属性是“类”并且列有一个现有的类,我们会想要class(x) &lt;- unique(c(class(x), newclass))
    【解决方案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 包中投入了所有精力来处理变量labels 和units,但有时我认为像你一样分离这些子集不变的属性已经做了可能是一个更好的主意。值得深思的好食物。
    • 感谢您的反馈,弗兰克!也许您也可以在问题中指定/详细说明您的要求,例如使用答案中的玩具数据?干杯
    • 这是一个非常不同的问题,但在某些情况下,最好将属性保存为单独的对象,甚至不将其放置为“外部”data.table 属性。例如,我有时认为变量标签在整个分析脚本中应该是特定于变量名称的,无论变量来自哪个数据表。然后我会定义一个像上面的a 这样的对象,并让各种图形和表格制作功能在a 中查找labelunits 以形成轴标签和表格行或列标题。
    猜你喜欢
    • 2014-09-28
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多