Trucchi per gestire la memoria disponibile in una sessione R.


490

Quali trucchi usano le persone per gestire la memoria disponibile di una sessione R interattiva? Uso le seguenti funzioni [basate sui post di Petr Pikal e David Hinds nella lista r-help nel 2004] per elencare (e / o ordinare) gli oggetti più grandi e occasionalmente rm()alcuni di essi. Ma di gran lunga la soluzione più efficace era ... eseguire su Linux a 64 bit con ampia memoria.

Qualche altro bel trucco che la gente vuole condividere? Uno per posta, per favore.

# 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)
}

Nota, NON ne dubito, ma a che cosa serve? Sono abbastanza nuovo ai problemi di memoria in R, ma ne sto vivendo di recente (ecco perché stavo cercando questo post :) - quindi sto iniziando con tutto questo. In che modo aiuta il mio lavoro quotidiano?
Matt Bannert,

4
se vuoi vedere gli oggetti all'interno di una funzione, devi usare: lsos (pos = environment ()), altrimenti mostrerà solo variabili globali. Per scrivere sull'errore standard: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn,

Perché Linux a 64 bit e non Windows a 64 bit? La scelta del sistema operativo fa una differenza non banale quando ho 32 GB di RAM da usare?
Jase,

3
@pepsimax: questo è stato impacchettato nel multilevelPSApacchetto . Il pacchetto è progettato per qualcos'altro, ma è possibile utilizzare la funzione da lì senza caricare il pacchetto dicendo requireNamespace(multilevelPSA); multilevelPSA::lsos(...). O nel Dmiscpacchetto (non su CRAN).
krlmlr,

1
Se il set di dati ha dimensioni gestibili, di solito vado su R studio> Ambiente> Vista griglia. Qui puoi vedere e ordinare tutti gli elementi nel tuo ambiente attuale in base alle dimensioni.
kRazzy R,

Risposte:


197

Assicurati di registrare il tuo lavoro in uno script riproducibile. Di tanto in tanto, riapri R, quindi la source()tua sceneggiatura. Pulirai tutto ciò che non usi più e, come ulteriore vantaggio, avrai testato il tuo codice.


58
La mia strategia è quella di spezzare i miei script lungo le linee di load.R e do.R, dove load.R potrebbe impiegare un po 'di tempo per caricare i dati da file o da un database e fare qualsiasi minimo pre-elaborazione / fusione di quei dati. L'ultima riga di carico. R è qualcosa per salvare lo stato dell'area di lavoro. Quindi do.R è il mio scratchpad in base al quale costruisco le mie funzioni di analisi. Ricarica spesso do.R (con o senza ricaricare lo stato dell'area di lavoro da load.R secondo necessità).
Josh Reich,

32
Questa è una buona tecnica. Quando i file vengono eseguiti in un certo ordine del genere, io spesso li prefisso con un numero: 1-load.r, 2-explore.r, 3-model.r- in questo modo è ovvio agli altri che c'è qualche ordine presente.
Hadley,

4
Non posso sostenere abbastanza questa idea. Ho insegnato R ad alcune persone e questa è una delle prime cose che dico. Questo vale anche per qualsiasi linguaggio in cui lo sviluppo includa un REPL e un file in fase di modifica (ad esempio Python). Anche rm (ls = list ()) e source () funzionano, ma è meglio riaprire (anche pacchetti cancellati).
Vince il

53
Il fatto che la risposta più votata implichi il riavvio di R è la peggiore critica di R possibile.
sabato

7
@ MartínBel che rimuove solo gli oggetti creati nell'ambiente globale. Non scarica pacchetti o oggetti S4 o molte altre cose.
Hadley,

160

Uso il pacchetto data.table . Con il suo :=operatore puoi:

  • Aggiungi colonne per riferimento
  • Modifica sottoinsiemi di colonne esistenti per riferimento e per gruppo per riferimento
  • Elimina colonne per riferimento

Nessuna di queste operazioni copia il (potenzialmente di grandi dimensioni) data.table, nemmeno una volta.

  • L'aggregazione è anche particolarmente veloce perché data.tableutilizza molta meno memoria di lavoro.

Link correlati :


109

L'ho visto su un post su Twitter e penso che sia una fantastica funzione di Dirk! In seguito alla risposta di JD Long, lo farei per una lettura intuitiva:

# 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()

Il risultato è qualcosa di simile al seguente:

                      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 principale che ho aggiunto era (di nuovo, adattata dalla risposta di JD):

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

questa funzione può essere aggiunta a dplyr o ad altri pacchetti chiave?
userJT

1
Vale la pena notare che (almeno con la base 3.3.2) capture.outputnon è più necessario e obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })produce un output pulito. Infatti, non rimuoverlo produce virgolette indesiderate nell'output, ovvero [1] "792.5 Mb"invece di 792.5 Mb.
Nutle,

@Nutle Eccellente, ho aggiornato il codice di conseguenza :)
Tony Breyal,

Passerei anche obj.class <- napply(names, function(x) as.character(class(x))[1])a obj.class <- napply(names, function(x) class(x)[1]) poiché classrestituisco sempre un vettore di caratteri ora (base-3.5.0).
DeltaIV

49

Uso aggressivo il subsetparametro con la selezione delle sole variabili richieste quando si passano i frame di dati data=all'argomento delle funzioni di regressione. Se si dimentica di aggiungere variabili sia alla formula che al select=vettore, si verificano alcuni errori , ma si risparmia ancora molto tempo a causa della riduzione della copia degli oggetti e si riduce in modo significativo il footprint di memoria. Supponiamo di avere 4 milioni di record con 110 variabili (e lo faccio). Esempio:

# 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 titolo di impostazione del contesto e della strategia: la gdlab2variabile è un vettore logico che è stato costruito per i soggetti in un set di dati che aveva tutti i valori normali o quasi normali per un gruppo di test di laboratorio ed HIVfinalera un vettore di carattere che sintetizzava i test preliminari e di conferma per l'HIV .


48

Adoro lo script .ls.objects () di Dirk, ma ho continuato a strizzare gli occhi per contare i caratteri nella colonna delle dimensioni. Quindi ho fatto alcuni brutti hack per renderlo presente con una formattazione abbastanza per le dimensioni:

.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

Questo è un buon trucco.

Un altro suggerimento è quello di utilizzare oggetti efficienti in termini di memoria ove possibile: ad esempio, utilizzare una matrice anziché un data.frame.

Questo non riguarda realmente la gestione della memoria, ma un'importante funzione che non è ampiamente nota è memory.limit (). Puoi aumentare il valore predefinito usando questo comando memory.limit (size = 2500), dove la dimensione è in MB. Come ha detto Dirk, è necessario utilizzare 64 bit per trarne un vantaggio reale.


25
Questo non è applicabile solo a Windows?
Christopher DuBois,

4
> memory.limit () [1] Inf Messaggio di avviso: 'memory.limit ()' è specifico di Windows
LJT

L'uso di tibble invece di data.frame ci aiuta ancora meglio a risparmiare memoria?

32

Mi piace abbastanza la funzione oggetti migliorata sviluppata da Dirk. Gran parte del tempo, però, per me è sufficiente un output più semplice con il nome e la dimensione dell'oggetto. Ecco una funzione più semplice con un obiettivo simile. L'uso della memoria può essere ordinato in ordine alfabetico o in base alle dimensioni, può essere limitato a un determinato numero di oggetti e può essere ordinato in ordine crescente o decrescente. Inoltre, lavoro spesso con dati di 1 GB +, quindi la funzione cambia di conseguenza le unità.

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))
}

Ed ecco un esempio di output:

> 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

Non salvo mai un'area di lavoro R. Uso script di importazione e script di dati e output di oggetti dati particolarmente grandi che non voglio ricreare spesso su file. In questo modo inizio sempre con un nuovo spazio di lavoro e non ho bisogno di pulire oggetti di grandi dimensioni. Questa è una funzione molto bella.


30

Sfortunatamente non ho avuto il tempo di testarlo ampiamente, ma ecco un suggerimento sulla memoria che non avevo mai visto prima. Per me la memoria richiesta è stata ridotta di oltre il 50%. Quando leggi cose in R con ad esempio read.csv richiedono una certa quantità di memoria. Dopodiché puoi salvarli con save("Destinationfile",list=ls()) La prossima volta che apri R puoi usare load("Destinationfile") Ora l'utilizzo della memoria potrebbe essere diminuito. Sarebbe bello se qualcuno potesse confermare se questo produce risultati simili con un set di dati diverso.


4
si, ho provato lo stesso. Nel mio caso l'utilizzo della memoria scende anche al 30%. 1,5 GB di memoria utilizzata, salvata in .RData (~ 30 MB). Nuova sessione dopo il caricamento .RData utilizza meno di 500 MB di memoria.
f3lix,

Ho provato con 2 set di dati (100 MB e 2,7 GB) caricati in data.table utilizzando fread, quindi salvato in .RData. I file RData erano effettivamente più piccoli di circa il 70% ma dopo il nuovo caricamento, la memoria utilizzata era esattamente la stessa. Speravo che questo trucco riducesse il footprint di memoria ... mi sto perdendo qualcosa?
NoviceProg

@NoviceProg Non penso che ti manchi qualcosa, ma è un trucco, immagino che non funzionerà in tutte le situazioni. Nel mio caso, la memoria dopo il nuovo caricamento è stata effettivamente ridotta come descritto.
Dennis Jaheruddin

6
@NoviceProg Un paio di cose. Innanzitutto, seguire il credo di data.table è probabilmente più efficiente in termini di memoria nel caricamento dei file rispetto a read.csv. In secondo luogo, i risparmi di memoria che le persone notano qui hanno principalmente a che fare con la dimensione della memoria del processo R (che si espande per contenere oggetti e si ritrae quando si verifica la garbage collection). Tuttavia, Garbage Collection non rilascia sempre tutta la RAM al sistema operativo. L'arresto della sessione R e il caricamento dell'elemento dal punto in cui è stato memorizzato rilascerà quanta più RAM possibile ... ma se l'overhead era piccolo per cominciare ... nessun guadagno.
russellpierce

27

Per illustrare ulteriormente la strategia comune di riavvii frequenti, possiamo usare littler che ci consente di eseguire espressioni semplici direttamente dalla riga di comando. Ecco un esempio che a volte utilizzo per cronometrare BLAS diversi per un semplice crossprod.

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

Allo stesso modo,

 r -lMatrix -e'example(spMatrix)'

carica il pacchetto Matrix (tramite l'opzione --packages | -l) ed esegue gli esempi della funzione spMatrix. Poiché r inizia sempre "fresco", questo metodo è anche un buon test durante lo sviluppo del pacchetto.

Infine, ma non meno importante, funziona alla grande anche per la modalità batch automatizzata negli script usando l'intestazione shebang '#! / Usr / bin / r'. Rscript è un'alternativa in cui littler non è disponibile (ad esempio su Windows).


23

Per scopi sia di velocità che di memoria, durante la creazione di un frame di dati di grandi dimensioni tramite alcune serie complesse di passaggi, lo scaricherò periodicamente (il set di dati in corso in fase di creazione) su disco, aggiungendo a tutto ciò che è venuto prima e quindi riavvio . In questo modo i passaggi intermedi funzionano solo su frame di dati di dimensioni ridotte (il che è buono, ad esempio rbind rallenta considerevolmente con oggetti più grandi). L'intero set di dati può essere riletto alla fine del processo, quando tutti gli oggetti intermedi sono stati rimossi.

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 per notare che il data.tablepacchetto tables()sembra essere un buon sostituto della .ls.objects()funzione personalizzata di Dirk (dettagliata nelle risposte precedenti), anche se solo per data.frames / tables e non per esempio matrici, array, liste.


questo non elenca alcun data.frames quindi non è eccezionale
userJT

16
  1. Sono fortunato e i miei grandi set di dati vengono salvati dallo strumento in "blocchi" (sottoinsiemi) di circa 100 MB (binario a 32 bit). Pertanto, posso eseguire le fasi di pre-elaborazione (eliminazione di parti non informative, downsampling) in sequenza prima di fondere il set di dati.

  2. Chiamare gc ()"a mano" può aiutare se la dimensione dei dati si avvicina alla memoria disponibile.

  3. A volte un algoritmo diverso richiede molta meno memoria.
    A volte c'è un compromesso tra la vettorializzazione e l'uso della memoria.
    confronta: split& lapplycontro un forciclo.

  4. Per motivi di analisi dei dati semplice e veloce, spesso lavoro prima con un piccolo sottoinsieme casuale ( sample ()) di dati. Una volta terminato lo script di analisi dei dati / .Rnw, il codice di analisi dei dati e i dati completi vanno al server di calcolo per il calcolo per tutta la notte / durante il fine settimana / ....


11

L'uso di ambienti anziché elenchi per gestire raccolte di oggetti che occupano una quantità significativa di memoria di lavoro.

Il motivo: ogni volta un elemento di a list struttura viene modificato, l'intero elenco viene temporaneamente duplicato. Questo diventa un problema se il requisito di archiviazione dell'elenco è circa la metà della memoria di lavoro disponibile, perché i dati devono essere scambiati sul disco rigido lento. Gli ambienti, d'altra parte, non sono soggetti a questo comportamento e possono essere trattati in modo simile agli elenchi.

Ecco un esempio:

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))

In combinazione con strutture come big.matrixo data.tableche consentono di modificare il contenuto sul posto, è possibile ottenere un utilizzo della memoria molto efficiente.


6
Questo non è più vero: dall'avanzata R di Hadley , "Le modifiche a R 3.1.0 hanno reso questo uso [di ambienti] sostanzialmente meno importante perché la modifica di un elenco non fa più una copia profonda."
petrelharp,

8

La llfunzione nel gDatapacchetto può mostrare anche l'utilizzo della memoria di ciascun oggetto.

gdata::ll(unit='MB')

Non sul mio sistema: R versione 3.1.1 (2014-07-10), x86_64-pc-linux-gnu (64-bit), gdata_2.13.3, gtools_3.4.1.
krlmlr,

Hai ragione, l'ho provato una volta che è stato ordinato per caso!
user1436187

1
si prega di modificare la funzione per utilizzare Gb, Mb
userJT

7

Se vuoi davvero evitare le perdite, dovresti evitare di creare grandi oggetti nell'ambiente globale.

Quello che di solito faccio è avere una funzione che fa il lavoro e ritorna NULL- tutti i dati vengono letti e manipolati in questa funzione o in altri che chiama.


7

Con solo 4 GB di RAM (con Windows 10, quindi realizzane circa 2 o più realisticamente 1 GB) ho dovuto fare molta attenzione con l'allocazione.

Uso data.table quasi esclusivamente.

La funzione 'fread' ti consente di sottoinsieme le informazioni in base ai nomi dei campi durante l'importazione; importa solo i campi effettivamente necessari per cominciare. Se stai usando la lettura R di base, annulla le colonne spurie subito dopo l'importazione.

Come 42- suggerisce , sia possibile, eseguirò un sottoinsieme all'interno delle colonne immediatamente dopo aver importato le informazioni.

Spesso rm () oggetti dall'ambiente non appena non sono più necessari, ad es. Nella riga successiva dopo averli usati per sottoinsieme qualcos'altro, e chiamo gc ().

'fread' e 'fwrite' da data.table possono essere molto veloci rispetto alle letture e scritture di base R.

Come suggerisce kpierce8 , scrivo quasi sempre tutto fuori dall'ambiente e lo riporto, anche con migliaia / centinaia di migliaia di piccoli file da superare. Ciò non solo mantiene "pulito" l'ambiente e mantiene bassa l'allocazione di memoria ma, probabilmente a causa della grave mancanza di RAM disponibile, R ha una propensione a crash frequenti sul mio computer; molto frequentemente. Avere il backup delle informazioni sull'unità stessa mentre il codice avanza attraverso varie fasi significa che non devo ricominciare dall'inizio se si blocca.

A partire dal 2017, penso che gli SSD più veloci siano in esecuzione di alcuni GB al secondo attraverso la porta M2. Ho un SSD Kingston V300 (550MB / s) da 50 GB davvero di base che utilizzo come disco principale (con Windows e R). Conservo tutte le informazioni di massa su un piatto WD da 500 GB economico. Spingo i set di dati sull'SSD quando inizio a lavorarci. Questo, combinato con "impaurire" e "scrivere" tutto ha funzionato alla grande. Ho provato a usare 'ff' ma preferisco il primo. La velocità di lettura / scrittura 4K può creare problemi con questo; il backup di un quarto di milione di file 1k (per un valore di 250 MB) dall'unità SSD al piatto può richiedere ore. Per quanto ne so, non esiste ancora alcun pacchetto R in grado di ottimizzare automaticamente il processo di "chunkification"; ad esempio guarda quanta RAM ha un utente, testare le velocità di lettura / scrittura della RAM / di tutte le unità collegate e quindi suggerire un protocollo di "chunkification" ottimale. Ciò potrebbe produrre significativi miglioramenti del flusso di lavoro / ottimizzazioni delle risorse; ad es. dividerlo in ... MB per il ram -> dividerlo in ... MB per l'SSD -> dividerlo in ... MB sul piatto -> dividerlo in ... MB sul nastro. Potrebbe campionare in anticipo insiemi di dati per dargli uno stick di misurazione più realistico su cui lavorare.

Molti dei problemi a cui ho lavorato in R riguardano la formazione di coppie di combinazioni e di permutazione, triple ecc., Il che rende la RAM limitata più una limitazione poiché spesso si espandono almeno esponenzialmente ad un certo punto. Questo mi ha fatto concentrare molta attenzione sulla qualità piuttosto che sulla quantità di informazioni che entrano in esse per iniziare, piuttosto che cercare di ripulirla in seguito, e sulla sequenza di operazioni nella preparazione delle informazioni per iniziare (a partire da l'operazione più semplice e aumentando la complessità); ad es. sottoinsieme, quindi unisci / unisci, quindi forma combinazioni / permutazioni ecc.

Sembrano esserci dei vantaggi nell'utilizzare la lettura e la scrittura di base R in alcuni casi. Ad esempio, il rilevamento degli errori all'interno di 'fread' è così buono che può essere difficile provare a ottenere informazioni davvero disordinate in R per cominciare per ripulirle. Base R sembra anche molto più semplice se usi Linux. Base R sembra funzionare bene in Linux, Windows 10 utilizza ~ 20 GB di spazio su disco mentre Ubuntu ha bisogno solo di pochi GB, la RAM necessaria con Ubuntu è leggermente inferiore. Ma ho notato grandi quantità di avvisi ed errori durante l'installazione di pacchetti di terze parti in (L) Ubuntu. Non consiglierei di allontanarmi troppo da (L) Ubuntu o altre distribuzioni di titoli con Linux poiché puoi perdere così tanta compatibilità complessiva da rendere il processo quasi inutile (penso che 'unità' dovrebbe essere cancellata in Ubuntu dal 2017 ).

Spero che alcuni di questi possano aiutare gli altri.


5

Questo non aggiunge nulla a quanto sopra, ma è scritto nello stile semplice e fortemente commentato che mi piace. Produce una tabella con gli oggetti ordinati in dimensioni, ma senza alcuni dettagli forniti negli esempi sopra:

#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

Questa è una risposta più recente a questa eccellente vecchia domanda. Da 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

Se stai lavorando su Linux e desideri utilizzare diversi processi e devi solo eseguire operazioni di lettura su uno o più oggetti di grandi dimensioni, utilizza makeForkClusterinvece di makePSOCKcluster. Ciò consente anche di risparmiare tempo nell'invio dell'oggetto di grandi dimensioni agli altri processi.


2

Apprezzo molto alcune delle risposte di cui sopra, seguendo @hadley e @Dirk che suggeriscono di chiudere R, emettere sourcee utilizzare la riga di comando, trovo una soluzione che ha funzionato molto bene per me. Ho avuto a che fare con centinaia di extra di massa, ognuno occupa circa 20 Mb di memoria, quindi ho usato due script R, come segue:

Prima un involucro:

#!/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 questo script in pratica controllo ciò che fa il mio script principale runConsensus.re scrivo la risposta dei dati per l'output. Con questo, ogni volta che il wrapper chiama lo script sembra che la R venga riaperta e la memoria venga liberata.

Spero che sia d'aiuto.


2

Oltre alle più generali tecniche di gestione della memoria fornite nelle risposte sopra, cerco sempre di ridurre il più possibile le dimensioni dei miei oggetti. Ad esempio, lavoro con matrici molto grandi ma molto sparse, in altre parole matrici in cui la maggior parte dei valori sono zero. Utilizzando il pacchetto "Matrix" (importante per le maiuscole) sono stato in grado di ridurre le dimensioni medie degli oggetti da ~ 2 GB a ~ 200 MB semplicemente come:

my.matrix <- Matrix(my.matrix)

Il pacchetto Matrix include formati di dati che possono essere utilizzati esattamente come una matrice normale (non è necessario modificare l'altro codice) ma sono in grado di archiviare dati sparsi in modo molto più efficiente, sia caricati in memoria che salvati su disco.

Inoltre, i file non elaborati che ricevo sono in formato "lungo" in cui ogni punto dati ha variabili x, y, z, i. Molto più efficiente per trasformare i dati in un x * y * zarray di dimensioni con solo variabili i.

Conosci i tuoi dati e usa un po 'di buon senso.


2

Suggerimento per la gestione di oggetti che richiedono calcoli intermedi pesanti: quando si utilizzano oggetti che richiedono molti calcoli pesanti e passaggi intermedi per creare, trovo spesso utile scrivere un blocco di codice con la funzione per creare l'oggetto, quindi un blocco separato di codice che mi dà la possibilità di generare e salvare l'oggetto come rmdfile o di caricarlo esternamente da un rmdfile che ho già salvato in precedenza. Questo è particolarmente facile da R Markdownusare usando la seguente struttura di code-chunk.

```{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 questa struttura di codice, tutto ciò che devo fare è cambiare a LOADseconda che io voglia generare e salvare l'oggetto o caricarlo direttamente da un file salvato esistente. (Certo, devo generarlo e salvarlo la prima volta, ma dopo questo ho la possibilità di caricarlo.) L'impostazione LOAD = TRUEignora l'uso della mia complicata funzione ed evita tutto il pesante calcolo in esso. Questo metodo richiede ancora memoria sufficiente per archiviare l'oggetto di interesse, ma ti evita di doverlo calcolare ogni volta che esegui il codice. Per gli oggetti che richiedono molti calcoli pesanti di passaggi intermedi (ad es. Per calcoli che coinvolgono loop su array di grandi dimensioni), ciò può far risparmiare una notevole quantità di tempo e calcolo.


1

In esecuzione

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

di tanto in tanto aiuta anche R a liberare memoria inutilizzata ma non ancora rilasciata.


Cosa fa il forloop qui? Non c'è nessuno inella gcchiamata.
Umaomamaomao,

@qqq è lì solo per evitare di copiare e incollare gc(reset = T)nove volte
Marcelo Ventura,

14
Ma perché dovresti eseguirlo 9 volte? (curioso, non critico)
Umaomamaomao

1

Puoi anche ottenere dei benefici usando knitr e inserendo la tua sceneggiatura in blocchi Rmd.

Di solito divido il codice in diversi blocchi e seleziono quale salverà un checkpoint nella cache o in un file RDS, e

Laggiù puoi impostare un blocco da salvare in "cache", oppure puoi decidere di eseguirlo o meno un blocco particolare. In questo modo, in una prima esecuzione è possibile elaborare solo "parte 1", un'altra esecuzione è possibile selezionare solo "parte 2", ecc.

Esempio:

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)
```

Come effetto collaterale, anche questo potrebbe farti venire il mal di testa in termini di riproducibilità :)


1

Sulla base della risposta di @ Dirk e @ Tony ho fatto un leggero aggiornamento. Il risultato è stato presentato [1]prima dei valori di dimensioni piuttosto elevate, quindi ho eliminato quello capture.outputche ha risolto il 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

Cerco di mantenere piccola la quantità di oggetti quando si lavora in un progetto più ampio con molti passaggi intermedi. Quindi, invece di creare molti oggetti unici chiamati

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

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

Lavoro con oggetti temporanei che chiamo temp.

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

Il che mi lascia con meno file intermedi e più panoramica.

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

Per risparmiare più memoria, posso semplicemente rimuoverlo tempquando non è più necessario.

rm(temp)

Se ho bisogno di diversi file intermedi, io uso temp1, temp2, temp3.

Per testare io uso test, test2...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.