Trucos para administrar la memoria disponible en una sesión R


490

¿Qué trucos utilizan las personas para administrar la memoria disponible de una sesión interactiva de R? Utilizo las funciones a continuación [basadas en publicaciones de Petr Pikal y David Hinds en la lista de ayuda de r en 2004] para enumerar (y / u ordenar) 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 memoria suficiente.

¿Algún otro truco agradable que la gente quiera compartir? Uno 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)
}

Tenga en cuenta que NO lo dudo, pero ¿de qué sirve eso? Soy bastante nuevo en los problemas de memoria en R, pero últimamente estoy experimentando algunos (es por eso que estaba buscando esta publicación :), así que estoy comenzando con todo esto. ¿Cómo ayuda esto a mi trabajo diario?
Matt Bannert

44
si desea ver los objetos dentro de una función, debe usar: lsos (pos = environment ()), de lo contrario solo mostrará variables globales. Para escribir en el error estándar: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn

¿Por qué Linux de 64 bits y no Windows de 64 bits? ¿La elección del sistema operativo hace una diferencia no trivial cuando tengo 32 GB de RAM para usar?
Jase

3
@pepsimax: Esto ha sido empaquetado en el multilevelPSApaquete . El paquete está diseñado para otra cosa, pero puede usar la función desde allí sin cargar el paquete diciendo requireNamespace(multilevelPSA); multilevelPSA::lsos(...). O en el Dmiscpaquete (no en CRAN).
krlmlr

1
Si el conjunto de datos es de un tamaño manejable, generalmente voy a R studio> Entorno> Vista de cuadrícula. Aquí puede ver y ordenar todos los elementos en su entorno actual según el tamaño.
kRazzy R

Respuestas:


197

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


58
Mi estrategia es dividir mis scripts a lo largo de las líneas de load.R y do.R, donde load.R puede tardar bastante tiempo en cargar datos de archivos o una base de datos, y realiza un mínimo de procesamiento previo / fusión de esos datos La última línea de load.R es algo para guardar el estado del espacio de trabajo. Entonces do.R es mi bloc de notas mediante el cual construyo mis funciones de análisis. Frecuentemente recargo do.R (con o sin recargar el estado del espacio de trabajo desde load.R según sea necesario).
Josh Reich

32
Esa es una buena técnica. Cuando los archivos se ejecutan en un orden determinado de esa manera, a menudo ellos con un número de prefijo: 1-load.r, 2-explore.r, 3-model.r- de esa manera que es obvio para los demás que hay un cierto orden actual.
hadley

44
No puedo respaldar esta idea lo suficiente. He enseñado R a algunas personas y esta es una de las primeras cosas que digo. Esto también se aplica a cualquier lenguaje donde el desarrollo incorpora un REPL y un archivo que se está editando (es decir, Python). rm (ls = list ()) y source () también funcionan, pero la reapertura es mejor (los paquetes también se borran).
Vince

53
El hecho de que la respuesta más votada implique reiniciar R es la peor crítica de R posible.
sds

77
@ MartínBel que solo elimina objetos creados en el entorno global. No descarga paquetes ni objetos S4 ni muchas otras cosas.
hadley

160

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

  • Agregar columnas por referencia
  • Modificar subconjuntos de columnas existentes por referencia y por grupo por referencia
  • Eliminar columnas por referencia

Ninguna de estas operaciones copia el (potencialmente grande) data.tableen absoluto, ni siquiera una vez.

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

Enlaces relacionados :


109

¡Vi 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") })

¿Se puede agregar esta función a dplyr o algún otro paquete de claves?
userJT

1
Vale la pena señalar que (al menos con base-3.3.2) capture.outputya no es necesario y obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })produce una salida limpia. De hecho, no eliminarlo produce comillas no deseadas en la salida, es decir, en [1] "792.5 Mb"lugar de 792.5 Mb.
Nutle

@Nutle Excelente, he actualizado el código en consecuencia :)
Tony Breyal

También cambiaría obj.class <- napply(names, function(x) as.character(class(x))[1])a obj.class <- napply(names, function(x) class(x)[1]) ya que classsiempre devuelve un vector de caracteres ahora (base-3.5.0).
DeltaIV

49

Hago un uso agresivo del subsetparámetro con la selección de solo las variables requeridas al pasar marcos de datos al data=argumento de las funciones de regresión. Resulta en algunos errores si olvido agregar variables tanto a la fórmula como al select=vector, pero aún así ahorra mucho tiempo debido a la disminución de la copia de objetos y reduce la huella de memoria significativamente. Digamos que tengo 4 millones de registros con 110 variables (y lo tengo). 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 gdlab2variable es un vector lógico que se construyó para 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 HIVfinalera un vector de caracteres que resumía las pruebas preliminares y confirmatorias para VIH .


48

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 que esté presente 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
}

34

Ese es un buen truco.

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

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


25
¿No es esto solo aplicable a Windows?
Christopher DuBois

44
> memory.limit () [1] Inf Mensaje de advertencia: 'memory.limit ()' es específico de Windows
LJT

¿Usar tibble en lugar de data.frame nos ayuda aún mejor para ahorrar memoria?

32

Me gusta bastante la función de objetos mejorados desarrollada por Dirk. Sin embargo, la mayor parte del tiempo, un resultado más básico 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 de forma ascendente o descendente. Además, a menudo trabajo con datos que son 1GB +, 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

30

Nunca guardo un espacio de trabajo de R. Utilizo scripts de importación y scripts de datos y envío cualquier objeto de datos especialmente grande que no quiera recrear a menudo en archivos. De esta manera, siempre comienzo con un espacio de trabajo nuevo y no necesito limpiar objetos grandes. Sin embargo, esa es una función muy buena.


30

Lamentablemente, no tuve tiempo de probarlo exhaustivamente, 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 lee 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 memoria podría haber disminuido. Sería bueno si alguien pudiera confirmar si esto produce resultados similares con un conjunto de datos diferente.


44
Sí, experimenté lo mismo. El uso de memoria cae incluso al 30% en mi caso. Memoria de 1.5GB utilizada, guardada en .RData (~ 30MB). Nueva sesión después de cargar .RData usa menos de 500 MB de memoria.
f3lix

Intenté con 2 conjuntos de datos (100MB y 2.7GB) cargados en data.table usando fread, luego los guardé en .RData. Los archivos RData eran de hecho un 70% más pequeños, pero después de volver a cargarlos, la memoria utilizada era exactamente la misma. Esperaba que este truco redujera la huella de la memoria ... ¿me estoy perdiendo algo?
NoviceProg

@NoviceProg No creo que te estés perdiendo algo, pero es un truco, supongo que no funcionará en todas las situaciones. En mi caso, la memoria después de volver a cargar se redujo realmente como se describe.
Dennis Jaheruddin

66
@NoviceProg Un par de cosas. Primero, fread, seguir el credo de data.table es probablemente más eficiente en la memoria para cargar archivos que read.csv. En segundo lugar, los ahorros de memoria que las personas señalan aquí tienen que ver principalmente con el tamaño de la memoria del proceso R (que se expande para contener objetos y se retrae cuando se realiza la recolección de basura). Sin embargo, la recolección de basura no siempre libera toda la RAM al sistema operativo. Detener la sesión R y cargar el elemento desde donde se ha almacenado liberará tanta RAM como sea posible ... pero si la sobrecarga era pequeña para empezar ... sin ganancia.
russellpierce

27

Para ilustrar mejor 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 uso para cronometrar 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 modificador --packages | -l) y ejecuta los ejemplos de la función spMatrix. Como r siempre comienza 'fresco', este método también es una buena prueba durante el desarrollo del paquete.

Por último, pero no menos importante, r también funciona muy 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).


23

Tanto para la velocidad como para la memoria, cuando construya un gran marco de datos a través de una serie de pasos complejos, lo descargaré periódicamente (el conjunto de datos en progreso que se está creando) en el disco, añadiendo todo lo que vino antes, y luego lo reiniciaré . De esta forma, los pasos intermedios solo funcionan en marcos de datos pequeños (lo cual es bueno, por ejemplo, rbind se ralentiza considerablemente con objetos más grandes). Todo el conjunto de datos se puede volver a leer 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 )

17

Solo para notar que el data.tablepaquete tables()parece ser un reemplazo bastante bueno para la .ls.objects()función personalizada de Dirk (detallado en respuestas anteriores), aunque solo para data.frames / tables y no, por ejemplo, matrices, matrices, listas.


esto no enumera ningún data.frames, por lo que no es tan bueno
userJT

16
  1. Soy afortunado y el instrumento guarda mis grandes conjuntos de datos en "fragmentos" (subconjuntos) de aproximadamente 100 MB (binario de 32 bits). Por lo tanto, puedo hacer los pasos de preprocesamiento (eliminar partes no informativas, reducir el muestreo) secuencialmente antes de fusionar el conjunto de datos.

  2. Llamar 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 una compensación entre la vectorización y el uso de la memoria.
    comparar: split& lapplyvs. un forbucle.

  4. En aras del análisis de datos rápido y fácil, 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 finaliza el código de análisis de datos y los datos completos van al servidor de cálculo para el cálculo durante la noche / durante el fin de semana / ...


11

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

La razón: cada vez listque se modifica un elemento de una estructura, 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 los datos deben intercambiarse al 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))

En combinación con estructuras como big.matrixo data.tableque permiten alterar su contenido en el lugar, se puede lograr un uso de memoria muy eficiente.


66
Esto ya no es cierto: de la R avanzada de Hadley , "Los cambios a R 3.1.0 han hecho que este uso [de entornos] sea sustancialmente menos importante porque modificar una lista ya no hace una copia profunda".
petrelharp

8

La llfunción en el gDatapaquete también puede mostrar el uso de memoria de cada objeto.

gdata::ll(unit='MB')

No en mi sistema: R versión 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-bit), gdata_2.13.3, gtools_3.4.1.
krlmlr

Tienes razón, lo pruebo una vez que fue ordenado por casualidad.
user1436187

1
por favor modifique la función para usar Gb, Mb
userJT

7

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

Lo que generalmente hago es tener una función que haga el trabajo y regrese NULL: todos los datos se leen y manipulan en esta función u otras que llama.


7

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

Yo uso data.table casi exclusivamente.

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

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

Frecuentemente rm () objetos del entorno tan pronto como ya no son necesarios, por ejemplo, en la siguiente línea después de usarlos para subconjugar algo más, y llamar a gc ().

'fread' y 'fwrite' de data.table pueden ser muy rápidos en comparación con las lecturas y escrituras de base R.

Como sugiere kpierce8 , casi siempre escribo todo fuera del entorno y lo vuelvo a cargar, incluso con miles / cientos de miles de pequeños archivos para pasar. Esto no solo mantiene el entorno "limpio" y mantiene baja la asignación de memoria sino que, posiblemente debido a la grave falta de RAM disponible, R tiene una propensión a fallas frecuentes en mi computadora; muy frecuentemente Tener una copia de seguridad de la información en la unidad en sí a medida que el código avanza a través de varias etapas significa que no tengo que comenzar desde el principio si falla.

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

Muchos de los problemas en los que he trabajado en R involucran formar pares de combinación y permutación, triples, etc., lo que solo hace que tener RAM limitada sea más una limitación, ya que a menudo se expandirán al menos 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 ellos, en lugar de tratar de limpiarla después, y en la secuencia de operaciones para preparar 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 haber algunos beneficios al usar la lectura y escritura de 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. 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 RAM necesaria con Ubuntu es ligeramente menor. Pero he notado grandes cantidades de advertencias y errores al instalar paquetes de terceros en (L) Ubuntu. No recomendaría alejarse demasiado de (L) Ubuntu u otras distribuciones de acciones 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 )

Esperemos que algo de eso pueda ayudar a otros.


5

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 por 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]))),])

5

Esta es una respuesta más nueva a esta excelente pregunta antigua. De Hadley's Advanced R:

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 )


3

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


2

Realmente aprecio algunas de las respuestas anteriores, siguiendo a @hadley y @Dirk que sugieren cerrar R y emitir sourcey usar la línea de comando. Se me ocurrió 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 usé 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.ry escribo la respuesta de datos para la salida. Con esto, cada vez que el reiniciador llama al script, parece que R se vuelve a abrir y se libera la memoria.

Espero eso ayude.


2

Además de las técnicas de administración de memoria más generales que figuran en las respuestas anteriores, siempre trato de reducir el tamaño de mis objetos lo más posible. Por ejemplo, trabajo con matrices muy grandes pero muy escasas, en otras palabras, matrices donde la mayoría de los valores son cero. Usando el paquete 'Matrix' (capitalización importante) 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 pueden almacenar datos dispersos de manera mucho más eficiente, ya sea cargados en la memoria o guardados en el disco.

Además, los archivos sin formato que recibo están en formato 'largo' donde cada punto de datos tiene variables x, y, z, i. Mucho más eficiente para transformar los datos en una x * y * zmatriz de dimensiones con solo variables i.

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


2

Consejo para tratar con objetos que requieren cálculos intermedios pesados: cuando uso objetos que requieren muchos cálculos pesados ​​y pasos intermedios para crear, a menudo me resulta útil escribir un fragmento de código con la función para crear el objeto, y luego un fragmento separado de código que me da la opción de generar y guardar el objeto como un rmdarchivo, o cargarlo externamente desde un rmdarchivo que ya he guardado anteriormente. Esto es especialmente fácil de hacer al R Markdownusar la siguiente estructura de fragmentos de código.

```{r Create OBJECT}

COMPLICATED.FUNCTION <- function(...) { Do heavy calculations needing lots of memory;
                                        Output OBJECT; }

```
```{r Generate or load OBJECT}

LOAD <- TRUE;
#NOTE: Set LOAD to TRUE if you want to load saved file
#NOTE: Set LOAD to FALSE if you want to generate and save

if(LOAD == TRUE) { OBJECT <- readRDS(file = 'MySavedObject.rds'); } else
                 { OBJECT <- COMPLICATED.FUNCTION(x, y, z);
                             saveRDS(file = 'MySavedObject.rds', object = OBJECT); }

```

Con esta estructura de código, todo lo que necesito hacer es cambiar LOADdependiendo de si quiero generar y guardar el objeto, o cargarlo directamente desde un archivo guardado existente. (Por supuesto, tengo que generarlo y guardarlo la primera vez, pero después de esto tengo la opción de cargarlo). La configuración LOAD = TRUEomite el uso de mi función complicada y evita todo el cálculo pesado en él. Este método aún requiere suficiente memoria para almacenar el objeto de interés, pero le evita tener que calcularlo cada vez que ejecuta su código. Para los objetos que requieren muchos cálculos pesados ​​de pasos intermedios (por ejemplo, para cálculos que involucran bucles sobre matrices grandes), esto puede ahorrar una cantidad sustancial de tiempo y cómputo.


1

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.


¿Qué hace el forbucle aquí? No hay ien la gcllamada.
Umaomamaomao

@qqq está ahí solo para evitar copiar y pegar gc(reset = T)nueve veces
Marcelo Ventura

14
¿Pero por qué lo ejecutarías 9 veces? (curioso, no crítico)
Umaomamaomao

1

También puede obtener algún beneficio al usar knitr y poner su script en fragmentos Rmd.

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

Allí puede configurar un fragmento para que se guarde en "caché", o puede decidir ejecutar o no un fragmento en particular. De esta manera, en una primera ejecución solo puede procesar "parte 1", otra ejecución puede seleccionar solo "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 :)


1

Basado en la respuesta de @ Dirk y @ Tony, he hecho una ligera actualización. El resultado salía [1]antes de los valores de tamaño bonito, así que saqué el capture.outputque 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()

-1

Intento mantener pequeña la cantidad de objetos cuando trabajo en un proyecto más grande con muchos pasos intermedios. Entonces, en lugar de crear muchos objetos únicos llamados

dataframe-> step1-> step2-> step3->result

raster-> multipliedRast-> meanRastF-> sqrtRast->resultRast

Trabajo con objetos temporales a los que llamo temp.

dataframe-> temp-> temp-> temp->result

Lo que me deja con menos archivos intermedios y más información general.

raster  <- raster('file.tif')
temp <- raster * 10
temp <- mean(temp)
resultRast <- sqrt(temp)

Para ahorrar más memoria, simplemente puedo eliminar tempcuando ya no sea necesario.

rm(temp)

Si necesito varios archivos intermedios, uso temp1, temp2, temp3.

Para la prueba de uso test, test2...

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.