Обробка java.lang.OutOfMemoryError під час запису в Excel з R


84

Цей xlsxпакет можна використовувати для читання та запису електронних таблиць Excel із R. На жаль, навіть для помірно великих електронних таблиць це java.lang.OutOfMemoryErrorможе статися. Зокрема,

Помилка в .jcall ("RJavaTools", "Ljava / lang / Object;", "invokeMethod", cl,:
java.lang.OutOfMemoryError: Java heap space

Помилка .jcall ("RJavaTools", "Ljava / lang / Object;", "newInstance", .jfindClass (клас),:
java.lang.OutOfMemoryError: перевищено ліміт накладних витрат GC

(Інші пов’язані винятки також можливі, але рідше.)

Подібне запитання було задано щодо цієї помилки під час читання електронних таблиць.

Імпортувати великий файл xlsx у R?

Основною перевагою використання електронних таблиць Excel як носія даних над CSV є те, що ви можете зберігати кілька аркушів в одному файлі, тому тут ми розглядаємо список кадрів даних, які слід записати по одному кадру даних на робочий аркуш. Цей приклад набору даних містить 40 кадрів даних, кожен із двох стовпців до 200 тис. Рядків. Він розроблений для того, щоб бути досить великим, щоб бути проблематичним, але ви можете змінити розмір, змінивши n_sheetsта 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))

Природним методом запису цього у файл є створення книги за допомогою createWorkbook, а потім цикл по кожному виклику кадру даних createSheetта addDataFrame. Нарешті, книгу можна записати у файл за допомогою saveWorkbook. Я додав повідомлення до циклу, щоб було легше побачити, куди він потрапляє.

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

Запустивши це в 64-розрядному верстаті на машині з 8 ГБ оперативної пам'яті, він видає GC overhead limit exceededпомилку під addDataFrameчас першого запуску .

Як записати великі набори даних у електронні таблиці Excel за допомогою xlsx?

Відповіді:


79

Це відома проблема: http://code.google.com/p/rexcel/issues/detail?id=33

Поки невирішене питання сторінка посилається на рішення по Габору Гротендік припускаючи , що розмір купи повинен бути збільшений шляхом установки java.parametersпараметра до rJavaзавантаження пакета. ( rJavaце залежність від xlsx.)

options(java.parameters = "-Xmx1000m")

Значення 1000- це кількість мегабайт оперативної пам’яті, яка враховує купу Java; його можна замінити будь-яким значенням, яке вам подобається. Мої експерименти з цим дозволяють припустити, що більші значення кращі, і ви можете із задоволенням використовувати повне дозвіл RAM. Наприклад, я отримав найкращі результати, використовуючи:

options(java.parameters = "-Xmx8000m")

на машині з 8 Гб оперативної пам'яті.

Подальше поліпшення можна отримати, запитуючи збір сміття на кожній ітерації циклу. Як зазначає @gjabel, збирання сміття R можна виконувати за допомогою gc(). Ми можемо визначити функцію збору сміття Java, яка викликає System.gc()метод Java :

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

Тоді цикл можна оновити до:

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

З обома виправленнями коду код працював настільки, наскільки це було i = 29до появи помилки.

Одним із методів, який я невдало спробував, було використовувати write.xlsx2для написання вмісту у файл на кожній ітерації. Це було повільніше, ніж інший код, і він впав на 10-й ітерації (але принаймні частина вмісту була записана у файл).

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

38
Цю всю проблему тепер можна обійти, помінявши xlsxпакет на openxlsxпакет, який залежить, Rcppа не від Java.
Richie Cotton,

4
readxl- ще одна нова альтернатива C / C ++, яка виглядає багатообіцяючою.
Річі Коттон,

1
на жаль, я виявив, що обидва ці сміття для виявлення та зчитування дат - обидва потрапляють у невиправний безлад, що є форматом дат Excel: \
MichaelChirico

2
@RichieCotton, приємна альтернатива. Однак openxlsx не може читати файли .xls або .xlm! (Формат файлу Excel 2007).
Espanta

подзвонити options(java.parameters = "-Xmx8000m")перед тим навантаженням rJava, xlsxjars, xlsxвирішена 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 haltedв RHEL 6.3 x86_64, Java 1.7.0_79 (Oracle), rJava_0.9-7, xlsxjars_0.6.0, xlsx_0.5.7
Нік Донг

7

Спираючись на відповідь @ richie-cotton, я виявив, що додавання gc()до jgcфункції зменшує споживання процесора.

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

Мій попередній forцикл все ще боровся з оригінальною jgcфункцією, але за допомогою додаткової команди я більше не стикаюся з GC overhead limit exceededповідомленням про помилку.


-1

Ви також можете використовувати gc () всередині циклу, якщо пишете рядок за рядком. gc () означає вивіз сміття. gc () можна використовувати в будь-якому випадку, коли проблема з пам'яттю.


-1

Рішення для вищевказаної помилки: Будь ласка, використовуйте згаданий нижче r - код:

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

І спробуйте імпортувати файл ще раз, і ви не отримаєте жодної помилки, оскільки це працює для мене.


Два коментарі: xlConnect має однакову проблему. І що більш важливо, сказати комусь використовувати іншу бібліотеку не є рішенням проблеми з тією, на яку посилається. Мета тут - залишатися в межах пакета xlsx. Є й інші теми, присвячені XLConnect.
Michael Tuchman,

-1

У мене були проблеми з write.xlsx (), а не з читанням .... але потім зрозумів, що випадково використовував 32-бітну R. Заміна його на 64-бітну проблему вирішила.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.