2015-10-09 14 views
7

Dodawanie kolumny do obiektu Xts jest proste, jeśli znasz nazwę kolumny przed czasem. Na przykład, aby dodać kolumnę o nazwie „B”:Dynamicznie dodaj kolumnę do obiektu xts

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

Dodawanie dynamicznie nazwie kolumny (czyli kolumna którego nazwisko jest znane tylko w środowisku wykonawczym) jest trudniejsze:

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

Jedno podejście jest dodanie kolumny z tymczasową nazwą, a następnie zmiana jej nazwy:

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

Czy istnieje lepszy sposób na zrobienie tego? (Czy przypisanie do obiektu names obiektu Xts spowoduje utworzenie kopii obiektu? Na przykład, czy powyższe podejście zadziałałoby dobrze, gdyby n było bardzo duże?)

Odpowiedz

8

Najprostsza/najczystsza rzecz do zrobienia łączy oryginalny obiekt z nową kolumną (kolumnami) po przekonwertowaniu nowej kolumny (kolumn) na macierz (aby można było ustawić nazwę kolumny).

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) 

Aby odpowiedzieć na drugie pytanie: tak, przypisanie nazw (i nazw) do obiektu xts powoduje utworzenie kopii. Możesz to zobaczyć, używając tracemem i wyjścia z 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 

Można zobaczyć wywołanie colnames<- powoduje ~ 4MB dodatkowej pamięci do wykorzystania („max używany (Mb)” wzrosła o tę kwotę). Cały obiekt Xts to ~ 8MB, z czego połowa to coredata, a druga połowa to index. Tak więc 4 MB dodatkowej pamięci służy do kopiowania coredata.

Jeśli chcesz uniknąć kopiowania, możesz ustawić go ręcznie. Ale bądź ostrożny, ponieważ możesz zrobić coś, co w przeciwnym razie zostanie złapany przez "kontrole" w 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

Uważam, że nie ma dobrej alternatywy, ale nazwy kolumn są tylko atrybutami, więc są tanie w modyfikacji i nie zostaną wykonane kopie. (EDIT: uh-oh, właśnie widziałem, że wydaje się być przeciwieństwem Joshua. -> Zobacz dyskusję w komentarzach. Wygląda na to, że dimnames.xts robi coś więcej niż tylko ustawienie atrybutu i wiąże się z kopiowaniem podstawowych danych, więc . uważać)

można również użyć cbind(), która jest synonimem merge.xts, ale (AFAIK) nie zapewnia przewagę metody x$b pokazałeś:

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" 

ja również pokazał jedną stronę zmienić nazwę kolumny. Jeśli nie wiesz, że jest to trzecia kolumna, to podejście ogólne to: colnames(x)[length(colnames(x))] = "real name"

+0

Jak już powiedziałem w mojej odpowiedzi, ustawienie "colnames" powoduje tworzenie kopii. Na przykład: 'x <- .xts (1,1); tracemem (x); colnames (x) <- "hi" ' –

+0

Edytowałem, podczas dodawania tego komentarza @JoshuaUlrich Faktyczne dane macierzy nie są kopiowane, czy to prawda? –

+0

Rzeczywiste dane matrycy są niestety kopiowane. Dodałem trochę szczegółów do mojej odpowiedzi, aby zademonstrować. –