亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

準確地理解data.table何時是對另一個data.table的引用

準確地理解data.table何時是對另一個data.table的引用

瀟瀟雨雨 2019-06-05 11:15:00
我很難理解data.table..有些操作似乎“打破”了引用,我想確切地了解發生了什么。關于創建data.table從另一個data.table(經<-,然后通過以下方法更新表:=,原來的表也被修改了。這是預期的,如下所示:?data.table::copy和StackOverflow:pass-by-reference-the-operator-in-the-data-table-package下面是一個例子:library(data.table)DT <- data.table(a=c(1,2), b=c(11,12))print(DT)#      a  b# [1,] 1 11# [2,] 2 12newDT <- DT         # reference, not copynewDT[1, a := 100] # modify new DTprint(DT)           # DT is modified too.#        a  b# [1,] 100 11# [2,]   2 12但是,如果我插入一個非-:=之間的基礎修改<-任務和:=上面的線,DT現在不再修改:DT = data.table(a=c(1,2), b=c(11,12))newDT <- DT         newDT$b[2] <- 200  # new operationnewDT[1, a := 100]print(DT)#      a  b# [1,] 1 11# [2,] 2 12所以看起來newDT$b[2] <- 200行在某種程度上破壞了引用。我猜想這會以某種方式調用一個副本,但我想完全了解R是如何處理這些操作的,以確保我不會在代碼中引入潛在的bug。如果有人能向我解釋這件事,我會非常感激的。
查看完整描述

2 回答

?
肥皂起泡泡

TA貢獻1829條經驗 獲得超6個贊

是的,它是在R中使用的<-(或=或->)的副本。整體對象。您可以使用tracemem(DT)和.Internal(inspect(DT)),如下所示。這個data.table特征:=和set()通過引用將它們傳遞給任何對象。因此,如果該對象以前被復制(通過subas簽名)<-或顯式copy(DT))然后是通過引用修改的副本。


DT <- data.table(a = c(1, 2), b = c(11, 12)) 

newDT <- DT 


.Internal(inspect(DT))

# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)

#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2

#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12

# ATTRIB:  # ..snip..


.Internal(inspect(newDT))   # precisely the same object at this point

# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)

#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2

#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12

# ATTRIB:  # ..snip..


tracemem(newDT)

# [1] "<0x0000000003b7e2a0"


newDT$b[2] <- 200

# tracemem[0000000003B7E2A0 -> 00000000040ED948]: 

# tracemem[00000000040ED948 -> 00000000040ED830]: .Call copy $<-.data.table $<- 


.Internal(inspect(DT))

# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),TR,ATT] (len=2, tl=100)

#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2

#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12

# ATTRIB:  # ..snip..


.Internal(inspect(newDT))

# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)

#   @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2

#   @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,200

# ATTRIB:  # ..snip..

注意,即使是a向量被復制(不同的十六進制值表示向量的新副本),盡管a沒有改變。甚至整個b被復制,而不僅僅是更改需要更改的元素。對于大型數據來說,這是很重要的避免,以及為什么:=和set()被介紹給data.table.


現在,用我們的拷貝newDT我們可以參考修改:


newDT

#      a   b

# [1,] 1  11

# [2,] 2 200


newDT[2, b := 400]

#      a   b        # See FAQ 2.21 for why this prints newDT

# [1,] 1  11

# [2,] 2 400


.Internal(inspect(newDT))

# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)

#   @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2

#   @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,400

# ATTRIB:  # ..snip ..

注意,所有3個十六進制值(列點的向量,以及2列中的每一列)都保持不變。因此,它確實是通過引用而修改的,根本沒有副本。


或者,我們可以修改原始DT引用:


DT[2, b := 600]

#      a   b

# [1,] 1  11

# [2,] 2 600


.Internal(inspect(DT))

# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)

#   @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2

#   @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,600

#   ATTRIB:  # ..snip..

這些十六進制值與我們看到的原始值相同。DT上面。類型example(copy)有關更多示例,請使用tracemem以及與.的比較data.frame.


順便說一下,如果你tracemem(DT)然后DT[2,b:=600]你會看到一份報告。的前10行的副本。print方法可以。用invisible()或在函數或腳本中調用時,print方法不被調用。


所有這一切也適用于內部功能;即,:=和set()不要在寫時復制,即使在函數中也是如此。如果需要修改本地副本,請調用x=copy(x)在函數開始的時候。但是,記住data.table適用于大數據(以及小數據的快速編程優勢)。我們故意不想復制大對象(永遠)。因此,我們不需要考慮通常的3*工作記憶因素的經驗法則。我們試著只需要一個列那么大的工作記憶(即工作記憶系數為1/nol,而不是3)。


查看完整回答
反對 回復 2019-06-05
?
繁花如伊

TA貢獻2012條經驗 獲得超12個贊

簡單總結一下。

<-帶著data.table就像基本;也就是說,在完成以下操作之前,將不進行復制。<-(例如更改列名或更改元素,如DT[i,j]<-v)。然后,它獲取整個對象的副本,就像基一樣。這就是所謂的抄寫。我想,應該更好地稱為“仿冒”!當您使用“特殊”時,它不會復制。:=運算符,或set*data.table..如果您有大量數據,您可能希望使用它們。:=set*不會復制data.table甚至在職能范圍內。

給定此示例數據:

DT <- data.table(a=c(1,2), b=c(11,12))

以下只是“綁定”另一個名稱DT2綁定到當前綁定到名稱的相同數據對象。DT :

DT2 <- DT

這永遠不會復制,也不會在基礎上復制。它只是標記數據對象,以便R知道兩個不同的名稱(DT2DT)指向同一個對象。所以R需要復制對象,如果兩者都是副簽名以后的事。

那是完美的data.table也是。這個:=不是為了這么做。因此,下面是一個故意的錯誤,因為:=不只是用于綁定對象名稱:

DT2 := DT    # not what := is for, not defined, gives a nice error

:=是為了次簽署參考一下。但你不像你在基地里那樣使用它:

DT[3,"foo"] := newvalue    # not like this

你就這樣用它:

DT[3,foo:=newvalue]    # like this

改變了DT參考一下。假設您添加了一個新列new通過引用數據對象,沒有必要這樣做:

DT <- DT[,new:=1L]

因為RHS已經改變了DT參考一下。額外的DT <-就是誤解了什么:=的確如此。你可以把它寫在那里,但它是多余的。

DT因引用而更改:=,即使在職能范圍內:

f <- function(X){
    X[,new2:=2L]
    return("something else")}f(DT)   # will change DTDT2 <- DT
f(DT)   # will change both DT and DT2 (they're the same data object)

data.table是用于大型數據集的,請記住。如果你有20 GBdata.table在記憶中,你需要一個方法來做到這一點。這是一個非常慎重的設計決策data.table.

當然可以復制。您只需要告訴data.table,您肯定要復制您的20 GB數據集,方法是使用copy()職能:

DT3 <- copy(DT)   # rather than DT3 <- DTDT3[,new3:=3L]     # now, this just changes DT3 because it's a copy, not DT too.

為了避免復制,不要使用基類型分配或更新:

DT$new4 <- 1L                 # will make a copy so use :=attr(DT,"sorted") <- "a"      # will make a copy use setattr()

如果您希望確保您正在通過引用進行更新,請使用.Internal(inspect(x))然后看看成分的內存地址值(見MatthewDowle的答案)。

寫字:=在……里面j這樣你就可以參考按組..可以按組按引用添加新列。所以這就是為什么:=在里面是這樣做的[...] :

DT[, newcol:=mean(x), by=group]


查看完整回答
反對 回復 2019-06-05
  • 2 回答
  • 0 關注
  • 830 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號