首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >锁定data.table表的内容

问锁定data.table表的内容
EN

Stack Overflow用户
提问于 2018-05-29 19:38:14
回答 3查看 433关注 0票数 4

可以使data.table成为静态的(即不可更新的)吗?使用lockBinding()函数可以防止变量被重新赋值,但是仍然可以编辑数据表的列。示例:

代码语言:javascript
复制
> dt = data.table( x = 1:5 )
> lockBinding( "dt", env = environment() )
> dt = 1
Error: cannot change value of locked binding for 'dt'
> dt[ , x := 1 ]
> dt[ , x ]
[1] 1 1 1 1 1

我猜这与数据表的引用方式有关,但是,能够锁定数据表的内容也会很有用。(我经常有共享的引用表,我不想不小心更新这些表。)

EN

回答 3

Stack Overflow用户

发布于 2018-05-29 20:59:24

这有点棘手。一种方法是劫持[函数,禁止在对象上使用:=。如果我们想要绑定一个data.table,我们可以给它添加一个类,如下所示:

代码语言:javascript
复制
boundDT <- function(dt){
  class(dt) <- c("bound.data.table", class(dt))
  dt
}

结果:

代码语言:javascript
复制
library(data.table)
dt = data.table( x = 1:5 )
bound <- boundDT(dt)
class(bound)
[1] "bound.data.table" "data.table"       "data.frame"   

如果我们创建一个新的索引函数来处理bound.data.table类,我们可以做我们的事情:

代码语言:javascript
复制
`[.bound.data.table` <- function(dt, ...){
  if(any(unlist(sapply(match.call()[-(1:2)], function(x) if(length(x) > 1)as.character(x[1]) == ":=")))){
    stop("Can't use `:=` on this object.")
  }
  class(dt) <- class(dt)[-1]
  dt[...]
}

这将检查调用中是否使用了函数:=,如果使用了,则抛出错误。否则,它将删除data.table内部副本上的绑定类,并调用常规的[函数。

代码语言:javascript
复制
bound[, x := 1]
 Error in `[.bound.data.table`(bound, , `:=`(x, 1)) : 
  Can't use `:=` on this object. 
bound[, x]
[1] 1 2 3 4 5

这是丑陋的,但它似乎是有效的。

需要注意的是:

在联接中使用:=时,如果绑定表不是基表,则此操作不起作用:

代码语言:javascript
复制
dt = data.table( x = 1:5 , y = 5:1)
bound <- boundDT(dt)
dt[bound, y := 1, on = .(x = x)]
bound
   x y
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1

但是:

代码语言:javascript
复制
bound[dt, y := 1, on = .(x = x)]
 Error in `[.bound.data.table`(bound, dt, `:=`(y, 1), on = .(x = x)) : 
  Can't use `:=` on this object.

阻止使用set*

随着围绕:=操作符的大多数问题的解决,我们可以专注于防止在我们的对象上使用set*。

当使用绑定的data.table时,在提供data.table之前,我们可以检查调用堆栈以查看是否有任何set*函数。

代码语言:javascript
复制
bindDT <- function(dt){
  bound <- boundDT(dt)
  function(){
    calls <- sys.calls()
    forbidden <- c("set", "set2key", "set2keyv", "setattr", "setcolorder", "setdiff", "setDT", 
                   "setDTthreads", "setequal", "setindex", "setindexv", "setkey", "setkeyv", 
                   "setnames", "setNumericRounding", "setorder", "setorderv")
    matches <- unlist(lapply(calls, function(x) as.character(x)[1] %in% forbidden))
    if(any(matches)){
      stop(paste0("Can't use function ", calls[[which(matches)[1]]][1], " on bound data.table."))
    }

    bound
  }
}

此函数像以前一样绑定data.table,但它返回的不是此函数,而是一个函数。调用此函数时,将检查调用堆栈中是否有set*函数,如果发现任何错误,则抛出错误。我从data.table帮助页面获得了这个列表,所以这个列表应该是完整的。

通过使用pryr,可以使用活动绑定来避免每次使用时都将data.table作为函数进行调用

代码语言:javascript
复制
library(data.table)
library(pryr)

dt = data.table( x = 1:5 , y = 5:1)
bound %<a-% (bindDT(dt))()

setkey(bound, x)
Error in (bindDT(dt))() : Can't use function setkey on bound data.table.
票数 4
EN

Stack Overflow用户

发布于 2018-05-29 19:55:51

您可以将data.table放入函数中:

代码语言:javascript
复制
library("data.table")
dt = function() data.table( x = 1:5 )
dt() = 1 ### error
dt()[ , x := 1 ]
dt()[ , x ]
# > dt()[ , x ]
# [1] 1 2 3 4 5

下面是一个不会每次都生成data.table的变体:

代码语言:javascript
复制
library("data.table")
dt0 = data.table( x = 1:5 )
dt <- function() copy(dt0)
dt() = 1
dt()[ , x := 1 ]
dt()[ , x ]

我对我的解决方案不太满意:每当用户想要使用它时,它都会复制data.table。每次要使用未更改的data.table时,都必须通过调用函数dt()来执行此操作

票数 1
EN

Stack Overflow用户

发布于 2018-05-31 09:40:42

代码语言:javascript
复制
attr(dt, ".data.table.locked") = TRUE

将锁定大多数data.table操作。尽管如此,一些操作(setnames)仍然可能偷偷过去。但是,请注意,这是data.table的一个未记录的内部功能,旨在用于其他目的。因此,如果您尝试更改锁定的表,则收到的错误消息看起来会很奇怪和令人困惑。而且不能保证它在该包的后续版本中将如何执行。

如果你决定走这条路,你也应该在之后使用lockBinding来锁定基本的R类型的操作。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50583429

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档