Tricks zum Verwalten des verfügbaren Speichers in einer R-Sitzung

490

Welche Tricks verwenden 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 in der R-Help-Liste von 2004], um die größten Objekte aufzulisten (und / oder zu sortieren) und gelegentlichrm() einige davon. Aber bei weitem die effektivste Lösung war ... unter 64-Bit-Linux mit viel Speicher zu laufen.

Irgendwelche anderen netten Tricks, die Leute teilen wollen? Eine 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)
}
Dirk Eddelbuettel
quelle
Beachten Sie, ich bezweifle es NICHT, aber wozu dient das? Ich bin ziemlich neu in Bezug auf Speicherprobleme in R, aber ich habe in letzter Zeit einige (deshalb habe ich nach diesem Beitrag gesucht :) - also fange ich gerade mit all dem an. Wie hilft das meiner täglichen Arbeit?
Matt Bannert
4
Wenn Sie die Objekte in einer Funktion sehen möchten, müssen Sie Folgendes verwenden: lsos (pos = environment ()), andernfalls werden nur globale Variablen angezeigt. So schreiben Sie in den Standardfehler: write.table (lsos (pos = environment ()), stderr (), quote = FALSE, sep = '\ t')
Michael Kuhn
Warum 64-Bit-Linux und nicht 64-Bit-Windows? Macht die Wahl des Betriebssystems einen nicht trivialen Unterschied, wenn ich 32 GB RAM zur Verfügung habe?
Jase
3
@pepsimax: Dies wurde in das multilevelPSAPaket gepackt . Das Paket ist für etwas anderes konzipiert, aber Sie können die Funktion von dort aus verwenden, ohne das Paket zu laden, indem Sie sagen requireNamespace(multilevelPSA); multilevelPSA::lsos(...). Oder im DmiscPaket (nicht auf CRAN).
krlmlr
1
Wenn der Datensatz eine überschaubare Größe hat, gehe ich normalerweise zu R Studio> Umgebung> Rasteransicht. Hier können Sie alle Elemente in Ihrer aktuellen Umgebung nach Größe anzeigen und sortieren.
KRazzy R

Antworten:

197

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

Hadley
quelle
58
Meine Strategie besteht darin, meine Skripte nach dem Vorbild von load.R und do.R aufzuteilen, wobei load.R einige Zeit in Anspruch nehmen kann, um Daten aus Dateien oder einer Datenbank zu laden, und nur ein Minimum an Vorverarbeitung / Zusammenführung von diese Daten. Die letzte Zeile von load.R dient zum Speichern des Arbeitsbereichsstatus. Dann ist do.R mein Notizblock, mit dem ich meine Analysefunktionen ausbaue. Ich lade do.R häufig neu (mit oder ohne den Arbeitsbereichsstatus nach Bedarf von load.R neu zu laden).
Josh Reich
32
Das ist eine gute Technik. Wenn Sie Dateien in einer bestimmten Reihenfolge so ausgeführt werden, als Präfix ich sie oft mit einer Zahl: 1-load.r, 2-explore.r, 3-model.r- auf diese Weise an anderen offensichtlich ist , dass es eine gewisse Ordnung vorhanden.
Hadley
4
Ich kann diese Idee nicht genug unterstützen. Ich habe ein paar Leuten R beigebracht und dies ist eines der ersten Dinge, die ich sage. Dies gilt auch für jede Sprache, in der die Entwicklung eine REPL und eine zu bearbeitende Datei enthält (z. B. Python). rm (ls = list ()) und source () funktionieren ebenfalls, aber das erneute Öffnen ist besser (Pakete werden ebenfalls gelöscht).
Vince
53
Die Tatsache, dass die am besten gewählte Antwort einen Neustart von R beinhaltet, ist die schlimmste mögliche Kritik an R.
SDS
7
@ MartínBel, der nur Objekte entfernt, die in der globalen Umgebung erstellt wurden. Es werden keine Pakete oder S4-Objekte oder viele andere Dinge entladen.
Hadley
160

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

  • Fügen Sie Spalten als Referenz hinzu
  • Ändern Sie Teilmengen vorhandener Spalten nach Referenz und nach Gruppe nach Referenz
  • Spalten durch Referenz löschen

Keine dieser Operationen kopiert die (möglicherweise große) data.tableüberhaupt, nicht einmal.

  • Die Aggregation ist auch besonders schnell, da data.tableviel weniger Arbeitsspeicher benötigt wird.

Verwandte Links :

Matt Dowle
quelle
109

Hab das auf einem Twitter-Post gesehen und finde, es ist 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 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

HINWEIS: Der Hauptteil, den ich hinzugefügt habe, war (wiederum angepasst an JDs Antwort):

obj.prettysize <- napply(names, function(x) {
                           print(object.size(x), units = "auto") })
Tony Breyal
quelle
Kann diese Funktion zu dplyr oder einem anderen Schlüsselpaket hinzugefügt werden?
userJT
1
Erwähnenswert ist, dass (zumindest mit base-3.3.2) capture.outputkeine Notwendigkeit mehr besteht und obj.prettysize <- napply(names, function(x) {format(utils::object.size(x), units = "auto") })eine saubere Ausgabe erzeugt. Wenn Sie es nicht entfernen, werden unerwünschte Anführungszeichen in der Ausgabe erzeugt, dh [1] "792.5 Mb"anstelle von 792.5 Mb.
Nutle
@Nutle Ausgezeichnet, ich habe den Code entsprechend aktualisiert :)
Tony Breyal
Ich würde auch zu wechseln obj.class <- napply(names, function(x) as.character(class(x))[1]), obj.class <- napply(names, function(x) class(x)[1]) da ich classjetzt immer einen Vektor von Zeichen zurückgebe (base-3.5.0).
DeltaIV
49

Ich benutze den subsetParameter aggressiv, indem ich nur die erforderlichen Variablen auswähle, wenn ich Datenrahmen an das data=Argument der Regressionsfunktionen übergebe. Es führt zwar zu einigen Fehlern, wenn ich vergesse, Variablen sowohl zur Formel als auch zum select=Vektor hinzuzufügen , spart jedoch aufgrund des verringerten Kopierens von Objekten viel Zeit und verringert den Speicherbedarf erheblich. Angenommen, ich habe 4 Millionen Datensätze mit 110 Variablen (und das tue ich auch). 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 gdlab2Variable 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 aufwies, und HIVfinalein Zeichenvektor, der vorläufige und bestätigende Tests auf HIV zusammenfasste .

IRTFM
quelle
48

Ich liebe Dirks .ls.objects () -Skript, aber ich blinzelte weiter, 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
}
JD Long
quelle
34

Das ist ein guter Trick.

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

Dies betrifft nicht wirklich die Speicherverwaltung, aber eine wichtige Funktion, die nicht allgemein bekannt ist, ist memory.limit (). Sie können die Standardeinstellung mit dem Befehl memory.limit (Größe = 2500) erhöhen, wobei die Größe in MB angegeben ist. Wie Dirk bereits erwähnt hat, müssen Sie 64-Bit verwenden, um dies wirklich nutzen zu können.

Shane
quelle
25
Gilt das nicht nur für Windows?
Christopher DuBois
4
> memory.limit () [1] Inf Warnmeldung: 'memory.limit ()' ist Windows-spezifisch
LJT
Hilft uns die Verwendung von tibble anstelle von data.frame noch besser, um Speicherplatz zu sparen?
32

Mir gefällt die von Dirk entwickelte verbesserte Objektfunktion sehr gut. In den meisten Fällen reicht mir jedoch eine grundlegendere Ausgabe mit dem Objektnamen und der Größe aus. Hier ist eine einfachere Funktion mit einem ähnlichen Ziel. Die Speichernutzung kann alphabetisch oder nach Größe sortiert, auf eine bestimmte Anzahl von Objekten beschränkt und aufsteigend oder absteigend sortiert werden. Außerdem arbeite ich oft mit Daten, die 1 GB + groß sind, 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 eine Beispielausgabe:

> 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
Michael Hallquist
quelle
30

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

kpierce8
quelle
30

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

Dennis Jaheruddin
quelle
4
Ja, ich habe das gleiche erlebt. Die Speichernutzung sinkt in meinem Fall sogar auf 30%. 1,5 GB Speicher verwendet, gespeichert in .RData (~ 30 MB). Neue Sitzung nach dem Laden von .RData verwendet weniger als 500 MB Speicher.
f3lix
Ich habe versucht, mit 2 Datensätzen (100 MB und 2,7 GB) in data.table zu laden freadund dann in .RData zu speichern. Die RData-Dateien waren zwar etwa 70% kleiner, aber nach dem erneuten Laden war der verwendete Speicher genau der gleiche. Hatte ich gehofft, dass dieser Trick den Speicherbedarf verringert? Vermisse ich etwas?
NoviceProg
@NoviceProg Ich glaube nicht, dass dir etwas fehlt, aber es ist ein Trick, ich denke, es wird nicht in allen Situationen funktionieren. In meinem Fall wurde der Speicher nach dem erneuten Laden tatsächlich wie beschrieben reduziert.
Dennis Jaheruddin
6
@ NoviceProg Ein paar Dinge. Erstens ist das Befolgen des Credo von data.table beim Laden von Dateien wahrscheinlich speichereffizienter als read.csv. Zweitens haben die Speichereinsparungen, die hier festgestellt werden, hauptsächlich mit der Speichergröße des R-Prozesses zu tun (der sich erweitert, um Objekte zu halten, und sich zurückzieht, wenn die Speicherbereinigung stattfindet). Die Speicherbereinigung gibt jedoch nicht immer den gesamten Arbeitsspeicher an das Betriebssystem zurück. Wenn Sie die R-Sitzung stoppen und das Element von dem Ort laden, an dem es gespeichert wurde, wird so viel RAM wie möglich freigegeben ... aber wenn der Overhead anfangs gering war ... kein Gewinn.
Russellpierce
27

Um die gängige Strategie häufiger Neustarts weiter zu veranschaulichen, können wir Littler verwenden , mit dem wir einfache Ausdrücke direkt über die Befehlszeile ausführen können. Hier ist ein Beispiel, mit dem ich manchmal verschiedene BLAS für ein einfaches Crossprod zeitlich festlege.

 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" startet, ist diese Methode auch ein guter Test während der Paketentwicklung.

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

Dirk Eddelbuettel
quelle
23

Wenn Sie aus Gründen der Geschwindigkeit und des Speichers einen großen Datenrahmen über eine komplexe Reihe von Schritten erstellen, werde ich ihn regelmäßig (den in Bearbeitung befindlichen Datensatz, der gerade erstellt wird) auf die Festplatte leeren, an alle vorherigen Daten anhängen und ihn dann neu starten . Auf diese Weise arbeiten die Zwischenschritte nur bei kleineren Datenrahmen (was gut ist, da z. B. rbind bei größeren Objekten erheblich langsamer wird). Der gesamte Datensatz kann am Ende des Prozesses zurückgelesen 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 )
Ben BL
quelle
17

Nur um zu beachten, dass data.tablePakete tables()ein ziemlich guter Ersatz für Dirks .ls.objects()benutzerdefinierte Funktion zu sein scheinen (detailliert in früheren Antworten), obwohl nur für data.frames / Tabellen und nicht zB Matrizen, Arrays, Listen.

Geotheorie
quelle
Dies listet keine data.frames auf, also ist es nicht so toll
userJT
16
  1. Ich habe Glück und meine großen Datenmengen werden vom Instrument in "Chunks" (Teilmengen) von ungefähr 100 MB (32-Bit-Binärdatei) gespeichert. Auf diese Weise kann ich Vorverarbeitungsschritte (Löschen nicht informativer Teile, Downsampling) nacheinander ausführen, bevor der Datensatz zusammengeführt wird.

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

  3. Manchmal benötigt ein anderer Algorithmus viel weniger Speicher.
    Manchmal gibt es einen Kompromiss zwischen Vektorisierung und Speichernutzung.
    Vergleiche: split& lapplygegen eine forSchleife.

  4. Um eine schnelle und einfache Datenanalyse zu ermöglichen, arbeite ich häufig zuerst mit einer kleinen zufälligen Teilmenge ( sample ()) der Daten. Sobald das Datenanalyseskript / .Rnw fertig ist, werden die Datenanalysecodes und die vollständigen Daten zur Berechnung über Nacht / über Wochenende / ... an den Berechnungsserver gesendet.

cbeleites unzufrieden mit SX
quelle
11

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 listStruktur 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 diesem Verhalten nicht 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.matrixoder, data.tabledie es ermöglichen, ihren Inhalt an Ort und Stelle zu ändern, kann eine sehr effiziente Speichernutzung erreicht werden.

Georg Schnabel
quelle
6
Dies trifft nicht mehr zu: Aus Hadleys fortgeschrittenem R "haben Änderungen an R 3.1.0 diese Verwendung [von Umgebungen] wesentlich weniger wichtig gemacht, da das Ändern einer Liste keine tiefe Kopie mehr erstellt."
Petrelharp
8

Die llFunktion im gDataPaket kann auch die Speichernutzung jedes Objekts anzeigen.

gdata::ll(unit='MB')
user1436187
quelle
Nicht auf meinem System: R-Version 3.1.1 (10.07.2014), x86_64-pc-linux-gnu (64-Bit), gdata_2.13.3, gtools_3.4.1.
krlmlr
Sie haben Recht, ich teste es, sobald es zufällig bestellt wurde!
user1436187
1
Bitte ändern Sie die Funktion, um Gb, Mb
userJT
7

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

Normalerweise habe ich eine Funktion, die den Job erledigt und zurückgibt NULL- alle Daten werden in dieser oder anderen aufgerufenen Funktionen gelesen und bearbeitet.

Alexander Radev
quelle
7

Mit nur 4 GB RAM (unter Windows 10, also ungefähr 2 oder mehr realistisch 1 GB) musste ich bei der Zuweisung sehr vorsichtig sein.

Ich benutze fast ausschließlich data.table.

Mit der Funktion 'fread' können Sie Informationen beim Import nach Feldnamen unterteilen. Importieren Sie zunächst nur die Felder, die tatsächlich benötigt werden. Wenn Sie Base R Read verwenden, setzen Sie die falschen Spalten unmittelbar nach dem Import auf Null.

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

Ich rm () häufig Objekte aus der Umgebung, sobald sie nicht mehr benötigt werden, z. B. in der nächsten Zeile, nachdem ich sie verwendet habe, um etwas anderes zu unterteilen, und rufe gc () auf.

'fread' und 'fwrite' aus data.table können im Vergleich zu Lese- und Schreibvorgängen der Basis R sehr schnell sein.

Wie kpierce8 vorschlägt, schreibe ich fast immer alles aus der Umgebung heraus und fresse es wieder ein, selbst wenn Tausende / Hunderttausende winziger Dateien durchkommen müssen. Dies hält nicht nur die Umgebung "sauber" und hält die Speicherzuweisung niedrig, sondern R neigt möglicherweise aufgrund des schwerwiegenden Mangels an verfügbarem RAM dazu, häufig auf meinem Computer abzustürzen. sehr 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 er abstürzt.

Ab 2017 laufen meiner Meinung nach die schnellsten SSDs über den M2-Port mit einigen GB pro Sekunde. Ich habe eine wirklich einfache 50 GB Kingston V300 (550 MB / s) SSD, die ich als primäre Festplatte verwende (mit Windows und R). Ich behalte alle Masseninformationen auf einem billigen 500-GB-WD-Plattenteller. Ich verschiebe die Datensätze auf die SSD, wenn ich anfange, daran zu arbeiten. Dies, kombiniert mit "Freading" und "Fwrite", hat alles super geklappt. Ich habe versucht, 'ff' zu verwenden, bevorzuge aber das erstere. 4K-Lese- / Schreibgeschwindigkeiten können jedoch zu Problemen führen. Das Sichern einer Viertelmillion 1k-Dateien (im Wert von 250 MB) von der SSD auf dem Plattenteller kann Stunden dauern. Soweit mir bekannt ist, ist noch kein R-Paket verfügbar, mit dem der Chunkifizierungsprozess automatisch optimiert werden kann. Schauen Sie sich beispielsweise an, wie viel RAM ein Benutzer hat. Testen Sie die Lese- / Schreibgeschwindigkeiten des RAM / aller angeschlossenen Laufwerke und schlagen Sie dann ein optimales Chunkification-Protokoll vor. Dies könnte zu erheblichen Workflow-Verbesserungen / Ressourcenoptimierungen führen. zB 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 Plattenteller -> teilen Sie es auf ... MB auf dem Band. Es könnte Datensätze im Voraus abtasten, um einen realistischeren Messstab für die Arbeit zu erhalten.

Viele der Probleme, an denen ich in R gearbeitet habe, betreffen die Bildung von Kombinations- und Permutationspaaren, Tripeln usw., was das begrenzte RAM nur zu einer größeren Einschränkung macht, da sie sich häufig irgendwann zumindest exponentiell ausdehnen. Dies hat mich dazu gebracht, viel Aufmerksamkeit auf die Qualität im Gegensatz zur Quantität der Informationen zu richten, die zunächst in sie eingehen, anstatt zu versuchen, sie anschließend zu bereinigen, und auf die Abfolge der Vorgänge bei der Vorbereitung der Informationen (beginnend mit) die einfachste Operation und Erhöhung der Komplexität); zB Teilmenge, dann Zusammenführen / Verbinden, dann Kombinationen / Permutationen bilden usw.

In einigen Fällen scheint die Verwendung von Lesen und Schreiben auf Basis R einige Vorteile zu haben. 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 Speicherplatz, während Ubuntu nur wenige GB benötigt, der mit Ubuntu benötigte RAM ist etwas niedriger. Bei der Installation von Paketen von Drittanbietern in (L) Ubuntu sind mir jedoch zahlreiche Warnungen und Fehler aufgefallen. Ich würde nicht empfehlen, sich zu weit von (L) Ubuntu oder anderen Aktiendistributionen mit Linux zu entfernen, da Sie so viel Gesamtkompatibilität verlieren können, dass der Prozess fast sinnlos wird (ich denke, dass 'Unity' in Ubuntu ab 2017 abgebrochen werden soll ).

Hoffentlich hilft einiges davon anderen.

bg49ag
quelle
5

Dies fügt dem oben Gesagten 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 einige der 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]))),])
JamesF
quelle
5

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 )

Chris Beeley
quelle
3

Wenn Sie unter Linux arbeiten und mehrere Prozesse verwenden möchten und nur Lesevorgänge für ein oder mehrere große Objekte ausführen müssen, verwenden Sie makeForkClusteranstelle von a makePSOCKcluster. Dies spart Ihnen auch Zeit beim Senden des großen Objekts an die anderen Prozesse.

gdkrmr
quelle
2

Ich schätze einige der obigen Antworten sehr, nachdem ich @hadley und @Dirk gefolgt bin, die vorschlagen, R zu schließen sourceund die Befehlszeile auszugeben und zu verwenden. Ich habe eine Lösung gefunden, die für mich sehr gut funktioniert hat. Ich musste mich mit Hunderten von Massenspektren befassen, von denen jedes etwa 20 MB Speicher belegt, also 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 grundsätzlich, was mein Hauptskript tut runConsensus.r, und schreibe die Datenantwort für die Ausgabe. Damit scheint jedes Mal, wenn der Wrapper das Skript aufruft, das R erneut geöffnet und der Speicher freigegeben zu werden.

Ich hoffe es hilft.

user1265067
quelle
2

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, 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 (Sie müssen Ihren anderen Code nicht ändern), aber spärliche Daten viel effizienter speichern können, unabhängig davon, ob sie in den Speicher geladen oder auf der Festplatte gespeichert werden.

Außerdem haben die Rohdateien, die ich erhalte, ein "langes" Format, in dem jeder Datenpunkt Variablen enthält x, y, z, i. Viel effizienter, um die Daten in ein x * y * zDimensionsarray mit nur Variablen umzuwandeln i.

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

D Greenwood
quelle
2

Tipp für den Umgang mit Objekten , für die eine umfangreiche Zwischenberechnung erforderlich ist : Wenn Sie Objekte verwenden, für deren Erstellung viele umfangreiche Berechnungen und Zwischenschritte erforderlich sind, ist es häufig hilfreich, einen Codeabschnitt mit der Funktion zum Erstellen des Objekts und anschließend einen separaten Block zu schreiben Code, mit dem ich das Objekt entweder generieren und als rmdDatei speichern oder extern aus einer rmdDatei laden kann, die ich bereits zuvor gespeichert habe. Dies ist besonders einfach, wenn Sie R Markdowndie folgende Code-Chunk-Struktur verwenden.

```{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 ändern, LOADje nachdem, ob ich das Objekt generieren und speichern oder direkt aus einer vorhandenen gespeicherten Datei laden möchte. (Natürlich muss ich es generieren und beim ersten Mal speichern, aber danach habe ich die Möglichkeit, es zu laden.) Das Einstellen LOAD = TRUEumgeht die Verwendung meiner komplizierten Funktion und vermeidet alle darin enthaltenen umfangreichen Berechnungen. Diese Methode benötigt immer noch genügend Speicher, um das interessierende Objekt zu speichern, erspart Ihnen jedoch die Berechnung jedes Mal, wenn Sie Ihren Code ausführen. Bei Objekten, die eine umfangreiche Berechnung von Zwischenschritten erfordern (z. B. bei Berechnungen mit Schleifen über große Arrays), kann dies einen erheblichen Zeit- und Rechenaufwand bedeuten.

Ben - Monica wieder einsetzen
quelle
1

Laufen

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

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

Marcelo Ventura
quelle
Was macht die forSchleife hier? Es gibt keinen iim gcAufruf.
Umaomamaomao
@qqq es ist nur da, um das gc(reset = T)neunmalige Kopieren und Einfügen zu vermeiden
Marcelo Ventura
14
Aber warum sollten Sie es 9 Mal ausführen? (neugierig, nicht kritisch)
Umaomamaomao
1

Sie können auch einige Vorteile erzielen, indem Sie knitr verwenden und Ihr Skript in Rmd-Chuncks einfügen.

Normalerweise teile ich den Code in verschiedene Abschnitte auf und wähle aus, welcher einen Prüfpunkt im Cache oder in einer RDS-Datei speichern soll, und

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 Durchlauf nur "Teil 1" verarbeiten, in einer anderen Ausführung nur "Teil 2" usw. auswählen.

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önnte dies Ihnen auch Kopfschmerzen in Bezug auf die Reproduzierbarkeit ersparen :)

Matias Thayer
quelle
1

Basierend auf der Antwort von @ Dirk und @ Tony habe ich ein kleines Update gemacht. Das Ergebnis wurde [1]vor den hübschen Größenwerten ausgegeben , also habe ich das herausgenommen, capture.outputwas 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()
ilyas
quelle
-1

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

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

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

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

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 ihn einfach entfernen, tempwenn er nicht mehr benötigt wird.

rm(temp)

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

Zum Testen verwende ich test, test2...

Morgenstern
quelle