Manejo de java.lang.OutOfMemoryError al escribir en Excel desde R

82

El xlsxpaquete se puede utilizar para leer y escribir hojas de cálculo de Excel desde R. Desafortunadamente, incluso para hojas de cálculo moderadamente grandes, java.lang.OutOfMemoryErrorpuede ocurrir. En particular,

Error en .jcall ("RJavaTools", "Ljava / lang / Object;", "invokeMethod", cl,:
java.lang.OutOfMemoryError: Java heap space

Error en .jcall ("RJavaTools", "Ljava / lang / Object;", "newInstance", .jfindClass (clase),:
java.lang.OutOfMemoryError: se superó el límite de sobrecarga de GC

(Otras excepciones relacionadas también son posibles pero más raras).

Se hizo una pregunta similar con respecto a este error al leer hojas de cálculo.

¿Importando un archivo xlsx grande a R?

La principal ventaja de usar hojas de cálculo de Excel como medio de almacenamiento de datos sobre CSV es que puede almacenar varias hojas en el mismo archivo, por lo que aquí consideramos una lista de marcos de datos para escribir un marco de datos por hoja de trabajo. Este conjunto de datos de ejemplo contiene 40 marcos de datos, cada uno con dos columnas de hasta 200.000 filas. Está diseñado para ser lo suficientemente grande como para ser problemático, pero puede cambiar el tamaño modificando n_sheetsy 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))

El método natural de escribir esto en un archivo es crear un libro de trabajo usando createWorkbook, luego recorrer cada marco de datos llamando createSheety addDataFrame. Finalmente, el libro de trabajo se puede escribir en un archivo usando saveWorkbook. Agregué mensajes al bucle para que sea más fácil ver dónde se cae.

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

Al ejecutar esto en 64 bits en una máquina con 8 GB de RAM, arroja el GC overhead limit exceedederror mientras se ejecuta addDataFramepor primera vez.

¿Cómo escribo grandes conjuntos de datos en hojas de cálculo de Excel usando xlsx?

Richie algodón
fuente

Respuestas:

78

Este es un problema conocido: http://code.google.com/p/rexcel/issues/detail?id=33

Si bien no está resuelto, la página de problemas se vincula a una solución de Gabor Grothendieck que sugiere que el tamaño del montón debe aumentarse configurando la java.parametersopción antes de rJavaque se cargue el paquete. ( rJavaes una dependencia de xlsx.)

options(java.parameters = "-Xmx1000m")

El valor 1000es el número de megabytes de RAM para permitir el montón de Java; se puede reemplazar con cualquier valor que desee. Mis experimentos con esto sugieren que los valores más grandes son mejores, y puede usar felizmente su derecho de RAM completo. Por ejemplo, obtuve los mejores resultados usando:

options(java.parameters = "-Xmx8000m")

en la máquina con 8 GB de RAM.

Se puede obtener una mejora adicional solicitando una recolección de basura en cada iteración del ciclo. Como señaló @gjabel, la recolección de basura R se puede realizar usando gc(). Podemos definir una función de recolección de basura de Java que llame al System.gc()método de Java :

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

Luego, el bucle se puede actualizar a:

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

Con estas dos correcciones de código, el código se ejecutó hasta i = 29antes de generar un error.

Una técnica que intenté sin éxito fue usar write.xlsx2para escribir el contenido en el archivo en cada iteración. Esto fue más lento que el otro código y se cayó en la décima iteración (pero al menos parte del contenido se escribió en el archivo).

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 algodón
fuente
38
Todo este problema ahora se puede eludir cambiando el xlsxpaquete por el openxlsxpaquete, que depende Rcppde Java en lugar de hacerlo.
Richie Cotton
4
readxles otra nueva alternativa de C / C ++ que parece prometedora.
Richie Cotton
1
desafortunadamente, encontré que ambos son bastante basura para detectar y leer fechas; ambos terminan en el lío incorregible que es el formato de fecha de Excel: \
MichaelChirico
2
@RichieCotton, buena alternativa. Sin embargo, openxlsx no puede leer archivos .xls o .xlm. (2007 formato de archivo de Excel).
Espanta
llamar options(java.parameters = "-Xmx8000m")antes de la carga rJava, xlsxjars, xlsxresuelto 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 halteden 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

Sobre la base de la respuesta de @ richie-cotton, descubrí que agregar gc()a la jgcfunción mantenía bajo el uso de la CPU.

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

Mi forciclo anterior todavía tenía problemas con la jgcfunción original , pero con un comando adicional, ya no me encuentro con el GC overhead limit exceededmensaje de error.

guyabel
fuente
-1

También puede usar gc () dentro del ciclo si está escribiendo fila por fila. gc () significa recolección de basura. gc () se puede utilizar en cualquier caso de problema de memoria.

Arunkumar CR
fuente
-1

Solución para el error anterior: utilice el código r mencionado a continuación:

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

E intente importar el archivo nuevamente y no obtendrá ningún error, ya que funciona para mí.

Santosh
fuente
Dos comentarios: xlConnect tiene el mismo problema. Y lo que es más importante, decirle a alguien que use una biblioteca diferente no es una solución al problema con la que se hace referencia. El objetivo aquí es permanecer dentro del paquete xlsx. Hay otros hilos dedicados a XLConnect.
Michael Tuchman
-1

Estaba teniendo problemas con write.xlsx () en lugar de leer ... pero luego me di cuenta de que había estado ejecutando accidentalmente 32bit R. Cambiarlo a 64bit solucionó el problema.

Jbell
fuente