动态添加列到 xts 对象
Dynamically add column to xts object
如果您提前知道列的名称,则向 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
非常大,上述方法是否有效?)
easiest/clearest 要做的事情是将原始对象与新列合并,然后将新列转换为矩阵(以便您可以设置列名称)。
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)会创建一个副本。您可以通过使用 tracemem
和 gc
.
的输出来查看它。
> 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 的额外内存("max used (Mb)" 增加了该数量)。整个 xts 对象大约有 8MB,其中一半是 coredata
,另一半是 index
。所以额外使用的4MB内存是用来复制coredata
.
如果要避免复制,可以手动设置。但要小心,因为你可能会做一些会被 colnames<-.xts
.
中的 "checks" 捕获的事情
> 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
我相信没有好的选择,但列名只是一个属性,因此修改起来很便宜,并且不会进行复制。
(编辑: 呃哦,刚刚看到我好像在说与 Joshua 相反的内容。--> 请参阅评论中的讨论。似乎 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"
我还展示了一种更改列名称的方法。如果您不知道它是第 3 列,那么通用方法是:colnames(x)[length(colnames(x))] = "real name"
如果您提前知道列的名称,则向 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
非常大,上述方法是否有效?)
easiest/clearest 要做的事情是将原始对象与新列合并,然后将新列转换为矩阵(以便您可以设置列名称)。
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)会创建一个副本。您可以通过使用 tracemem
和 gc
.
> 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 的额外内存("max used (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
我相信没有好的选择,但列名只是一个属性,因此修改起来很便宜,并且不会进行复制。
(编辑: 呃哦,刚刚看到我好像在说与 Joshua 相反的内容。--> 请参阅评论中的讨论。似乎 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"
我还展示了一种更改列名称的方法。如果您不知道它是第 3 列,那么通用方法是:colnames(x)[length(colnames(x))] = "real name"