2011-03-16 16 views
8

Jeśli za pomocą plasterka tabeli, używając nazw kolumn, R przydziela pamięć do przechowywania plasterka w nowej lokalizacji? W szczególności mam tabelę z kolumnami depth1 i depth2, między innymi. Chcę dodać kolumny, które zawierają max i min dwóch. Mam 2 podejścia:Czy plasterki tabeli zajmują pamięć w R?

dd = dat[,c("depth1","depth2")] 
dat$mindepth = apply(dd,1,min) 
dat$maxdepth = apply(dd,1,max) 
remove(dd) 

lub

dat$mindepth = apply(dat[,c("depth1","depth2")],1,min) 
dat$maxdepth = apply(dat[,c("depth1","depth2")],1,max) 

Jeśli nie używam się nową pamięć, wolałbym wziąć plasterek tylko raz, w przeciwnym razie chciałbym zapisać realokacji. Który jest lepszy? Problemy z pamięcią mogą mieć krytyczne znaczenie podczas pracy z dużymi zbiorami danych, więc nie mów tego z korzeniami wszystkich złych memów.

+5

Poddanie prawie wszystkiego w R tworzy kopię. Istnieje kilka wyjątków w przesyłanych pakietach. – hadley

+0

@hadley - czy chcesz opublikować to jako odpowiedź, aby można było zaakceptować itp. Tylko dla rekordów ...? –

Odpowiedz

6

wiem, że to nie ma rzeczywiście odpowiedzieć główną myśl pytanie (@hadley zrobiła i że zasługuje na kredyt), ale istnieją inne opcje dla tych, sugerujesz. Tutaj możesz użyć pmin() i pmax() jako innego rozwiązania, a używając with() lub within() możemy to zrobić bez wyraźnego podzbioru, aby utworzyć dd.

R> set.seed(1) 
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10)) 
R> dat <- within(dat, mindepth <- pmin(depth1, depth2)) 
R> dat <- within(dat, maxdepth <- pmax(depth1, depth2)) 
R> 
R> dat 
     depth1 depth2 mindepth maxdepth 
1 0.26550866 0.2059746 0.20597457 0.2655087 
2 0.37212390 0.1765568 0.17655675 0.3721239 
3 0.57285336 0.6870228 0.57285336 0.6870228 
4 0.90820779 0.3841037 0.38410372 0.9082078 
5 0.20168193 0.7698414 0.20168193 0.7698414 
6 0.89838968 0.4976992 0.49769924 0.8983897 
7 0.94467527 0.7176185 0.71761851 0.9446753 
8 0.66079779 0.9919061 0.66079779 0.9919061 
9 0.62911404 0.3800352 0.38003518 0.6291140 
10 0.06178627 0.7774452 0.06178627 0.7774452 

Możemy spojrzeć na ile kopiowanie idzie z tracemem() ale tylko jeśli R został skompilowany z następującą opcję konfiguracji aktywowane --enable-memory-profiling.

R> set.seed(1) 
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10)) 
R> tracemem(dat) 
[1] "<0x2641cd8>" 
R> dat <- within(dat, mindepth <- pmin(depth1, depth2)) 
tracemem[0x2641cd8 -> 0x2641a00]: within.data.frame within 
tracemem[0x2641a00 -> 0x2641878]: [<-.data.frame [<- within.data.frame within 
R> tracemem(dat) 
[1] "<0x2657bc8>" 
R> dat <- within(dat, maxdepth <- pmax(depth1, depth2)) 
tracemem[0x2657bc8 -> 0x2c765d8]: within.data.frame within 
tracemem[0x2c765d8 -> 0x2c764b8]: [<-.data.frame [<- within.data.frame within 

Widzimy więc, że R kopiowane dat dwukrotnie podczas każdego within() rozmowy. Porównaj to z twoich dwóch sugestie:

R> set.seed(1) 
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10)) 
R> tracemem(dat) 
[1] "<0x2e1ddd0>" 
R> dd <- dat[,c("depth1","depth2")] 
R> tracemem(dd) 
[1] "<0x2df01a0>" 
R> dat$mindepth = apply(dd,1,min) 
tracemem[0x2df01a0 -> 0x2cf97d8]: as.matrix.data.frame as.matrix apply 
tracemem[0x2e1ddd0 -> 0x2cc0ab0]: 
tracemem[0x2cc0ab0 -> 0x2cc0b20]: $<-.data.frame $<- 
tracemem[0x2cc0b20 -> 0x2cc0bc8]: $<-.data.frame $<- 
R> tracemem(dat) 
[1] "<0x26b93c8>" 
R> dat$maxdepth = apply(dd,1,max) 
tracemem[0x2df01a0 -> 0x2cc0e30]: as.matrix.data.frame as.matrix apply 
tracemem[0x26b93c8 -> 0x26742c8]: 
tracemem[0x26742c8 -> 0x2674358]: $<-.data.frame $<- 
tracemem[0x2674358 -> 0x2674478]: $<-.data.frame $<- 

Tutaj dd są kopiowane raz w każdym wywołaniu apply ponieważ apply() konwertuje dd do matrycy przed kontynuowaniem. Ostatnie trzy linie w każdym bloku wyjściowym tracemem wskazują, że trzy kopie dat są tworzone w celu wstawienia nowej kolumny.

Co z drugą opcją?

R> set.seed(1) 
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10)) 
R> tracemem(dat) 
[1] "<0x268bc88>" 
R> dat$mindepth <- apply(dat[,c("depth1","depth2")],1,min) 
tracemem[0x268bc88 -> 0x26376b0]: 
tracemem[0x26376b0 -> 0x2637720]: $<-.data.frame $<- 
tracemem[0x2637720 -> 0x2637790]: $<-.data.frame $<- 
R> tracemem(dat) 
[1] "<0x2466d40>" 
R> dat$maxdepth <- apply(dat[,c("depth1","depth2")],1,max) 
tracemem[0x2466d40 -> 0x22ae0d8]: 
tracemem[0x22ae0d8 -> 0x22ae1f8]: $<-.data.frame $<- 
tracemem[0x22ae1f8 -> 0x22ae318]: $<-.data.frame $<- 

Tutaj ta wersja unika kopię udział w tworzeniu dd, ale we wszystkich innych aspektach jest podobny do poprzedniego sugestię.

Czy możemy zrobić coś lepszego? Tak, i jeden prosty sposób to użycie opcji within() Zacząłem jednak wykonać oba oświadczenia do tworzenia nowych mindepth i maxdepth zmienne w jednym wywołaniu within():

R> set.seed(1) 
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10)) 
R> tracemem(dat) 
[1] "<0x21c4158>" 
R> dat <- within(dat, { mindepth <- pmin(depth1, depth2) 
+      maxdepth <- pmax(depth1, depth2) }) 
tracemem[0x21c4158 -> 0x21c44a0]: within.data.frame within 
tracemem[0x21c44a0 -> 0x21c4628]: [<-.data.frame [<- within.data.frame within 

W tej wersji mamy tylko powołać dwie kopie dat w porównaniu z 4 kopiami oryginalnej wersji within().

Co powiesz, jeśli zmusimy dat do matrycy, a następnie wykonamy wstawienia?

R> set.seed(1) 
R> dat <- data.frame(depth1 = runif(10), depth2 = runif(10)) 
R> tracemem(dat) 
[1] "<0x1f29c70>" 
R> mat <- as.matrix.data.frame(dat) 
tracemem[0x1f29c70 -> 0x1f09768]: as.matrix.data.frame 
R> tracemem(mat) 
[1] "<0x245ff30>" 
R> mat <- cbind(mat, pmin(mat[,1], mat[,2]), pmax(mat[,1], mat[,2])) 
R> 

To jest poprawa jak tylko ponieść koszt pojedynczej kopii dat gdy zmuszanie do matrycy.Oszukałem trochę, dzwoniąc bezpośrednio do metody as.matrix.data.frame(). Gdybyśmy po prostu używali as.matrix(), ponosilibyśmy inną kopię mat.

Podkreśla to jeden z powodów, dla których macierze są znacznie szybsze w użyciu niż ramki danych.