2015-10-09 106 views
7

如果您提前知道列的名稱,那麼向xts對象添加列是非常簡單的。例如,要添加名爲「B」柱:將列動態添加到xts對象

n <- 5 
x <- merge(xts(order.by = as.Date('2015-1-1') + 1:n), a = rnorm(n)) 
x$b <- rnorm(n) 

添加動態命名的列(即,其名稱僅在運行時是已知的一列)是很難:

new.col.name <- 'c' # known only at runtime 
x[, new.col.name] <- rnorm(n) # this generates an error 

一種方法是添加一個臨時名稱的列,然後重命名它:

stopifnot(!('tmp' %in% names(x))) 
x$tmp <- rnorm(n) 
names(x)[names(x) == 'tmp'] <- new.col.name 

有沒有更好的方法來做到這一點? (此外,沒有分配到對象的一個​​副本的XTS對象的結果names正在取得?因此,例如,將上述方法工作做好,如果n非常大?)

回答

8

最簡單/最清楚的事在將新列轉換爲矩陣(以便設置列名稱)後,將原始對象與新列合併。

set.seed(21) 
newData <- rnorm(n) 
x1 <- merge(x, matrix(newData, ncol=1, dimnames=list(NULL, new.col.name))) 
# another way to do the same thing 
dim(newData) <- c(nrow(x), 1) 
colnames(newData) <- new.col.name 
x2 <- merge(x, newData) 

要回答你的第二個問題:是的,一個XTS對象分配名稱(和colnames)創建一個副本。您可以通過使用tracememgc的輸出來查看它。

> R -q # new R session 
R> x <- xts::.xts(1:1e6, 1:1e6) 
R> tracemem(x) 
[1] "<0x2892400>" 
R> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 259260 13.9  592000 31.7 350000 18.7 
Vcells 1445207 11.1 4403055 33.6 3445276 26.3 
R> colnames(x) <- "hi" 
tracemem[0x2892400 -> 0x24c1ad0]: 
tracemem[0x24c1ad0 -> 0x2c62d30]: colnames<- 
tracemem[0x2c62d30 -> 0x3033660]: dimnames<-.xts dimnames<- colnames<- 
tracemem[0x3033660 -> 0x3403f90]: dimnames<-.xts dimnames<- colnames<- 
tracemem[0x3403f90 -> 0x37d48c0]: colnames<- dimnames<-.xts dimnames<- colnames<- 
tracemem[0x37d48c0 -> 0x3033660]: dimnames<-.xts dimnames<- colnames<- 
R> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 259696 13.9  592000 31.7 350000 18.7 
Vcells 1445750 11.1 4403055 33.6 3949359 30.2 
R> print(object.size(x), units="Mb") 
7.6 Mb 

你可以看到colnames<-調用導致使用的額外內存4MB〜(以下簡稱「使用(MB)最大」增加的量)。整個xts對象是〜8MB,其中一半是coredata,另一半是index。所以使用4MB的額外內存是複製coredata

如果您想避免複製,可以手動設置。但要小心,因爲你可以做一些其他事情,否則會被colnames<-.xts中的「檢查」所捕獲。

> R -q # new R session 
R> x <- xts::.xts(1:1e6, 1:1e6) 
R> tracemem(x) 
[1] "<0x2cc5330>" 
R> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 256397 13.7  592000 31.7 350000 18.7 
Vcells 1440915 11.0 4397699 33.6 3441761 26.3 
R> attr(x, 'dimnames') <- list(NULL, "hi") 
tracemem[0x2cc5330 -> 0x28f4a00]: 
R> gc() 
      used (Mb) gc trigger (Mb) max used (Mb) 
Ncells 256403 13.7  592000 31.7 350000 18.7 
Vcells 1440916 11.0 4397699 33.6 3441761 26.3 
R> print(object.size(x), units="Mb") 
7.6 Mb 
1

我相信沒有很好的選擇,但列名只是一個屬性,所以很便宜修改,不會生成副本進行。 (編輯:呃,哦,剛剛看到我似乎在說與喬舒亞相反.-->看到討論的意見。似乎dimnames.xts不僅僅是設置一個屬性,並涉及複製底層數據,所以。要小心)

您還可以使用cbind(),這是merge.xts的代名詞,但(據我所知),它不提供優勢的x$b方法,你發現:

n <- 5 
x <- merge(xts(order.by = as.Date('2015-1-1') + 1:n), a = rnorm(n)) 
x$b <- rnorm(n) 
x = cbind(x, c = rnorm(n)) 
colnames(x)[3] = "real name" 

我也表現出一種方法更改列名稱。如果您不知道它是第三列,那麼通用方法是:colnames(x)[length(colnames(x))] = "real name"

+0

正如我在我的回答中所說的,設置'colnames'確實會創建副本。例如:'x < - .xts(1,1); tracemem(X); colnames(x)< - 「hi」' –

+0

我正在編輯,而你添加了那條評論@JoshuaUlrich實際的矩陣數據是不是被複制的呢? –

+0

實際的矩陣數據很難被複制。我已經在我的答案中添加了一些細節來演示。 –