Sé que esto no es en realidad responden al empuje principal de la pregunta (@hadley lo ha hecho y merece crédito), pero hay otras opciones para los que sugiere. Aquí podría usar pmin()
y pmax()
como otra solución, y usando with()
o within()
podemos hacerlo sin subconjuntos explícitos para crear un 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
Podemos mirar a la cantidad de copias que pasa con tracemem()
pero solamente si su R fue compilado con la siguiente opción de configuración activa --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
Y vemos que R copiado dat
dos veces durante cada within()
llamada. Compare eso con sus dos sugerencias:
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 $<-
Aquí, dd
se copia una vez en cada llamada a apply
porque apply()
convierte dd
a una matriz antes de continuar. Las últimas tres líneas en cada bloque de salida tracemem
indican que se están haciendo tres copias de dat
para insertar la nueva columna.
¿Qué pasa con su segunda opción?
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 $<-
Aquí esta versión evita la copia conlleva la creación de dd
, pero en todos los demás aspectos es similar a la sugerencia anterior.
¿Podemos hacer algo mejor? Sí, y una manera sencilla es utilizar la opción within()
empecé con mas ejecutar ambas afirmaciones para crear nuevos mindepth
y maxdepth
variables en la llamada a 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
En esta versión sólo invocamos dos copias de dat
en comparación con las 4 copias de la versión original within()
.
¿Qué pasa si coaccionamos dat
a una matriz y luego hacemos las inserciones?
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>
Eso es una mejora, ya que sólo incurrir en el coste de la copia única de dat
cuando coaccionar a una matriz.Intenté engañar un poco llamando directamente al método as.matrix.data.frame()
. Si hubiéramos usado as.matrix()
, habríamos incurrido en otra copia de mat
.
Esto destaca una de las razones por las que las matrices son mucho más rápidas de usar que los marcos de datos.
Subconjunto prácticamente de todo en R crea una copia. Hay algunas excepciones en los paquetes contribuidos. – hadley
@hadley - ¿Desea publicar eso como una respuesta, por lo que puede ser aceptado, etc. solo para los registros ...? –