R_tidyverse

csvファイルをparquet形式に変換する

SASよりもRの使用が増えつつあるので、Rの勉強も本格化しています。
データセットの保存形式もSAS形式に代わる形式を検討する必要があるので、今回はデータセットをparquet形式で保存する方法を検討しました。

parquetとは

parquetについてはたくさん文献があるので代表的な解説記事をご紹介します。

parquetはマルチプラットフォーム、オープンソースのデータセット形式であり、列方向への圧縮がかかるため容量も小さくなるとのこと。
クエリ時に必要な列だけスキャンされるらしく、クエリも高速化できるらしいです。

個人的にはRやpythonで共通で使用できる点と、容量が小さくなる点が魅力的に感じました。

従来のSAS形式にするとCSVよりも数倍位容量を食うケースが多くて
困っていたのでこれはうれしいです。最近はAWSなどのクラウドストレージサービスを使う事例も多くてデータの容量は解析環境の維持コストにダイレクトに効いてきます。

SASはviya環境ではsashdatとかを使えば効率的なのかもしれませんが、関係者がみんなviya使えるわけではないし、環境の自由度も高いほうがいいので既存のCSVデータをparquetに変換することにしました。

変換方法

基本的に以下のパッケージをロードした環境で実施します。使用PCはwindows 10、 メモリ16GBです。

使用ソフトウェア

  • R.4.2.2
  • Rstudio 2022.12.0 build 353

使用パッケージ

  • tidyverse
  • arrow
  • duckdb

arrowパッケージを使う

Rではtibbleはメモリ上に展開されます。CSVデータをいったんtibbleとして読み取ってそれをparquet形式として書き出すと、メモリに乗りきらない大容量データだと正しく変換できません。

open_csv_dataset関数を使うとすべてのデータをメモリ上に展開しなくてもparquetに変換できます。ただしメモリはそれなりに消費するので、メモリが少ないマシンだと厳しいかもしれません。

csvfile <- "CSVファイルパス" 

csv_stream <-arrow::open_csv_dataset(csvfile) 

arrow::write_parquet(csv_stream, sink=出力先のparquetファイル名)

約14.4GB(17カラム、およそ8800万レコード)のCSVファイルをparquetに変換した結果は以下の通りです。

  • 実行時間:約4.08分(5回の平均)
  • 使用メモリ:10GB以上
  • 変換されたparquetファイル:2.1GB(CSVファイルのおおよそ15%)

duckdbを使う

duckdbはデータ分析用のSQLですが、COPYをつかうことでparquetを書き出すことができます。メモリにデータを展開するわけではなく、ディスクに逐次的に出力しているようです。使用するメモリはarrowを使う場合よりも圧倒的に少なかったです。メモリが少ないマシンならduckdbのほうが大容量のCSVを安定的にparquetに書き出せるかもしれません。ただしarrowをつかうよりも時間がかかります。またarrowを使うよりも圧縮率が落ちる印象です。

library(duckdb) 
con <- dbConnect(duckdb::duckdb(), dbdir=":memory:") 

csvconv <- function (nm) { query <- paste ("copy (select * from read_csv_auto('", nm, ".csv', header=True)) to '", nm, ".parquet' (format 'parquet')", ,sep="") 
print(paste("processsing..", nm)) 
print(dbExecute(con, query)) } 

csvconv("csvファイル名")

約14.4GB(17カラム、およそ8800万レコード)のCSVファイルをparquetに変換した結果は以下の通りです。

  • 実行時間:約6.85分(5回の平均)
  • 使用メモリ:約275MB
  • 変換されたparquetファイル:3.2GB(CSVファイルのおおよそ22%)

結論

ほとんどの場合はarrowパッケージの関数を使ってCSVをparquetに変換するのがベストだと思います。どうしてもメモリが少ない場合はduckdbを使うのがよさそうです。