2017-02-17 45 views
2

我認爲這個問題與R中Mutable Vs不可變對象的概念有關,它可能是一個「開始問題」。我遇到了這個問題,其中函數names()setnames()功能包data.table。我相信這是預期的行爲,但對我來說這是相當令人驚訝的,我相信這不僅與names()有關。返回R中Mutable或Immutable變量的函數

想象我有一個兩列A和B稱爲dt data.table:

dt <- data.table(a = 1:5, b= 1:5) 
oldNames <- names(dt) 

如果打印oldNames這顯然表明:

oldNames 
[1] "a" "b" 

但是,如果你恰克的dt名稱與setnames()

setnames(dt,oldNames,c("aNew","bNew")) 

變量oldNames的內容已更改。

oldNames 
[1] "aNew" "bNew" 

我知道,在Python這是一些數據類型(易變的),而不是在別人(的inmutable的)預期的行爲。在R中,是否也有這種雙子宮切開術?

對我而言,「預期」行爲將是變量oldNames存儲列的名稱,它不依賴於data.table的未來更改。例如,與length()功能不會發生這種情況:

L <- length(dt) 
L 
[1] 2 
dt[,c:=1:5] 
L 
[1] 2 

到有關此問題或解釋一些有用的信息的任何鏈接將非常感激,並會產生什麼方式代碼,以便oldNames不改變其dt修改後的內容。

+0

你是正確的,這是重複的,我認爲這是一般Data.Table沒有的R問題/行爲。 非常感謝。 –

回答

1

我相信這是由於data.table包的實現。在R,多個符號可以指向同樣的事情,但一般不會造成問題,因爲對象將在修改被複制,例如:

a <- c(1,2) 
b <- a 
# To check the memory address, 
# `a` and `b` are pointed to the same object since `b` does not modify `a`. 
pryr::address(a) 
[1] "0x1a735620" 
pryr::address(b) 
[1] "0x1a735620" 
# Then we modify `a` 
a <- c(1,3) 
# We will notice that the address of `a` has changed 
# since there are modifications, but `b` not. 
pryr::address(a) 
[1] "0x1a72f168" 
pryr::address(b) 
[1] "0x1a735620" 

從我有限的知識,在data.table包是一點點特殊的,因爲它會通過一些操作修改對象。見:

dt <- data.table(a = 1:5, b= 1:5) 
n1 <- names(dt) 

pryr::address(n1) 
[1] "0x18aeffe0" 

setnames(dt, c("a","b"), c("aa","bb")) 

n2 <- names(dt) 
pryr::address(n2) # identical to the address of `n1` 
[1] "0x18aeffe0" 

n1 
[1] "aa" "bb" 

我認爲data.table包沒有識別出有一個變量指向它的名稱屬性,從而導致問題。我會認爲它是一個錯誤,你可能想用「data.table」來標記問題。

在當前時間,您可以使用n <- c(names(dt))來存儲名稱,這樣R會考慮c()修改name屬性並將其存儲在不同的內存地址中。

順便說一句,R確實有可變對象,見Reference classR6 objects ;-)

問候;

更新:??

見data.table ::複製和data.table :: setnames

要引用data.table ::副本:?

一'dt_names = names(DT)'時可能需要'copy()'。由於 R的copy-on-modify,'dt_names'仍然指向 內存中與「名稱(DT)」相同的位置。因此,修改'DT'現在通過引用, 說通過添加一個新列,'dt_names'也將得到更新。爲了避免 這個,必須明確 copy:'dt_names < - copy(names(DT))'。

它們當然在R中不常見,data.table可以這樣做,因爲它使用R的C接口。

會議信息:

> sessionInfo() 
R version 3.3.2 (2016-10-31) 
Platform: x86_64-suse-linux-gnu (64-bit) 
Running under: openSUSE Tumbleweed 

locale: 
[1] LC_CTYPE=en_US.UTF-8  LC_NUMERIC=C    LC_TIME=en_US.UTF-8  
[4] LC_COLLATE=en_US.UTF-8  LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8 
[7] LC_PAPER=en_US.UTF-8  LC_NAME=C     LC_ADDRESS=C    
[10] LC_TELEPHONE=C    LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C  

attached base packages: 
[1] stats  graphics grDevices utils  datasets methods base  

other attached packages: 
[1] pryr_0.1.2   data.table_1.9.6 magrittr_1.5  personalutils_0.1.0 

loaded via a namespace (and not attached): 
[1] tools_3.3.2  Rcpp_0.12.9  stringi_1.1.2 codetools_0.2-15 
[5] stringr_1.1.0 chron_2.3-47 
+0

非常感謝您的回答。 當你說「_ data.table包沒有認識到有一個變量指向它的名稱屬性,從而導致problem_」是因爲我這是R包中的通常行爲? –

+0

非常感謝您的回答。 當你說「_ data.table包沒有識別出有一個指向它的名稱屬性的變量,從而導致問題_」,這是R包中的通常行爲?其實我對你的地址洞察力有點混淆。我檢查了'L'(length())對象的地址,並且它的地址不會改變(就像'names'對象發生的那樣),但是它的值,當我將新列添加到'dt'時,doesn'不改變!所以'L'指向內存中的相同位置,但它的值不會從2更改爲3。 –

+0

嘿貢薩洛,對不起,我在我的例子中犯了一個錯誤,你可以看到我更新的答案。使用'length()'和'names()'有點不同,使用'length()'就像使用'c(names())'一樣,它會做一些計算並生成一個新變量。 –