Обработка ошибки java.lang.OutOfMemoryError при записи в Excel из R

84

xlsxПакет может быть использован для чтения и записи таблицы Excel от R. К сожалению, даже для умеренно больших таблиц, java.lang.OutOfMemoryErrorможет произойти. В частности,

Ошибка в .jcall ("RJavaTools", "Ljava / lang / Object;", "invokeMethod", cl,:
java.lang.OutOfMemoryError: пространство кучи Java

Ошибка в .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пакет, который зависит от RcppJava.
Ричи Коттон
4
readxlеще одна новая альтернатива C / C ++, которая выглядит многообещающей.
Ричи Коттон
1
к сожалению, я обнаружил, что оба они совершенно бесполезны для определения и чтения дат - оба заканчиваются неисправимым беспорядком, который представляет собой формат даты Excel: \
MichaelChirico
2
@RichieCotton, хорошая альтернатива. Однако openxlsx не может читать файлы .xls или .xlm! (Формат файла 2007 excel).
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 хлопка-ответ, я обнаружил , добавив gc()к jgcфункции сохранили низкий уровень загрузки процессора.

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

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

Guyabel
источник
-1

Вы также можете использовать gc () внутри цикла, если вы пишете строка за строкой. gc () означает сборку мусора. gc () можно использовать в любом случае проблемы с памятью.

Арункумар ЧР
источник
-1

Решение вышеуказанной ошибки: используйте указанный ниже r-код:

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

И попробуйте импортировать файл еще раз, и вы не получите никаких ошибок, так как у меня это работает.

Сантош
источник
Два комментария: у xlConnect такая же проблема. И что еще более важно, указание кому-то использовать другую библиотеку не является решением проблемы с той, на которую ссылаются. Цель здесь - остаться в пределах пакета xlsx. Есть и другие темы, посвященные XLConnect.
Майкл Тачман
-1

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

Jbell
источник