wake-up-neo.net

Tricks, um den verfügbaren Speicher in einer R-Sitzung zu verwalten

Welche Tricks verwenden die Benutzer, um den verfügbaren Speicher einer interaktiven R-Sitzung zu verwalten? Ich benutze die folgenden Funktionen [basierend auf Beiträgen von Petr Pikal und David Hinds zur r-help-Liste im Jahr 2004], um die größten Objekte aufzulisten (und/oder zu sortieren) und gelegentlich rm() einige von ihnen. Aber die mit Abstand effektivste Lösung war ... unter 64-Bit-Linux mit viel Speicher zu laufen.

Möchtest du andere nette Tricks mit anderen teilen? Einen pro Post bitte.

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

Um die übliche Strategie häufiger Neustarts weiter zu veranschaulichen, können wir littler verwenden, um einfache Ausdrücke direkt über die Befehlszeile auszuführen. Hier ist ein Beispiel, das ich manchmal benutze, um verschiedene BLAS für einen einfachen CrossProd zu messen.

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

Gleichfalls,

 r -lMatrix -e'example(spMatrix)'

lädt das Matrix-Paket (über den Schalter --packages | -l) und führt die Beispiele der spMatrix-Funktion aus. Da r immer "frisch" beginnt, ist diese Methode auch ein guter Test bei der Paketentwicklung.

Last but not least eignet sich r auch hervorragend für den automatisierten Batch-Modus in Skripten, die den Shebang-Header '#!/Usr/bin/r' verwenden. Rscript ist eine Alternative, bei der weniger verfügbar ist (z. B. unter Windows).

25

Stellen Sie sicher, dass Sie Ihre Arbeit in einem reproduzierbaren Skript aufzeichnen. Öffnen Sie von Zeit zu Zeit R und dann source() Ihr Skript. Sie bereinigen alles, was Sie nicht mehr verwenden, und haben als zusätzlichen Vorteil Ihren Code getestet.

186
hadley

Ich benutze das Paket data.table . Mit := Operator können Sie:

  • Spalten nach Verweis hinzufügen
  • Ändern Sie Teilmengen vorhandener Spalten nach Verweis und nach Gruppe nach Verweis
  • Spalten nach Verweis löschen

Keine dieser Operationen kopiert das (potenziell große) data.table überhaupt nicht einmal.

  • Die Aggregation ist auch besonders schnell, weil data.table verbraucht viel weniger Arbeitsspeicher.

Verwandte Links :

154
Matt Dowle

Hab das auf einem Twitter-Post gesehen und finde es eine großartige Funktion von Dirk! In Anlehnung an die Antwort von JD Long würde ich dies zum benutzerfreundlichen Lesen tun:

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

Was zu so etwas wie Folgendem führt:

                      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

ANMERKUNG: Der Hauptteil, den ich hinzufügte, war (wieder angepasst von JDs Antwort):

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

Ich benutze den Parameter subset aggressiv, indem ich nur die erforderlichen Variablen auswähle, wenn ich Datenrahmen an das Argument data= Der Regressionsfunktionen übergebe. Es führt zu einigen Fehlern, wenn ich vergesse, Variablen sowohl zur Formel als auch zum select= - Vektor hinzuzufügen, aber es spart trotzdem viel Zeit aufgrund des verringerten Kopierens von Objekten und verringert den Speicherbedarf erheblich. Angenommen, ich habe 4 Millionen Datensätze mit 110 Variablen (und das tue ich). Beispiel:

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

Zur Festlegung des Kontexts und der Strategie: Die Variable gdlab2 Ist ein logischer Vektor, der für Probanden in einem Datensatz erstellt wurde, der alle normalen oder fast normalen Werte für eine Reihe von Labortests und HIVfinal aufwies. war ein Zeichenvektor, der vorläufige und bestätigende Tests auf HIV zusammenfasste.

48
42-

Ich liebe das .ls.objects () -Skript von Dirk, aber ich habe weiter geschielt, um die Zeichen in der Größenspalte zu zählen. Also habe ich ein paar hässliche Hacks gemacht, um es mit hübscher Formatierung für die Größe zu präsentieren:

.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
}
48
JD Long

Das ist ein guter Trick.

Ein weiterer Vorschlag besteht darin, speichereffiziente Objekte zu verwenden, wo immer dies möglich ist: Verwenden Sie beispielsweise eine Matrix anstelle eines data.frame.

Damit wird die Speicherverwaltung nicht wirklich angesprochen, aber eine wichtige Funktion, die nicht allgemein bekannt ist, ist memory.limit (). Sie können den Standardwert mithilfe des Befehls memory.limit (size = 2500) erhöhen, wobei die Größe in MB angegeben wird. Wie Dirk bereits erwähnt hat, müssen Sie 64-Bit verwenden, um dies wirklich nutzen zu können.

33
Shane

Mir gefällt die von Dirk entwickelte verbesserte Objektfunktion sehr gut. In den meisten Fällen reicht mir jedoch eine einfachere Ausgabe mit dem Objektnamen und der Objektgröße aus. Hier ist eine einfachere Funktion mit einem ähnlichen Ziel. Die Speichernutzung kann alphabetisch oder nach Größe geordnet, auf eine bestimmte Anzahl von Objekten begrenzt und auf- oder absteigend sortiert werden. Außerdem arbeite ich oft mit Daten von über 1 GB, sodass die Funktion die Einheiten entsprechend ändert.

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

Und hier ist ein Beispiel für die Ausgabe:

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

Leider hatte ich keine Zeit, es ausgiebig zu testen, aber hier ist ein Erinnerungstipp, den ich vorher nicht gesehen habe. Bei mir wurde der benötigte Speicher um mehr als 50% reduziert. Wenn Sie etwas mit read.csv in R einlesen, benötigen diese eine bestimmte Menge an Speicher. Danach können Sie sie mit save("Destinationfile",list=ls()) speichern. Wenn Sie R das nächste Mal öffnen, können Sie load("Destinationfile") verwenden. Jetzt ist möglicherweise weniger Speicher belegt. Es wäre schön, wenn jemand bestätigen könnte, ob dies zu ähnlichen Ergebnissen mit einem anderen Datensatz führt.

30

Ich speichere niemals einen R-Arbeitsbereich. Ich verwende Importskripte und Datenskripte und gebe besonders große Datenobjekte, die ich nicht oft neu erstellen möchte, in Dateien aus. Auf diese Weise beginne ich immer mit einem neuen Arbeitsbereich und muss keine großen Gegenstände ausräumen. Das ist allerdings eine sehr schöne Funktion.

29
kpierce8

Sowohl aus Gründen der Geschwindigkeit als auch des Speichers werde ich beim Erstellen eines großen Datenrahmens über einige komplexe Abfolgen von Schritten diesen (den gerade erstellten Datensatz) in regelmäßigen Abständen auf die Festplatte spülen, an alle vorherigen Daten anhängen und ihn anschließend neu starten . Auf diese Weise funktionieren die Zwischenschritte nur bei kleinen Datenrahmen (was gut ist, da beispielsweise rbind bei größeren Objekten erheblich verlangsamt wird). Am Ende des Vorgangs kann der gesamte Datensatz wieder eingelesen werden, wenn alle Zwischenobjekte entfernt wurden.

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 )
23
Ben B-L

Zu beachten ist, dass das tables() des data.table - Pakets ein ziemlich guter Ersatz für die .ls.objects() benutzerdefinierte Funktion von Dirk zu sein scheint (ausführlich in früheren Antworten), obwohl dies nur für data.frames/gilt. Tabellen und nicht z Matrizen, Arrays, Listen.

17
geotheory
  1. Ich habe Glück und meine großen Datenmengen werden vom Instrument in "Chunks" (Teilmengen) von ungefähr 100 MB (32-Bit-Binär) gespeichert. Auf diese Weise kann ich die Vorverarbeitungsschritte (Löschen nicht informativer Teile, Downsampling) nacheinander ausführen, bevor der Datensatz fusioniert wird.

  2. Das Aufrufen von gc () "von Hand" kann hilfreich sein, wenn sich die Größe der Daten dem verfügbaren Speicher nähert.

  3. Manchmal benötigt ein anderer Algorithmus viel weniger Speicher.
    Manchmal gibt es einen Kompromiss zwischen Vektorisierung und Speichernutzung.
    Vergleiche: split & lapply mit einer for Schleife.

  4. Um die Daten schnell und einfach analysieren zu können, arbeite ich häufig zuerst mit einer kleinen zufälligen Teilmenge (sample ()) der Daten. Sobald das Datenanalyseskript/.Rnw fertig ist, werden der Datenanalysecode und die vollständigen Daten zur Berechnung über Nacht/über Wochenende/... an den Berechnungsserver gesendet.

14
cbeleites

Die Verwendung von Umgebungen anstelle von Listen zur Verarbeitung von Sammlungen von Objekten, die einen erheblichen Arbeitsspeicher belegen.

Der Grund: Jedes Mal, wenn ein Element einer list -Struktur geändert wird, wird die gesamte Liste vorübergehend dupliziert. Dies wird zu einem Problem, wenn der Speicherbedarf der Liste etwa die Hälfte des verfügbaren Arbeitsspeichers beträgt, da dann Daten auf die langsame Festplatte übertragen werden müssen. Umgebungen hingegen unterliegen nicht diesem Verhalten und können ähnlich wie Listen behandelt werden.

Hier ist ein Beispiel:

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 Verbindung mit Strukturen wie big.matrix oder data.table, die es ermöglichen, ihren Inhalt an Ort und Stelle zu ändern, kann eine sehr effiziente Speichernutzung erreicht werden.

11
Georg Schnabel

Die Funktion ll im Paket gData kann auch die Speichernutzung jedes Objekts anzeigen.

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

Mit nur 4 GB RAM (unter Windows 10, also machen Sie das ungefähr 2 oder realistischer 1 GB) musste ich wirklich vorsichtig mit der Zuordnung sein.

Ich verwende fast ausschließlich data.table.

Mit der Funktion "fread" können Sie Informationen beim Import nach Feldnamen unterteilen. importieren Sie nur die Felder, die zu Beginn tatsächlich benötigt werden. Wenn Sie den Basis-R-Lesevorgang verwenden, setzen Sie die falschen Spalten sofort nach dem Import auf Null.

Wie 42 - andeutet, werde ich dann, wo immer möglich, unmittelbar nach dem Importieren der Informationen eine Teilmenge innerhalb der Spalten erstellen.

Ich rme häufig () Objekte aus der Umgebung, sobald sie nicht mehr benötigt werden, z. in der nächsten Zeile, nachdem Sie sie für eine andere Teilmenge verwendet haben, und rufen Sie gc () auf.

'fread' und 'fwrite' aus data.table können sehr schnell sein, verglichen mit den Lese- und Schreibvorgängen der Basis R.

Wie kpierce8 andeutet, schreibe ich fast immer alles aus der Umgebung heraus und spanne es wieder ein, selbst wenn Tausende/Hunderttausende kleiner Dateien durchkommen müssen . Dies hält nicht nur die Umgebung 'sauber' und die Speicherzuordnung niedrig, sondern möglicherweise aufgrund des erheblichen Mangels an RAM neigt R dazu, häufig auf meinem Computer abstürzen zu müssen. wirklich häufig. Wenn die Informationen auf dem Laufwerk selbst gesichert werden, während der Code verschiedene Phasen durchläuft, muss ich nicht von vorne beginnen, wenn es abstürzt.

Ab 2017, denke ich, laufen die schnellsten SSDs mit ein paar GB pro Sekunde über den M2-Port. Ich besitze eine wirklich einfache 50-GB-SSD von Kingston V300 (550 MB/s), die ich als primäre Festplatte verwende (auf der Windows und R installiert sind). Ich behalte alle wichtigen Informationen zu einem billigen 500-GB-WD-Plattenteller. Ich verschiebe die Datensätze auf die SSD, wenn ich anfange, daran zu arbeiten. In Kombination mit "freading" und "fwrite" hat alles wunderbar geklappt. Ich habe versucht, 'ff' zu verwenden, bevorzuge jedoch das erstere. 4K-Lese-/Schreibgeschwindigkeiten können jedoch zu Problemen führen. Das Sichern von einer Viertelmillion 1k-Dateien (250 MB) von der SSD auf den Platter kann Stunden dauern. Soweit mir bekannt ist, steht noch kein R-Paket zur Verfügung, mit dem der "Chunkification" -Prozess automatisch optimiert werden kann. z.B. Sehen Sie sich an, wie viel RAM ein Benutzer hat, testen Sie die Lese-/Schreibgeschwindigkeiten von RAM/allen angeschlossenen Laufwerken und schlagen Sie dann ein optimales Blockierungsprotokoll vor. Dies könnte einige signifikante Verbesserungen des Workflows/Ressourcenoptimierungen bewirken. z.B. Teilen Sie es auf ... MB für den RAM -> Teilen Sie es auf ... MB für die SSD -> Teilen Sie es auf ... MB auf dem Platter -> Teilen Sie es auf ... MB auf dem Band. Zuvor könnten Datensätze abgetastet werden, um einen realistischeren Maßstab für die Arbeit zu erhalten.

Viele der Probleme, an denen ich in R gearbeitet habe, bestehen darin, Kombinations- und Permutationspaare, Tripel usw. zu bilden, was die Begrenzung von RAM nur noch mehr zu einer Einschränkung macht, wie sie es häufig tun werden mindestens irgendwann exponentiell ausdehnen. Dies hat mich dazu gebracht, viel Aufmerksamkeit auf die Qualität im Gegensatz zu Quantität von Informationen, die von Anfang an in sie eingehen, anstatt danach zu versuchen, sie zu bereinigen, und von der Abfolge der Vorgänge bei der Vorbereitung der Informationen (beginnend mit dem einfachsten Vorgang und zunehmender Komplexität); z.B. Teilmenge, dann Zusammenführen/Verbinden, dann Kombinationen/Permutationen bilden usw.

In einigen Fällen scheint die Verwendung von Base-R-Lese- und Schreibvorgängen einige Vorteile zu bieten. Zum Beispiel ist die Fehlererkennung in 'fread' so gut, dass es schwierig sein kann, wirklich unordentliche Informationen in R zu bekommen, um sie zu bereinigen. Base R scheint auch viel einfacher zu sein, wenn Sie Linux verwenden. Base R scheint unter Linux gut zu funktionieren, Windows 10 benötigt ~ 20 GB Festplattenspeicher, während Ubuntu nur wenige GB benötigt, der RAM, der mit Ubuntu benötigt wird, ist etwas geringer. Bei der Installation von Paketen von Drittanbietern in (L) Ubuntu sind mir jedoch zahlreiche Warnungen und Fehler aufgefallen. Ich würde nicht empfehlen, zu weit weg von (L) Ubuntu oder anderen Stock-Distributionen mit Linux zu driften, da Sie so viel Gesamtkompatibilität verlieren können, dass der Prozess nahezu sinnlos wird (ich denke, dass "Unity" in Ubuntu ab 2017 abgebrochen wird) ). Mir ist klar, dass dies bei einigen Linux-Benutzern nicht gut ankommt, aber einige der benutzerdefinierten Distributionen sind jenseits aller Neuerungen grenzwertig (ich habe Jahre damit verbracht, nur Linux zu verwenden).

Hoffentlich könnte ein Teil davon anderen helfen.

6
bg49ag

Wenn Sie die Lecks wirklich vermeiden möchten, sollten Sie vermeiden, große Objekte in der globalen Umgebung zu erstellen.

Was ich normalerweise mache, ist eine Funktion, die die Arbeit erledigt und NULL zurückgibt - alle Daten werden in dieser Funktion oder anderen, die sie aufruft, gelesen und bearbeitet.

6
Alexander Radev

Dies fügt dem Obigen nichts hinzu, ist aber in dem einfachen und stark kommentierten Stil geschrieben, den ich mag. Es wird eine Tabelle mit den in der Größe geordneten Objekten angezeigt, jedoch ohne die in den obigen Beispielen angegebenen Details:

#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
JamesF

Dies ist eine neuere Antwort auf diese ausgezeichnete alte Frage. Aus Hadleys 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 )

4
Chris Beeley

Wenn Sie an Linux arbeiten und mehrere Prozesse verwenden möchten und nur read Operationen an einem oder mehreren großen Objekten ausführen müssen benutze makeForkCluster anstelle von makePSOCKcluster. Dadurch sparen Sie auch die Zeit, die Sie für das Senden des großen Objekts an die anderen Prozesse benötigen.

3
gdkrmr

Ich schätze einige der obigen Antworten sehr, wenn ich @hadley und @Dirk folge, die darauf hindeuten, R zu schließen, source auszugeben und die Befehlszeile zu verwenden. Ich habe eine Lösung gefunden, die für mich sehr gut funktioniert hat. Ich musste mich mit Hunderten von Massenspektren auseinandersetzen, die jeweils etwa 20 MB Speicher belegen. Daher habe ich zwei R-Skripte wie folgt verwendet:

Zuerst ein Wrapper:

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

mit diesem Skript steuere ich im Grunde, was mein Hauptskript macht runConsensus.r, und ich schreibe die Datenantwort für die Ausgabe. Jedes Mal, wenn der Wrapper das Skript aufruft, scheint das R erneut geöffnet und der Speicher freigegeben zu werden.

Ich hoffe es hilft.

2
user1265067

Tipp für den Umgang mit Objekten, die umfangreiche Zwischenberechnungen erfordern: Wenn Sie Objekte verwenden, für deren Erstellung umfangreiche Berechnungen und Zwischenschritte erforderlich sind, finde ich es häufig hilfreich, mit der Funktion zum Erstellen des Codes einen Teil des Codes zu schreiben object und dann ein separater Codeabschnitt, mit dem ich das Objekt entweder als rmd -Datei generieren und speichern oder extern aus einer rmd -Datei laden kann, die ich bereits zuvor gespeichert habe. Dies ist besonders einfach in R Markdown mit der folgenden Code-Chunk-Struktur.

```{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); }

```

Bei dieser Codestruktur muss ich nur LOAD ändern, je nachdem, ob ich das Objekt generieren und speichern oder es direkt aus einer vorhandenen gespeicherten Datei laden möchte. (Natürlich muss ich es erst generieren und speichern, aber danach habe ich die Möglichkeit, es zu laden.) Setting LOAD = TRUE umgeht die Verwendung meiner komplizierten Funktion und vermeidet die darin enthaltenen umfangreichen Berechnungen. Diese Methode benötigt immer noch genügend Speicher, um das gewünschte Objekt zu speichern. Sie müssen es jedoch nicht jedes Mal berechnen, wenn Sie den Code ausführen. Für Objekte, die eine große Menge an Berechnungen von Zwischenschritten erfordern (z. B. für Berechnungen mit Schleifen über große Arrays), kann dies einen erheblichen Zeit- und Rechenaufwand einsparen.

2
Ben

Neben den allgemeineren Speicherverwaltungstechniken in den obigen Antworten versuche ich immer, die Größe meiner Objekte so weit wie möglich zu reduzieren. Zum Beispiel arbeite ich mit sehr großen, aber sehr spärlichen Matrizen, mit anderen Worten Matrizen, bei denen die meisten Werte Null sind. Mit dem 'Matrix'-Paket (Großschreibung wichtig) konnte ich meine durchschnittliche Objektgröße von ~ 2 GB auf ~ 200 MB reduzieren, und zwar so einfach wie:

my.matrix <- Matrix(my.matrix)

Das Matrix-Paket enthält Datenformate, die genau wie eine reguläre Matrix verwendet werden können (ohne dass Sie den anderen Code ändern müssen), aber in der Lage sind, spärliche Daten effizienter zu speichern, unabhängig davon, ob sie in den Speicher geladen oder auf der Festplatte gespeichert werden.

Außerdem sind die Rohdateien, die ich erhalte, im Langformat, wobei jeder Datenpunkt Variablen enthält x, y, z, i. Viel effizienter ist es, die Daten in ein Dimensionsarray x * y * z Mit nur der Variablen i umzuwandeln.

Kennen Sie Ihre Daten und verwenden Sie ein wenig gesunden Menschenverstand.

2
D Greenwood

Sie können auch Vorteile erzielen, wenn Sie knitr verwenden und Ihr Skript in Rmd-Chuncks ablegen.

Normalerweise teile ich den Code in verschiedene Abschnitte auf und wähle aus, welcher einen Checkpoint im Cache oder in einer RDS-Datei speichert

Dort können Sie einen Block festlegen, der im "Cache" gespeichert werden soll, oder Sie können entscheiden, ob ein bestimmter Block ausgeführt werden soll oder nicht. Auf diese Weise können Sie in einem ersten Durchgang nur "Teil 1" verarbeiten, in einem anderen Durchgang können Sie nur "Teil 2" auswählen usw.

Beispiel:

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

Als Nebeneffekt könnten Sie dadurch auch einige Kopfschmerzen in Bezug auf die Reproduzierbarkeit sparen :)

1
Matias Thayer

Basierend auf der Antwort von @ Dirk und @ Tony habe ich ein kleines Update gemacht. Das Ergebnis war die Ausgabe von [1] vor den hübschen Größenwerten, also habe ich den capture.output was das Problem gelöst hat:

.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
ilyas

Ich versuche, die Anzahl der Objekte klein zu halten, wenn ich in einem größeren Projekt mit vielen Zwischenschritten arbeite. Anstatt also viele einzigartige Objekte zu erstellen, werden diese aufgerufen

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

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

Ich arbeite mit temporären Objekten, die ich temp nenne.

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

Dadurch habe ich weniger Zwischendateien und mehr Übersicht.

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

Um mehr Speicherplatz zu sparen, kann ich temp einfach entfernen, wenn ich ihn nicht mehr benötige.

rm(temp)

Wenn ich mehrere Zwischendateien benötige, verwende ich temp1, temp2, temp3.

Zum Testen benutze ich test, test2, ...

0
mace

Laufen

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

von Zeit zu Zeit hilft R auch dabei, ungenutzten, aber noch nicht freigegebenen Speicher freizugeben.

0
Marcelo Ventura