Behandlung von java.lang.OutOfMemoryError beim Schreiben von R nach Excel

82

Das xlsxPaket kann zum Lesen und Schreiben von Excel-Tabellen aus R verwendet werden. Leider kann es auch bei mäßig großen Tabellen java.lang.OutOfMemoryErrorvorkommen. Speziell,

Fehler in .jcall ("RJavaTools", "Ljava / lang / Object;", "invokeMethod", cl ,:
java.lang.OutOfMemoryError: Java- Heapspeicher

Fehler in .jcall ("RJavaTools", "Ljava / lang / Object;", "newInstance", .jfindClass (Klasse) ,:
java.lang.OutOfMemoryError: GC-Overhead-Limit überschritten

(Andere verwandte Ausnahmen sind ebenfalls möglich, aber seltener.)

Eine ähnliche Frage wurde zu diesem Fehler beim Lesen von Tabellen gestellt.

Eine große XLSX-Datei in R importieren?

Der Hauptvorteil der Verwendung von Excel-Tabellen als Datenspeichermedium gegenüber CSV besteht darin, dass Sie mehrere Blätter in derselben Datei speichern können. Daher betrachten wir hier eine Liste von Datenrahmen, die als ein Datenrahmen pro Arbeitsblatt geschrieben werden sollen. Dieses Beispieldatensatz enthält 40 Datenrahmen mit jeweils zwei Spalten mit bis zu 200.000 Zeilen. Es ist groß genug, um problematisch zu sein, aber Sie können die Größe ändern, indem Sie n_sheetsund ändern n_rows.

library(xlsx)
set.seed(19790801)
n_sheets <- 40
the_data <- replicate(
  n_sheets,
  {
    n_rows <- sample(2e5, 1)
    data.frame(
      x = runif(n_rows),
      y = sample(letters, n_rows, replace = TRUE)
    )
  },
  simplify = FALSE
)
names(the_data) <- paste("Sheet", seq_len(n_sheets))

Die natürliche Methode zum Schreiben in eine Datei besteht darin, eine Arbeitsmappe mit zu erstellen createWorkbookund dann jeden Datenrahmen aufzurufen, der createSheetund aufruft addDataFrame. Schließlich kann die Arbeitsmappe mit in eine Datei geschrieben werden saveWorkbook. Ich habe der Schleife Nachrichten hinzugefügt, damit Sie leichter erkennen können, wo sie umfällt.

wb <- createWorkbook()  
for(i in seq_along(the_data))
{
  message("Creating sheet", i)
  sheet <- createSheet(wb, sheetName = names(the_data)[i])
  message("Adding data frame", i)
  addDataFrame(the_data[[i]], sheet)
}
saveWorkbook(wb, "test.xlsx")  

Wenn Sie dies in 64-Bit auf einem Computer mit 8 GB RAM ausführen, wird der GC overhead limit exceededFehler beim ersten Ausführen ausgegeben addDataFrame.

Wie schreibe ich große Datenmengen in Excel-Tabellen xlsx?

Richie Cotton
quelle

Antworten:

78

Dies ist ein bekanntes Problem: http://code.google.com/p/rexcel/issues/detail?id=33

Die ungelöste Problemseite enthält Links zu einer Lösung von Gabor Grothendieck , in der vorgeschlagen wird, die Größe des Heapspeichers zu erhöhen, indem die java.parametersOption vor dem rJavaLaden des Pakets festgelegt wird. ( rJavaist eine Abhängigkeit von xlsx.)

options(java.parameters = "-Xmx1000m")

Der Wert 1000gibt die Anzahl der Megabyte RAM an, die für den Java-Heap zulässig sind. Es kann durch einen beliebigen Wert ersetzt werden. Meine Experimente damit legen nahe, dass größere Werte besser sind und Sie Ihre volle RAM-Berechtigung gerne nutzen können. Zum Beispiel habe ich die besten Ergebnisse erzielt mit:

options(java.parameters = "-Xmx8000m")

auf dem Computer mit 8 GB RAM.

Eine weitere Verbesserung kann erzielt werden, indem in jeder Iteration der Schleife eine Speicherbereinigung angefordert wird. Wie von @gjabel angegeben, kann die R-Speicherbereinigung mithilfe von durchgeführt werden gc(). Wir können eine Java-Garbage-Collection-Funktion definieren, die die Java- System.gc()Methode aufruft :

jgc <- function()
{
  .jcall("java/lang/System", method = "gc")
}    

Dann kann die Schleife aktualisiert werden auf:

for(i in seq_along(the_data))
{
  gc()
  jgc()
  message("Creating sheet", i)
  sheet <- createSheet(wb, sheetName = names(the_data)[i])
  message("Adding data frame", i)
  addDataFrame(the_data[[i]], sheet)
}

Mit diesen beiden Codekorrekturen lief der Code so weit wie i = 29vor dem Auslösen eines Fehlers.

Eine Technik, die ich erfolglos versuchte, bestand darin write.xlsx2, den Inhalt bei jeder Iteration in eine Datei zu schreiben. Dies war langsamer als der andere Code und fiel bei der 10. Iteration um (aber zumindest ein Teil des Inhalts wurde in eine Datei geschrieben).

for(i in seq_along(the_data))
{
  message("Writing sheet", i)
  write.xlsx2(
    the_data[[i]], 
    "test.xlsx", 
    sheetName = names(the_data)[i], 
    append    = i > 1
  )
}
Richie Cotton
quelle
38
Dieses ganze Problem kann jetzt umgangen werden, indem das xlsxPaket gegen das openxlsxPaket ausgetauscht wird , das Rcppeher von Java als von Java abhängt .
Richie Cotton
4
readxlist eine weitere neue C / C ++ - Alternative, die vielversprechend aussieht.
Richie Cotton
1
Leider habe ich festgestellt, dass beide ziemlich schlecht sind, um Daten zu erkennen und zu lesen - beide landen in dem unverbesserlichen Durcheinander, das das Excel-Datumsformat ist: \
MichaelChirico
2
@RichieCotton, schöne Alternative. Openxlsx kann jedoch keine XLS- oder XLM-Dateien lesen! (2007 Excel-Dateiformat).
Espanta
ruft options(java.parameters = "-Xmx8000m")vor Belastung rJava, xlsxjars, xlsxgelöst Error in .jcall("RJavaTools", "Ljava/lang/Object;", "invokeMethod", cl, : org.apache.poi.POIXMLException: java.lang.reflect.InvocationTargetException Calls: getNetwork ... <Anonymous> -> .jrcall -> .jcall -> .jcheck -> .Call Execution haltedin RHEL 6.3 x86_64, Java 1.7.0_79 (Oracle), rJava_0.9-7, xlsxjars_0.6.0, xlsx_0.5.7
Nick Dong
7

Aufbauend auf der Antwort von @ richie-Cotton fand ich, dass das Hinzufügen gc()der jgcFunktion die CPU-Auslastung niedrig hielt.

jgc <- function()
{
  gc()
  .jcall("java/lang/System", method = "gc")
}    

Meine vorherige forSchleife hatte immer noch Probleme mit der ursprünglichen jgcFunktion, aber mit einem zusätzlichen Befehl wird keine GC overhead limit exceededFehlermeldung mehr angezeigt.

guyabel
quelle
-1

Sie können gc () auch innerhalb der Schleife verwenden, wenn Sie Zeile für Zeile schreiben. gc () steht für Garbage Collection. gc () kann in jedem Fall von Speicherproblemen verwendet werden.

Arunkumar CR
quelle
-1

Lösung für den obigen Fehler: Bitte verwenden Sie den unten angegebenen R-Code:

detach(package:xlsx)
detach(package:XLConnect)
library(openxlsx)

Versuchen Sie erneut, die Datei zu importieren, und Sie erhalten keine Fehlermeldung, da dies für mich funktioniert.

Santosh
quelle
Zwei Kommentare: xlConnect hat das gleiche Problem. Und was noch wichtiger ist: Jemandem zu sagen, dass er eine andere Bibliothek verwenden soll, ist keine Lösung für das Problem, auf das verwiesen wird. Das Ziel hier ist es, innerhalb des xlsx-Pakets zu bleiben. Es gibt andere Threads, die XLConnect gewidmet sind.
Michael Tuchman
-1

Ich hatte Probleme mit write.xlsx () anstatt zu lesen ... aber dann wurde mir klar, dass ich versehentlich 32-Bit-R ausgeführt habe. Durch Auswechseln auf 64-Bit wurde das Problem behoben.

Jbell
quelle