从R写入Excel时处理java.lang.OutOfMemoryError
xlsx
软件包可用于从R读取和写入Excel电子表格。不幸的是,即使是中等大小的电子表格,也可能发生java.lang.OutOfMemoryError
。 尤其是,
## 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
(其他相关例外也是可能的,但更less见)
在阅读电子表格时,也遇到过类似的问题。
导入一个大的xlsx文件到R?
使用Excel电子表格作为CSV数据存储介质的主要优点是可以在同一个文件中存储多个工作表,因此在这里我们考虑一个数据框列表,每个工作表要写入一个数据框。 这个示例数据集包含40个dataframe,每个dataframe包含两列高达200k行的数据。 它被devise成足够大以至于有问题,但是可以通过改变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
创build工作簿,然后遍历每个调用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")
在具有8GB RAM的机器上以64位的速度运行此操作时,会在GC overhead limit exceeded
运行addDataFrame
时引发GC overhead limit exceeded
错误。
如何使用xlsx
将大型数据集写入Excel电子表格?
这是一个已知问题: http : //code.google.com/p/rexcel/issues/detail?id=33
虽然没有解决,问题页面链接到一个由Gabor Grothendieck 的解决scheme ,build议在加载rJava
包之前,通过设置java.parameters
选项来增加堆大小。 ( rJava
是xlsx
的依赖。)
options(java.parameters = "-Xmx1000m")
值1000
是允许Java堆的RAM的兆字节数; 它可以replace为你喜欢的任何值。 我的这个实验表明,更大的值是更好的,你可以愉快地使用你的全部RAM权利。 例如,我使用以下方法得到了最好的结果:
options(java.parameters = "-Xmx8000m")
在8GB RAM的机器上。
通过在循环的每次迭代中请求垃圾回收可以获得进一步的改进。 正如@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次迭代(但至less部分内容被写入文件)下跌。
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 ) }
build立在@ richie棉花的答案,我发现joingc()
jgc
function保持CPU使用率低。
jgc <- function() { gc() .jcall("java/lang/System", method = "gc") }
我以前的for
循环仍然与原来的jgc
函数挣扎,但有了额外的命令,我不再遇到GC overhead limit exceeded
错误消息。
解决上述错误的方法:请使用下面提到的r代码:
detach(package:xlsx)detach(package:XLConnect)library(openxlsx)
而且,尝试再次导入文件,你不会得到任何错误,因为它为我工作。
如果您一行一行地写,也可以在循环中使用gc()。 gc()代表垃圾收集。 gc()可以用在任何情况下的内存问题。