¿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)
}
memory-management
r
Dirk Eddelbuettel
fuente
fuente
multilevelPSA
paquete . El paquete está diseñado para otra cosa, pero puede usar la función desde allí sin cargar el paquete diciendorequireNamespace(multilevelPSA); multilevelPSA::lsos(...)
. O en elDmisc
paquete (no en CRAN).Respuestas:
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.fuente
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.Yo uso el paquete data.table . Con su
:=
operador puedes:Ninguna de estas operaciones copia el (potencialmente grande)
data.table
en absoluto, ni siquiera una vez.data.table
usa mucha menos memoria de trabajo.Enlaces relacionados :
:=
operador en data.table?fuente
¡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:
Lo que resulta en algo como lo siguiente:
NOTA: La parte principal que agregué fue (nuevamente, adaptada de la respuesta de JD):
fuente
capture.output
ya no es necesario yobj.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 de792.5 Mb
.obj.class <- napply(names, function(x) as.character(class(x))[1])
aobj.class <- napply(names, function(x) class(x)[1])
ya queclass
siempre devuelve un vector de caracteres ahora (base-3.5.0).Hago un uso agresivo del
subset
parámetro con la selección de solo las variables requeridas al pasar marcos de datos aldata=
argumento de las funciones de regresión. Resulta en algunos errores si olvido agregar variables tanto a la fórmula como alselect=
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:A modo de establecer el contexto y la estrategia: la
gdlab2
variable 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 yHIVfinal
era un vector de caracteres que resumía las pruebas preliminares y confirmatorias para VIH .fuente
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:
fuente
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.
fuente
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.
Y aquí hay un ejemplo de salida:
fuente
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.
fuente
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 usarload("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.fuente
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?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.
Igualmente,
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).
fuente
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.
fuente
Solo para notar que el
data.table
paquetetables()
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.fuente
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.
Llamar
gc ()
"a mano" puede ayudar si el tamaño de los datos se acerca a la memoria disponible.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
&lapply
vs. unfor
bucle.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 / ...fuente
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
list
que 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:
En combinación con estructuras como
big.matrix
odata.table
que permiten alterar su contenido en el lugar, se puede lograr un uso de memoria muy eficiente.fuente
La
ll
función en elgData
paquete también puede mostrar el uso de memoria de cada objeto.fuente
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.fuente
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.
fuente
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:
fuente
Esta es una respuesta más nueva a esta excelente pregunta antigua. De Hadley's Advanced R:
( http://adv-r.had.co.nz/memory.html )
fuente
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
makeForkCluster
lugar de amakePSOCKcluster
. Esto también le ahorra tiempo al enviar el objeto grande a los otros procesos.fuente
Realmente aprecio algunas de las respuestas anteriores, siguiendo a @hadley y @Dirk que sugieren cerrar R y emitir
source
y 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:
con este script básicamente controlo lo que hace mi script principal
runConsensus.r
y 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.
fuente
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:
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 unax * y * z
matriz de dimensiones con solo variablesi
.Conozca sus datos y use un poco de sentido común.
fuente
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
rmd
archivo, o cargarlo externamente desde unrmd
archivo que ya he guardado anteriormente. Esto es especialmente fácil de hacer alR Markdown
usar la siguiente estructura de fragmentos de código.Con esta estructura de código, todo lo que necesito hacer es cambiar
LOAD
dependiendo 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ónLOAD = TRUE
omite 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.fuente
Corriendo
de vez en cuando también ayuda a R a liberar memoria no utilizada pero aún no liberada.
fuente
for
bucle aquí? No hayi
en lagc
llamada.gc(reset = T)
nueve vecesTambié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:
Como efecto secundario, esto también podría ahorrarle algunos dolores de cabeza en términos de reproducibilidad :)
fuente
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é elcapture.output
que resolvió el problema:fuente
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.
Para ahorrar más memoria, simplemente puedo eliminar
temp
cuando ya no sea necesario.Si necesito varios archivos intermedios, uso
temp1
,temp2
,temp3
.Para la prueba de uso
test
,test2
...fuente