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
(他の関連する例外も可能ですが、まれです。)
スプレッドシートを読むときに、このエラーに関して同様の質問がされました。
ExcelスプレッドシートをCSVよりもデータストレージメディアとして使用する主な利点は、同じファイルに複数のシートを保存できることです。したがって、ここでは、ワークシートごとに1データフレームを書き込むデータフレームのリストを検討します。このサンプルデータセットには40個のデータフレームが含まれ、各フレームには最大20万行の2つの列があります。問題が生じるほど大きくなるように設計されていますが、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")
これを8GB RAMのマシンで64ビットで実行すると、addDataFrame
を初めて実行するときにGC overhead limit exceeded
エラーがスローされます。
xlsx
を使用して大きなデータセットをExcelスプレッドシートに書き込むにはどうすればよいですか?
これは既知の問題です。 http://code.google.com/p/rexcel/issues/detail?id=
未解決の問題ページ ソリューションへのリンクGabor Grothendieck により、rJava
パッケージがロードされる前にJava.parameters
オプションを設定することでヒープサイズを増やすことを提案しています。 (rJava
はxlsx
の依存関係です。)
options(Java.parameters = "-Xmx1000m")
値1000
は、RAMヒープを許可するJavaのメガバイト数です。任意の値に置き換えることができます。これを使った私の実験は、値が大きいほど良いことを示しており、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
まで実行されました。
私が試みた失敗したテクニックの1つは、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
)
}
@ richie-cottonの回答に基づいて、gc()
をjgc
関数に追加すると、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)
そして、もう一度ファイルをインポートしてみてください。それは私にとってうまくいくので、エラーは発生しません。