Het pakket xlsx
kan worden gebruikt om Excel-spreadsheets van R te lezen en te schrijven. Helaas kan zelfs voor redelijk grote spreadsheets java.lang.OutOfMemoryError
optreden. In het bijzonder,
Fout in .jcall(“RJavaTools”, “Ljava/lang/Object;”, “invokeMethod”, cl, :
java.lang.OutOfMemoryError: Java-heapruimteFout in .jcall(“RJavaTools”, “Ljava/lang/Object;”, “newInstance”, .jfindClass(class), :
java.lang.OutOfMemoryError: GC-overheadlimiet overschreden
(Andere gerelateerde uitzonderingen zijn ook mogelijk, maar zeldzamer.)
Er is een soortgelijke vraag gesteld over deze fout bij het lezen van spreadsheets.
Importeer je een groot xlsx-bestand in R?
Het belangrijkste voordeel van het gebruik van Excel-spreadsheets als een gegevensopslagmedium ten opzichte van CSV is dat u meerdere bladen in hetzelfde bestand kunt opslaan, dus hier beschouwen we een lijst met gegevensframes die één gegevensframe per werkblad moet schrijven. Deze voorbeeldgegevensset bevat 40 gegevensframes, elk met twee kolommen van maximaal 200k rijen. Het is ontworpen om groot genoeg te zijn om problematisch te zijn, maar u kunt de grootte wijzigen door n_sheets
en n_rows
te wijzigen.
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))
De natuurlijke methode om dit naar een bestand te schrijven, is door een werkmap te maken met createWorkbook
en loop vervolgens over elk dataframe met de aanroep van createSheet
en addDataFrame
. Ten slotte kan de werkmap naar een bestand worden geschreven met behulp van saveWorkbook
. Ik heb berichten aan de lus toegevoegd om het gemakkelijker te maken te zien waar deze omvalt.
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")
Als je dit in 64-bit uitvoert op een machine met 8 GB RAM, krijg je de fout GC overhead limit exceeded
terwijl addDataFrame
voor de eerste keer wordt uitgevoerd.
Hoe schrijf ik grote datasets naar Excel-spreadsheets met xlsx
?
Antwoord 1, autoriteit 100%
Dit is een bekend probleem:
http://code.google.com/p/rexcel/issues/detail ?id=33
Hoewel onopgelost, linkt de probleempagina naar een oplossing door Gabor Grothendiecksuggereert dat de heapgrootte moet worden vergroot door de java.parameters
optie voordat het rJava
pakket wordt geladen. (rJava
is een afhankelijkheid van xlsx
.)
options(java.parameters = "-Xmx1000m")
De waarde 1000
is het aantal megabytes RAM voor de Java-heap; het kan worden vervangen door elke gewenste waarde. Mijn experimenten hiermee suggereren dat grotere waarden beter zijn, en dat je met plezier je volledige RAM-rechten kunt gebruiken. Ik kreeg bijvoorbeeld de beste resultaten met:
options(java.parameters = "-Xmx8000m")
op de machine met 8 GB RAM.
Een verdere verbetering kan worden verkregen door een garbagecollection aan te vragen in elke iteratie van de lus. Zoals opgemerkt door @gjabel, kan R garbage collection worden uitgevoerd met gc()
. We kunnen een Java-garbagecollectiefunctie definiëren die de Java System.gc()
methode:
jgc <- function()
{
.jcall("java/lang/System", method = "gc")
}
Vervolgens kan de lus worden bijgewerkt naar:
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)
}
Met beide codecorrecties liep de code tot i = 29
voordat er een fout werd gegenereerd.
Een techniek die ik zonder succes heb geprobeerd, was om write.xlsx2
te gebruiken om de inhoud bij elke iteratie naar een bestand te schrijven. Dit was langzamer dan de andere code en viel om bij de 10e iteratie (maar ten minste een deel van de inhoud werd naar een bestand geschreven).
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
)
}
Antwoord 2, autoriteit 9%
Voortbouwend op het antwoord van @richie-cotton, merkte ik dat het toevoegen van gc()
aan de functie jgc
het CPU-gebruik laag hield.
jgc <- function()
{
gc()
.jcall("java/lang/System", method = "gc")
}
Mijn vorige for
-lus worstelde nog steeds met de originele jgc
-functie, maar met een extra commando loop ik niet langer tegen GC overhead limit exceeded
foutmelding.
Antwoord 3
Oplossing voor de bovenstaande fout:
Gebruik de onderstaande r – code:
detach(package:xlsx)
detach(package:XLConnect)
library(openxlsx)
En probeer het bestand opnieuw te importeren en je zult geen foutmelding krijgen omdat het voor mij werkt.
Antwoord 4
Herstart R en voeg, voordat u de R-pakketten laadt, in:
options(java.parameters = "-Xmx2048m")
of
options(java.parameters = "-Xmx8000m")
Antwoord 5
Je kunt ook gc() in de lus gebruiken als je rij voor rij schrijft. gc() staat voor garbage collection. gc() kan in elk geval van geheugenproblemen worden gebruikt.
Antwoord 6
Ik had problemen met write.xlsx()
in plaats van te lezen…. maar realiseerde me toen dat ik per ongeluk 32bit R had gebruikt. Door het om te wisselen naar 64bit is het probleem opgelost.