gpt4 book ai didi

r - 使用 colnames() 设置变量,使用 := operator, 变量更新 data.table 是静默更新的?

转载 作者:行者123 更新时间:2023-12-03 23:34:03 30 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





Why does data.table update names(DT) by reference, even if I assign to another variable?

(1 个回答)


8年前关闭。




嗯,这有点奇怪......似乎通过使用 := 运算符在 data.table 中创建一个新列,先前分配的变量(使用 colnames 创建)会默默更改。

这是预期的行为吗?如果不是有什么错?

# Lets make a simple data table
require(data.table)
dt <- data.table(fruit=c("apple","banana","cherry"),quantity=c(5,8,23))
dt
fruit quantity
1: apple 5
2: banana 8
3: cherry 23

# and assign the column names to a variable
colsdt <- colnames(dt)
str(colsdt)
chr [1:2] "fruit" "quantity"

# Now let's add a column to the data table using the := operator
dt[,double_quantity:=quantity*2]
dt
fruit quantity double_quantity
1: apple 5 10
2: banana 8 16
3: cherry 23 46

# ... and WITHOUT explicitly changing 'colsdt', let's take another look:
str(colsdt)
chr [1:3] "fruit" "quantity" "double_quantity"

# ... colsdt has been silently updated!

为了比较,我想看看通过 data.frame 方法添加一个新列是否有同样的问题。它没有:
dt$triple_quantity=dt$quantity*3
dt
fruit quantity double_quantity triple_quantity
1: apple 5 10 15
2: banana 8 16 24
3: cherry 23 46 69

# ... again I make no explicit changes to colsdt, so let's take a look:
str(colsdt)
chr [1:3] "fruit" "quantity" "double_quantity"

# ... and this time it is NOT silently updated

那么这是 data.table := 运算符的错误还是预期的行为?

谢谢!

最佳答案

简答,使用 copy

colsdt <- copy(colnames(dt))

那你们都很好。
dt[,double_quantity:=quantity*2]
str(colsdt)
# chr [1:2] "fruit" "quantity"

一般情况下(即在基数 R 中),赋值运算符 <-为对象赋值时创建对象的新副本。即使分配给相同的对象名称时也是如此,如 x <- x + 1 ,或者更贵, DF$newCol <- DF$a + DF$b .对于大型对象(想想 100K+ 行、数十或数百列。如果更多列更糟糕),这可能会变得非常昂贵。
data.table , 通过 纯魔法 (阅读:C 代码)避免了这种开销。相反,它所做的是设置一个指向
已存储对象值的相同内存位置。这就是提供巨大效率和加速提升的原因。

但这也意味着您经常拥有可能看起来完全不同和独立的对象的对象
事实上是一回事

这就是 copy进来。它创建一个对象的新副本,而不是通过引用传递。

关于为什么会发生这种情况的更多细节。

注意:我非常松散地使用术语“源”和“目的地”,它们指的是分配关系 destination <- source
这实际上是预期的行为,诚然有点混淆。

在基地 R , 当您通过 <- 分配时,这两个对象指向相同的内存位置,直到其中之一发生变化。
这种处理内存的方式有很多好处,即只要两个对象具有相同的确切值,就不需要重复内存。尽可能长时间地推迟此步骤。
a <- 1:5
b <- a
.Internal(inspect(a)) # @11a5e2a88 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,2,3,4,5
.Internal(inspect(b)) # @11a5e2a88 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,2,3,4,5
^^^^ Notice the same memory location

一旦两个对象中的任何一个发生变化,那么这种“联系”就会被打破。也就是说,更改“源”或“目标”对象将导致该对象被重新分配到新的内存位置。
a[[3]] <- a[[3]] + 1
.Internal(inspect(a)) # @11004bc38 14 REALSXP g0c4 [NAM(1)] (len=5, tl=0) 1,2,4,4,5
^^^^ New Location
.Internal(inspect(b)) # @11a5e2a88 13 INTSXP g0c3 [NAM(2)] (len=5, tl=0) 1,2,3,4,5
^^^^^ Still same as it was before;
note the actual value. This is where `a` _had_ been
data.table中的问题情况是我们很少重新分配实际的 data.table 对象。
请注意,如果我们修改“目标”对象,则它会从该内存位置移动(复制)。
colsdt <- colnames(dt)
.Internal(inspect(colnames(dt))) # @114859280 16 STRSXP g0c7 [MARK,NAM(2)] (len=2, tl=100)
.Internal(inspect(colsdt)) # @114859280 16 STRSXP g0c7 [MARK,NAM(2)] (len=2, tl=100)
^^^^ Notice the same memory location
# insiginificant change
colsdt[] <- colsdt
.Internal(inspect(colsdt)) # @100aa4a40 16 STRSXP g0c2 [NAM(1)] (len=2, tl=100)

# we can test the original issue from the OP:
dt[, newCol := quantity*2]
str(colnames(dt)) # chr [1:3] "fruit" "quantity" "newCol"
str(colsdt) # chr [1:2] "fruit" "quantity"

要避免的情况:

但是,因为在使用 data.table 时,我们(几乎)总是通过引用进行修改,这可能会导致意想不到的结果。即,以下情况:
  • 我们使用标准 <- 从 data.table 对象分配赋值运算符
  • 然后随后我们更改“源”数据表的值
  • 我们期望(并且我们的代码可能依赖于)“目标”对象仍然具有先前分配给它的值。

  • 这当然会引起问题。
    data.table是一个非常强大的包。它的力量来源是它的长发,因为它尽可能避免复制。

    最佳实践:

    这将责任转移到用户在复制和期望复制时要慎重和明智。

    换句话说,最佳实践是:
    当您希望存在副本时,请使用复制功能。

    关于r - 使用 colnames() 设置变量,使用 := operator, 变量更新 data.table 是静默更新的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16542994/

    30 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com