Obsługa java.lang.OutOfMemoryError podczas pisania do Excela z R

84

xlsxPakiet może być wykorzystany do odczytu i zapisu arkuszy kalkulacyjnych Excel z R. Niestety, nawet dla średnio dużych arkuszy, java.lang.OutOfMemoryErrormoże wystąpić. W szczególności,

Błąd w .jcall („RJavaTools”, „Ljava / lang / Object;”, „invokeMethod”, cl,:
java.lang.OutOfMemoryError: Java heap space

Błąd w .jcall ("RJavaTools", "Ljava / lang / Object;", "newInstance", .jfindClass (klasa),:
java.lang.OutOfMemoryError: Przekroczono limit narzutu GC

(Inne powiązane wyjątki są również możliwe, ale rzadsze).

Podobne pytanie padło w związku z tym błędem podczas czytania arkuszy kalkulacyjnych.

Importujesz duży plik xlsx do R?

Główną zaletą używania arkuszy kalkulacyjnych Excel jako nośnika danych w porównaniu z CSV jest to, że można przechowywać wiele arkuszy w tym samym pliku, dlatego tutaj rozważamy listę ramek danych do zapisania po jednej ramce danych na arkusz. Ten przykładowy zestaw danych zawiera 40 ramek danych, każda z dwiema kolumnami po maksymalnie 200 tys. Wierszy. Został zaprojektowany tak, aby był wystarczająco duży, aby był problematyczny, ale możesz zmienić rozmiar, zmieniając n_sheetsi 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))

Naturalną metodą zapisywania tego do pliku jest utworzenie skoroszytu za pomocą createWorkbook, a następnie pętla nad każdym wywołaniem ramki danych createSheeti addDataFrame. Wreszcie skoroszyt można zapisać do pliku za pomocą saveWorkbook. Dodałem wiadomości do pętli, aby łatwiej było zobaczyć, gdzie się przewraca.

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

Uruchomienie tego w wersji 64-bitowej na komputerze z 8 GB pamięci RAM powoduje zgłoszenie GC overhead limit exceededbłędu podczas addDataFramepierwszego uruchomienia.

Jak pisać duże zbiory danych do arkuszy kalkulacyjnych programu Excel za pomocą xlsx?

Richie Cotton
źródło

Odpowiedzi:

79

To jest znany problem: http://code.google.com/p/rexcel/issues/detail?id=33

Chociaż nierozwiązane, strona problemu prowadzi do rozwiązania autorstwa Gabora Grothendiecka, sugerującego, że rozmiar sterty powinien zostać zwiększony poprzez ustawienie java.parametersopcji przed rJavazaładowaniem pakietu. ( rJavajest zależnością od xlsx.)

options(java.parameters = "-Xmx1000m")

Wartość 1000to liczba megabajtów pamięci RAM, na którą można wstawić stertę Java; można go zastąpić dowolną wartością. Moje eksperymenty z tym sugerują, że większe wartości są lepsze i możesz z radością korzystać z pełnych uprawnień do pamięci RAM. Na przykład najlepsze wyniki uzyskałem używając:

options(java.parameters = "-Xmx8000m")

na komputerze z 8 GB pamięci RAM.

Dalszą poprawę można uzyskać, żądając czyszczenia pamięci w każdej iteracji pętli. Jak zauważył @gjabel, wyrzucanie elementów bezużytecznych w języku R można wykonać za pomocą gc(). Możemy zdefiniować funkcję zbierania elementów bezużytecznych Java, która wywołuje System.gc()metodę Java :

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

Następnie pętlę można zaktualizować do:

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

W przypadku obu tych poprawek kod działał aż do momentu i = 29zgłoszenia błędu.

Jedną z technik, której próbowałem bez powodzenia, było write.xlsx2zapisywanie zawartości do pliku w każdej iteracji. Był wolniejszy niż inny kod i spadł w dziesiątej iteracji (ale przynajmniej część zawartości została zapisana do pliku).

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
źródło
38
Cały ten problem można teraz ominąć, zamieniając xlsxpakiet na openxlsxpakiet, który jest zależny Rcppraczej od języka Java.
Richie Cotton
4
readxlto kolejna nowa alternatywa C / C ++, która wygląda obiecująco.
Richie Cotton
1
niestety stwierdziłem, że oba są dość śmieciowe do wykrywania i odczytywania dat - oba kończą się w niepoprawnym bałaganie, jakim jest format daty Excel: \
MichaelChirico
2
@RichieCotton, fajna alternatywa. Jednak openxlsx nie może czytać plików .xls ani .xlm! (Format pliku programu Excel 2007).
Espanta,
wywołać options(java.parameters = "-Xmx8000m")przed obciążeniem rJava, xlsxjars, xlsxrozwiązane 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 haltedw RHEL 6,3 86_64, Java 1.7.0_79 (Oracle) rJava_0.9-7, xlsxjars_0.6.0, xlsx_0.5.7
Nick Dong
7

Opierając się na odpowiedzi @ richie-cotton, stwierdziłem, że dodanie gc()do jgcfunkcji utrzymało niskie zużycie procesora.

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

Moja poprzednia forpętla nadal walczyła z oryginalną jgcfunkcją, ale dzięki dodatkowemu poleceniu nie pojawia się już GC overhead limit exceededkomunikat o błędzie.

Guyabel
źródło
-1

Możesz także użyć gc () wewnątrz pętli, jeśli piszesz wiersz po wierszu. gc () oznacza odśmiecanie. gc () może być użyte w każdym przypadku problemu z pamięcią.

Arunkumar CR
źródło
-1

Rozwiązanie powyższego błędu: użyj poniższego r - kodu:

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

Spróbuj ponownie zaimportować plik, a nie pojawi się żaden błąd, ponieważ działa dla mnie.

Santosh
źródło
Dwie uwagi: xlConnect ma ten sam problem. A co ważniejsze, mówienie komuś, aby używał innej biblioteki, nie jest rozwiązaniem problemu z tą, do której się odwołujemy. Celem jest pozostanie w pakiecie xlsx. Istnieją inne wątki poświęcone XLConnect.
Michael Tuchman
-1

Miałem problemy z write.xlsx (), zamiast czytać ... ale potem zdałem sobie sprawę, że przypadkowo pracowałem z 32-bitowym R. Zamiana go na 64-bitową rozwiązała problem.

Jbell
źródło