error cannot allocate memory-management r

memory management - cannot - Trucos para gestionar la memoria disponible en una sesión R



cannot allocate vector size r (25)

¿Qué trucos utilizan las personas para gestionar la memoria disponible de una sesión interactiva de R? Utilizo las siguientes funciones [basadas en las publicaciones de Petr Pikal y David Hinds en la lista de ayuda en 2004] para enumerar (y / o clasificar) los objetos más grandes y, ocasionalmente, rm() algunos de ellos. Pero, con mucho, la solución más efectiva fue ... ejecutar bajo Linux de 64 bits con amplia memoria.

¿Algún otro truco que la gente quiera compartir? Una por publicación, por favor.

# improved list of objects .ls.objects <- function (pos = 1, pattern, order.by, decreasing=FALSE, head=FALSE, n=5) { napply <- function(names, fn) sapply(names, function(x) fn(get(x, pos = pos))) names <- ls(pos = pos, pattern = pattern) obj.class <- napply(names, function(x) as.character(class(x))[1]) obj.mode <- napply(names, mode) obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class) obj.size <- napply(names, object.size) obj.dim <- t(napply(names, function(x) as.numeric(dim(x))[1:2])) vec <- is.na(obj.dim)[, 1] & (obj.type != "function") obj.dim[vec, 1] <- napply(names, length)[vec] out <- data.frame(obj.type, obj.size, obj.dim) names(out) <- c("Type", "Size", "Rows", "Columns") if (!missing(order.by)) out <- out[order(out[[order.by]], decreasing=decreasing), ] if (head) out <- head(out, n) out } # shorthand lsos <- function(..., n=10) { .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n) }


  1. Tengo la suerte de que el instrumento guarda mis grandes conjuntos de datos en "fragmentos" (subconjuntos) de aproximadamente 100 MB (binario de 32 bits). Por lo tanto, puedo realizar pasos de preprocesamiento (eliminar partes no informativas, submuestreo) de forma secuencial antes de fusionar el conjunto de datos.

  2. Llamar a gc () "a mano" puede ayudar si el tamaño de los datos se acerca a la memoria disponible.

  3. A veces un algoritmo diferente necesita mucha menos memoria.
    A veces hay un intercambio entre vectorización y uso de memoria.
    comparar: split & lapply vs. a for loop.

  4. Por el simple y rápido análisis de datos, a menudo trabajo primero con un pequeño subconjunto aleatorio ( sample () ) de los datos. Una vez que el script de análisis de datos / .Rnw haya finalizado el código de análisis de datos y los datos completos vayan al servidor de cálculo para el cálculo nocturno / de fin de semana / ...


¡Vio esto en una publicación de Twitter y creo que es una función increíble de Dirk! Siguiendo la respuesta de JD Long, haría esto para una lectura fácil de usar:

# improved list of objects .ls.objects <- function (pos = 1, pattern, order.by, decreasing=FALSE, head=FALSE, n=5) { napply <- function(names, fn) sapply(names, function(x) fn(get(x, pos = pos))) names <- ls(pos = pos, pattern = pattern) obj.class <- napply(names, function(x) as.character(class(x))[1]) obj.mode <- napply(names, mode) obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class) obj.prettysize <- napply(names, function(x) { format(utils::object.size(x), units = "auto") }) obj.size <- napply(names, object.size) obj.dim <- t(napply(names, function(x) as.numeric(dim(x))[1:2])) vec <- is.na(obj.dim)[, 1] & (obj.type != "function") obj.dim[vec, 1] <- napply(names, length)[vec] out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim) names(out) <- c("Type", "Size", "PrettySize", "Length/Rows", "Columns") if (!missing(order.by)) out <- out[order(out[[order.by]], decreasing=decreasing), ] if (head) out <- head(out, n) out } # shorthand lsos <- function(..., n=10) { .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n) } lsos()

Lo que resulta en algo como lo siguiente:

Type Size PrettySize Length/Rows Columns pca.res PCA 790128 771.6 Kb 7 NA DF data.frame 271040 264.7 Kb 669 50 factor.AgeGender factanal 12888 12.6 Kb 12 NA dates data.frame 9016 8.8 Kb 669 2 sd. numeric 3808 3.7 Kb 51 NA napply function 2256 2.2 Kb NA NA lsos function 1944 1.9 Kb NA NA load loadings 1768 1.7 Kb 12 2 ind.sup integer 448 448 bytes 102 NA x character 96 96 bytes 1 NA

NOTA: La parte principal que agregué fue (nuevamente, adaptada de la respuesta de JD):

obj.prettysize <- napply(names, function(x) { print(object.size(x), units = "auto") })


Además de las técnicas de administración de memoria más generales que se dan en las respuestas anteriores, siempre trato de reducir el tamaño de mis objetos en la medida de lo posible. Por ejemplo, trabajo con matrices muy grandes pero muy dispersas, es decir, matrices donde la mayoría de los valores son cero. Al usar el paquete ''Matrix'' (las mayúsculas son importantes) pude reducir el tamaño promedio de mis objetos de ~ 2GB a ~ 200MB simplemente como:

my.matrix <- Matrix(my.matrix)

El paquete Matrix incluye formatos de datos que se pueden usar exactamente como una matriz regular (no es necesario cambiar su otro código), pero son capaces de almacenar datos dispersos de manera mucho más eficiente, ya sea cargados en la memoria o guardados en el disco.

Además, los archivos en bruto que recibo están en formato ''largo'' donde cada punto de datos tiene las variables x, y, z, i . Mucho más eficiente para transformar los datos en una matriz de dimensión x * y * z con solo la variable i .

Conozca sus datos y use un poco de sentido común.


Asegúrese de grabar su trabajo en un script reproducible. De vez en cuando, vuelva a abrir R, luego source() su script. Limpiará todo lo que ya no esté usando y, como beneficio adicional, habrá probado su código.


Basándome en las respuestas de @ Dirk y @ Tony, he hecho una pequeña actualización. El resultado fue la salida [1] antes de los valores de tamaño bonito, así que saqué la capture.output que resolvió el problema:

.ls.objects <- function (pos = 1, pattern, order.by, decreasing=FALSE, head=FALSE, n=5) { napply <- function(names, fn) sapply(names, function(x) fn(get(x, pos = pos))) names <- ls(pos = pos, pattern = pattern) obj.class <- napply(names, function(x) as.character(class(x))[1]) obj.mode <- napply(names, mode) obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class) obj.prettysize <- napply(names, function(x) { format(utils::object.size(x), units = "auto") }) obj.size <- napply(names, utils::object.size) obj.dim <- t(napply(names, function(x) as.numeric(dim(x))[1:2])) vec <- is.na(obj.dim)[, 1] & (obj.type != "function") obj.dim[vec, 1] <- napply(names, length)[vec] out <- data.frame(obj.type, obj.size, obj.prettysize, obj.dim) names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns") if (!missing(order.by)) out <- out[order(out[[order.by]], decreasing=decreasing), ] if (head) out <- head(out, n) return(out) } # shorthand lsos <- function(..., n=10) { .ls.objects(..., order.by="Size", decreasing=TRUE, head=TRUE, n=n) } lsos()


Con solo 4 GB de RAM (ejecutando Windows 10, así que haga aproximadamente 2 o más realistas de 1 GB) he tenido que tener mucho cuidado con la asignación.

Yo uso data.table casi exclusivamente.

La función ''fread'' le permite subcontratar información por nombres de campos en la importación; solo importa los campos que realmente se necesitan para empezar. Si está utilizando la base R de lectura, anule las columnas espurias inmediatamente después de la importación.

Como lo sugiere 42- , siempre que sea posible, me subcontrataré dentro de las columnas inmediatamente después de importar la información.

Con frecuencia, rm () objetos del entorno tan pronto como ya no son necesarios, por ejemplo, en la siguiente línea después de usarlos para subcontratar otra cosa, y llamar a gc ().

''fread'' y ''fwrite'' de data.table pueden ser muy rápidos en comparación con la base R lee y escribe.

Como sugiere kpierce8 , casi siempre escribo todo fuera del entorno y lo vuelvo a filtrar, incluso con miles / cientos de miles de archivos pequeños para transmitir. Esto no solo mantiene el entorno "limpio" y mantiene baja la asignación de memoria sino que, posiblemente debido a la gran falta de RAM disponible, R tiene una propensión a fallar con frecuencia en mi computadora; con mucha frecuencia Hacer una copia de seguridad de la información en la propia unidad a medida que el código avanza a través de varias etapas significa que no tengo que empezar desde el principio si se bloquea.

A partir de 2017, creo que los SSD más rápidos están funcionando a unos pocos GB por segundo a través del puerto M2. Tengo un SSD Kingston V300 (550MB / s) de 50GB que uso como mi disco principal (tiene Windows y R en él). Guardo toda la información a granel en una bandeja WD barata de 500GB. Muevo los conjuntos de datos al SSD cuando comienzo a trabajar en ellos. Esto, combinado con ''fread'' y ''fwrite'', todo ha funcionado muy bien. He intentado usar ''ff'' pero prefiero el primero. Sin embargo, las velocidades de lectura / escritura de 4K pueden crear problemas con esto; realizar copias de seguridad de un cuarto de millón de archivos 1k (250 MB de valor) desde el SSD a la bandeja puede llevar horas. Que yo sepa, aún no hay ningún paquete R disponible que pueda optimizar automáticamente el proceso de "chunkification"; por ejemplo, mire cuánta RAM tiene un usuario, pruebe las velocidades de lectura / escritura de la RAM / todas las unidades conectadas y luego sugiera un protocolo de "chunkification" óptimo. Esto podría producir algunas mejoras significativas en el flujo de trabajo / optimizaciones de recursos; por ejemplo, divídalo a ... MB para el ram -> divídalo a ... MB para el SSD -> divídalo a ... MB en el disco -> divídalo a ... MB en la cinta. Podría muestrear conjuntos de datos de antemano para darle una varilla de medición más realista para trabajar.

Muchos de los problemas en los que he trabajado en R involucran la formación de pares de combinación y permutación, triples, etc., lo que hace que la limitación de RAM sea una limitación, ya que a menudo se expandirán exponencialmente en algún momento. Esto me ha hecho centrar mucha atención en la calidad en lugar de la cantidad de información que ingresa, en lugar de tratar de limpiarla después, y en la secuencia de operaciones en la preparación de la información para comenzar (comenzando con La operación más simple y aumentando la complejidad); por ejemplo, subconjunto, luego fusionar / unir, luego formar combinaciones / permutaciones, etc.

Parece que hay algunos beneficios al usar la lectura y escritura de la base R en algunos casos. Por ejemplo, la detección de errores dentro de ''fread'' es tan buena que puede ser difícil tratar de obtener información realmente desordenada en R para comenzar a limpiarla. Base R también parece ser mucho más fácil si estás usando Linux. La Base R parece funcionar bien en Linux, Windows 10 usa ~ 20 GB de espacio en disco, mientras que Ubuntu solo necesita unos pocos GB, la memoria RAM necesaria con Ubuntu es ligeramente inferior. Pero he notado grandes cantidades de advertencias y errores al instalar paquetes de terceros en (L) Ubuntu. No recomendaría desviarse demasiado de (L) Ubuntu u otras distribuciones de stock con Linux, ya que puede perder tanta compatibilidad general que hace que el proceso sea casi inútil (creo que la "unidad" se cancelará en Ubuntu a partir de 2017). ). Me doy cuenta de que esto no funcionará bien con algunos usuarios de Linux, pero algunas de las distribuciones personalizadas no tienen sentido más allá de la novedad (he pasado años usando Linux solo).

Esperemos que algo de eso pueda ayudar a otros.


Corriendo

for (i in 1:10) gc(reset = T)

de vez en cuando también ayuda a R a liberar memoria no utilizada pero aún no liberada.


Desafortunadamente, no tuve tiempo de probarlo extensivamente, pero aquí hay un consejo de memoria que no he visto antes. Para mí la memoria requerida se redujo en más del 50%. Cuando lees cosas en R con, por ejemplo, read.csv, requieren una cierta cantidad de memoria. Después de esto, puede guardarlos con save("Destinationfile",list=ls()) La próxima vez que abra R, puede usar load("Destinationfile") Ahora el uso de la memoria puede haber disminuido. Sería bueno si alguien pudiera confirmar si esto produce resultados similares con un conjunto de datos diferente.


El uso de entornos en lugar de listas para manejar colecciones de objetos que ocupan una cantidad significativa de memoria de trabajo.

El motivo: cada vez que se modifica un elemento de una estructura de list , toda la lista se duplica temporalmente. Esto se convierte en un problema si el requisito de almacenamiento de la lista es aproximadamente la mitad de la memoria de trabajo disponible, porque entonces los datos deben intercambiarse con el disco duro lento. Los entornos, por otro lado, no están sujetos a este comportamiento y pueden tratarse de manera similar a las listas.

Aquí hay un ejemplo:

get.data <- function(x) { # get some data based on x return(paste("data from",x)) } collect.data <- function(i,x,env) { # get some data data <- get.data(x[[i]]) # store data into environment element.name <- paste("V",i,sep="") env[[element.name]] <- data return(NULL) } better.list <- new.env() filenames <- c("file1","file2","file3") lapply(seq_along(filenames),collect.data,x=filenames,env=better.list) # read/write access print(better.list[["V1"]]) better.list[["V2"]] <- "testdata" # number of list elements length(ls(better.list))

Junto con estructuras como big.matrix o data.table que permiten modificar su contenido en el lugar, se puede lograr un uso muy eficiente de la memoria.


Ese es un buen truco.

Otra sugerencia es utilizar objetos eficientes en la memoria siempre que sea posible: por ejemplo, use una matriz en lugar de un data.frame.

Esto no aborda realmente la administración de la memoria, pero una función importante que no es ampliamente conocida es memory.limit (). Puede aumentar el valor predeterminado utilizando este comando, memory.limit (tamaño = 2500), donde el tamaño es en MB. Como mencionó Dirk, debe utilizar 64 bits para poder aprovechar esto realmente.


Esta es una respuesta más reciente a esta excelente pregunta anterior. De la avanzada R de Hadley:

install.packages("pryr") library(pryr) object_size(1:10) ## 88 B object_size(mean) ## 832 B object_size(mtcars) ## 6.74 kB

( http://adv-r.had.co.nz/memory.html )


Esto no agrega nada a lo anterior, pero está escrito en el estilo simple y muy comentado que me gusta. Produce una tabla con los objetos ordenados en tamaño, pero sin algunos de los detalles dados en los ejemplos anteriores:

#Find the objects MemoryObjects = ls() #Create an array MemoryAssessmentTable=array(NA,dim=c(length(MemoryObjects),2)) #Name the columns colnames(MemoryAssessmentTable)=c("object","bytes") #Define the first column as the objects MemoryAssessmentTable[,1]=MemoryObjects #Define a function to determine size MemoryAssessmentFunction=function(x){object.size(get(x))} #Apply the function to the objects MemoryAssessmentTable[,2]=t(t(sapply(MemoryAssessmentTable[,1],MemoryAssessmentFunction))) #Produce a table with the largest objects first noquote(MemoryAssessmentTable[rev(order(as.numeric(MemoryAssessmentTable[,2]))),])


Hago un uso agresivo del parámetro del subset con la selección de solo las variables requeridas al pasar los marcos de data= a los data= argumento de las funciones de regresión. Si me olvido de agregar variables tanto a la fórmula como al select= vector, se producen algunos errores, pero aún así se ahorra mucho tiempo debido a la disminución de la copia de objetos y se reduce significativamente la huella de memoria. Digamos que tengo 4 millones de registros con 110 variables (y lo hago). Ejemplo:

# library(rms); library(Hmisc) for the cph,and rcs functions Mayo.PrCr.rbc.mdl <- cph(formula = Surv(surv.yr, death) ~ age + Sex + nsmkr + rcs(Mayo, 4) + rcs(PrCr.rat, 3) + rbc.cat * Sex, data = subset(set1HLI, gdlab2 & HIVfinal == "Negative", select = c("surv.yr", "death", "PrCr.rat", "Mayo", "age", "Sex", "nsmkr", "rbc.cat") ) )

A modo de establecer el contexto y la estrategia: la variable gdlab2 es un vector lógico que se construyó para los sujetos en un conjunto de datos que tenía todos los valores normales o casi normales para un montón de pruebas de laboratorio y HIVfinal era un vector de caracteres que resumía las pruebas preliminares y confirmatorias para el VIH.


La función ll en el paquete gData puede mostrar el uso de memoria de cada objeto.

gdata::ll(unit=''MB'')


Me encanta el script .ls.objects () de Dirk pero seguí entrecerrando los ojos para contar los caracteres en la columna de tamaño. Así que hice algunos trucos feos para presentarlos con un formato bonito para el tamaño:

.ls.objects <- function (pos = 1, pattern, order.by, decreasing=FALSE, head=FALSE, n=5) { napply <- function(names, fn) sapply(names, function(x) fn(get(x, pos = pos))) names <- ls(pos = pos, pattern = pattern) obj.class <- napply(names, function(x) as.character(class(x))[1]) obj.mode <- napply(names, mode) obj.type <- ifelse(is.na(obj.class), obj.mode, obj.class) obj.size <- napply(names, object.size) obj.prettysize <- sapply(obj.size, function(r) prettyNum(r, big.mark = ",") ) obj.dim <- t(napply(names, function(x) as.numeric(dim(x))[1:2])) vec <- is.na(obj.dim)[, 1] & (obj.type != "function") obj.dim[vec, 1] <- napply(names, length)[vec] out <- data.frame(obj.type, obj.size,obj.prettysize, obj.dim) names(out) <- c("Type", "Size", "PrettySize", "Rows", "Columns") if (!missing(order.by)) out <- out[order(out[[order.by]], decreasing=decreasing), ] out <- out[c("Type", "PrettySize", "Rows", "Columns")] names(out) <- c("Type", "Size", "Rows", "Columns") if (head) out <- head(out, n) out }


Me gusta bastante la función de objetos mejorada desarrollada por Dirk. Sin embargo, la mayoría de las veces, una salida más básica con el nombre y el tamaño del objeto es suficiente para mí. Aquí hay una función más simple con un objetivo similar. El uso de la memoria se puede ordenar alfabéticamente o por tamaño, se puede limitar a un cierto número de objetos y se puede ordenar ascendente o descendente. Además, a menudo trabajo con datos de 1 GB +, por lo que la función cambia las unidades en consecuencia.

showMemoryUse <- function(sort="size", decreasing=FALSE, limit) { objectList <- ls(parent.frame()) oneKB <- 1024 oneMB <- 1048576 oneGB <- 1073741824 memoryUse <- sapply(objectList, function(x) as.numeric(object.size(eval(parse(text=x))))) memListing <- sapply(memoryUse, function(size) { if (size >= oneGB) return(paste(round(size/oneGB,2), "GB")) else if (size >= oneMB) return(paste(round(size/oneMB,2), "MB")) else if (size >= oneKB) return(paste(round(size/oneKB,2), "kB")) else return(paste(size, "bytes")) }) memListing <- data.frame(objectName=names(memListing),memorySize=memListing,row.names=NULL) if (sort=="alphabetical") memListing <- memListing[order(memListing$objectName,decreasing=decreasing),] else memListing <- memListing[order(memoryUse,decreasing=decreasing),] #will run if sort not specified or "size" if(!missing(limit)) memListing <- memListing[1:limit,] print(memListing, row.names=FALSE) return(invisible(memListing)) }

Y aquí hay un ejemplo de salida:

> showMemoryUse(decreasing=TRUE, limit=5) objectName memorySize coherData 713.75 MB spec.pgram_mine 149.63 kB stoch.reg 145.88 kB describeBy 82.5 kB lmBandpass 68.41 kB


Nunca guardo un espacio de trabajo R Utilizo scripts de importación y scripts de datos y muestro cualquier objeto de datos especialmente grande que no quiero recrear a menudo en archivos. De esta manera, siempre comienzo con un nuevo espacio de trabajo y no necesito limpiar objetos grandes. Esa es una función muy bonita sin embargo.


Para ilustrar más a fondo la estrategia común de reinicios frecuentes, podemos usar littler que nos permite ejecutar expresiones simples directamente desde la línea de comandos. Aquí hay un ejemplo que a veces utilizo para sincronizar diferentes BLAS para un simple crossprod.

r -e''N<-3*10^3; M<-matrix(rnorm(N*N),ncol=N); print(system.time(crossprod(M)))''

Igualmente,

r -lMatrix -e''example(spMatrix)''

carga el paquete Matrix (a través del interruptor --packages | -l) y ejecuta los ejemplos de la función spMatrix. Como r siempre comienza de nuevo, este método también es una buena prueba durante el desarrollo del paquete.

Por último, pero no menos importante, también funciona bien para el modo por lotes automatizado en scripts que usan el encabezado shebang ''#! / Usr / bin / r''. Rscript es una alternativa donde littler no está disponible (por ejemplo, en Windows).


Realmente aprecio algunas de las respuestas anteriores, siguiendo a @hadley y @Dirk que sugieren cerrar R y emitir la source y usar la línea de comandos, se me ocurre una solución que funcionó muy bien para mí. Tuve que lidiar con cientos de espectros de masas, cada uno ocupa alrededor de 20 Mb de memoria, así que utilicé dos scripts R, de la siguiente manera:

Primero una envoltura:

#!/usr/bin/Rscript --vanilla --default-packages=utils for(l in 1:length(fdir)) { for(k in 1:length(fds)) { system(paste("Rscript runConsensus.r", l, k)) } }

con este script básicamente controlo lo que hace mi script principal runConsensus.r , y escribo la respuesta de los datos para la salida. Con esto, cada vez que el contenedor llama al script, parece que la R se vuelve a abrir y la memoria se libera.

Espero eso ayude.


Si está trabajando en Linux y desea usar varios procesos y solo tiene que realizar operaciones de lectura en uno o más objetos grandes, use makeForkCluster lugar de makePSOCKcluster . Esto también le ahorra tiempo al enviar el objeto grande a los otros procesos.


Si realmente desea evitar las fugas, debe evitar crear objetos grandes en el entorno global.

Lo que normalmente hago es tener una función que haga el trabajo y devuelva NULL : todos los datos se leen y manipulan en esta función o en otras a las que llama.


Solo para tener en cuenta que las tables() del paquete data.table tables() parecen ser un reemplazo bastante bueno para la función personalizada .ls.objects() Dirk (detallada en respuestas anteriores), aunque solo para data.frames / tables y no, por ejemplo, matrices, matrices, liza.


También puede obtener algún beneficio utilizando knitr y colocando su script en los módulos Rmd.

Por lo general, divido el código en diferentes partes y selecciono cuál guardará un punto de control en caché o en un archivo RDS, y

Allí puede establecer que un fragmento se guarde en "caché", o puede decidir ejecutar o no un fragmento en particular. De esta manera, en una primera ejecución puede procesar solo la "parte 1", otra ejecución puede seleccionar solo la "parte 2", etc.

Ejemplo:

part1 ```{r corpus, warning=FALSE, cache=TRUE, message=FALSE, eval=TRUE} corpusTw <- corpus(twitter) # build the corpus ``` part2 ```{r trigrams, warning=FALSE, cache=TRUE, message=FALSE, eval=FALSE} dfmTw <- dfm(corpusTw, verbose=TRUE, removeTwitter=TRUE, ngrams=3) ```

Como efecto secundario, esto también podría ahorrarle algunos dolores de cabeza en términos de reproducibilidad :)


Tanto para la velocidad como para la memoria, al crear un marco de datos de gran tamaño a través de una serie de pasos complejos, lo vaciaré periódicamente (el conjunto de datos en curso que se está construyendo) en el disco, añadiéndolo a todo lo que vino antes y luego lo reiniciaré. . De esta manera, los pasos intermedios solo funcionan en marcos de datos más pequeños (lo cual es bueno porque, por ejemplo, rbind se ralentiza considerablemente con los objetos más grandes). Se puede volver a leer todo el conjunto de datos al final del proceso, cuando se hayan eliminado todos los objetos intermedios.

dfinal <- NULL first <- TRUE tempfile <- "dfinal_temp.csv" for( i in bigloop ) { if( !i %% 10000 ) { print( i, "; flushing to disk..." ) write.table( dfinal, file=tempfile, append=!first, col.names=first ) first <- FALSE dfinal <- NULL # nuke it } # ... complex operations here that add data to ''dfinal'' data frame } print( "Loop done; flushing to disk and re-reading entire data set..." ) write.table( dfinal, file=tempfile, append=TRUE, col.names=FALSE ) dfinal <- read.table( tempfile )


Yo uso el paquete data.table . Con su := operador puedes:

  • Añadir columnas por referencia
  • Modificar subconjuntos de columnas existentes por referencia y por grupo por referencia
  • Eliminar columnas por referencia

Ninguna de estas operaciones copia la data.table (potencialmente grande) en absoluto, ni siquiera una vez.

  • La agregación también es particularmente rápida porque data.table utiliza mucha menos memoria de trabajo.

Enlaces relacionados :