web-dev-qa-db-fra.com

Gestion de Java.lang.OutOfMemoryError lors de l'écriture dans Excel depuis R

Le paquetage xlsx peut être utilisé pour lire et écrire des feuilles de calcul Excel à partir de R. Malheureusement, même pour des feuilles de calcul de taille moyenne, Java.lang.OutOfMemoryError peut arriver. En particulier,

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

## Error in .jcall("RJavaTools", "Ljava/lang/Object;", "newInstance", .jfindClass(class),  : 
##   Java.lang.OutOfMemoryError: GC overhead limit exceeded 

(D'autres exceptions connexes sont également possibles mais plus rares.)

Une question similaire a été posée à propos de cette erreur lors de la lecture de feuilles de calcul.

Importer un gros fichier xlsx dans R?

Le principal avantage de l'utilisation de feuilles de calcul Excel comme support de stockage de données sur un fichier CSV est que vous pouvez stocker plusieurs feuilles dans le même fichier. Nous considérons donc ici qu'une liste de trames de données doit être écrite par trame de données. Cet exemple de jeu de données contient 40 trames de données, chacune avec deux colonnes de 200 000 lignes maximum. Il est conçu pour être assez gros pour poser problème, mais vous pouvez changer la taille en modifiant n_sheets et 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))

La méthode naturelle pour écrire ceci dans un fichier est de créer un classeur en utilisant createWorkbook , puis en boucle sur chaque appel de trame de données (createSheet et - addDataFrame . Enfin, le classeur peut être écrit dans un fichier en utilisant saveWorkbook . J'ai ajouté des messages à la boucle pour mieux voir où cela tombe.

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

En exécutant ceci en 64 bits sur une machine avec 8 Go de RAM, il lance le GC overhead limit exceeded _ erreur lors de l'exécution de addDataFrame pour la première fois.

Comment puis-je écrire de grands ensembles de données sur des feuilles de calcul Excel en utilisant xlsx?

78
Richie Cotton

Il s'agit d'un problème connu: http://code.google.com/p/rexcel/issues/detail?id=

Bien que non résolue, la page du problème lien vers une solution by Gabor Grothendieck suggère que la taille du segment de mémoire doit être augmentée en définissant l'option Java.parameters Avant le rJava package est chargé. (rJava est une dépendance de xlsx.)

options(Java.parameters = "-Xmx1000m")

La valeur 1000 Est le nombre de mégaoctets de RAM pour permettre au segment de mémoire Java; il peut être remplacé par la valeur de votre choix). Mes expériences suggèrent que plus les valeurs sont grandes, mieux c'est, et vous pouvez utiliser avec plaisir votre droit =RAM complet. Par exemple, j'ai obtenu les meilleurs résultats en utilisant:

options(Java.parameters = "-Xmx8000m")

sur la machine avec 8 Go de RAM.

Une amélioration supplémentaire peut être obtenue en demandant une récupération de place à chaque itération de la boucle. Comme indiqué par @gjabel, la récupération de place R peut être effectuée à l'aide de gc() . Nous pouvons définir une fonction de récupération de place Java qui appelle la méthode Java System.gc()] _ :

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

Ensuite, la boucle peut être mise à jour pour:

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

Avec ces deux corrections de code, le code était aussi long que i = 29 Avant de générer une erreur.

Une technique que j'ai essayé sans succès consistait à utiliser write.xlsx2 Pour écrire le contenu dans un fichier à chaque itération. C'était plus lent que l'autre code et il est tombé à la dixième itération (mais au moins une partie du contenu a été écrite dans un fichier).

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
  )
}
77
Richie Cotton

En me basant sur la réponse @ richie-cotton, j'ai trouvé que l'ajout de gc() à la fonction jgc limitait l'utilisation du processeur.

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

Ma précédente boucle for avait encore du mal à utiliser la fonction originale jgc, mais avec une commande supplémentaire, je ne me heurte plus à GC overhead limit exceeded Message d'erreur.

7
gjabel

Solution pour l'erreur ci-dessus: Veuillez utiliser le code-r mentionné ci-dessous:

detach (package: xlsx) detach (package: XLConnect) bibliothèque (openxlsx)

Et essayez à nouveau d’importer le fichier et vous ne recevrez aucune erreur, cela fonctionnera pour moi.

0
Santosh