动态地将列添加到xts对象

时间:2015-10-09 13:31:17

标签: r xts

如果您提前知道列的名称,则可以直接向xts对象添加列。例如,要添加名为“b”的列:

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非常大,上述方法是否会正常工作?)

2 个答案:

答案 0 :(得分:9)

最简单/最清楚的事情是在将新列转换为矩阵后将原始对象与新列合并(这样就可以设置列名)。

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对象上分配名称(和名称)会创建一个副本。您可以使用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 :(得分:1)

我认为没有好的选择,但是列名只是一个属性,因此修改起来很便宜,并且不会制作副本。 (编辑:呃 - 哦,刚看到我似乎在说与约书亚相反.--&gt;在评论中看到讨论。似乎dimnames.xts不只是设置一个属性,并且涉及复制基础数据,所以要小心。)

您也可以使用cbind()这是merge.xts的同义词,但是(AFAIK)它对您展示的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"

我还展示了一种更改列名的方法。如果您不知道它是第3列,那么通用方法是:colnames(x)[length(colnames(x))] = "real name"