はじめに
Rで行数、列数が想定外に多いcsvを取り扱う必要があり、その際に試行錯誤したときの忘備録。
やったこと
CSVを単にtidyverse系のパッケージで取り込むにもメモリを喰いすぎるし、動作も重くなるので、DBに突っ込んで使いやすく加工すればいいのではと考えた。
↓
とはいえ、DBサーバたてたりとかはさずがに面倒なので、SQLiteを使えるRSQLiteのパッケージを使うことに。
↓
しかし、通常通りにインストールすると列数が2000を超えたデータはSQLiteの制限に引っかかって取り込めず。
↓
SQLiteはRSQLiteにバンドルされてるってんで、インストール後に変更は難しそうなので、RSQLiteのソースをDLして、SQLiteのソースに手を加えてから、ビルドしてインストールすることにした。
環境
Linux(Ubuntu)
R 4.1.2
RSQLite 2.2.18
通常通りにRSQLiteをインストールして読み込ませた場合
- 32766列の適当なcsvファイルを作成する
for i in `seq 1 32766`; do if [ ${i} -ne 32766 ]; then echo -n A${i}, >> temp.csv; else echo A${i} >> temp.csv; fi; done
for i in `seq 1 32766`; do if [ ${i} -ne 32766 ]; then echo -n ${i}, >> temp.csv; else echo ${i} >> temp.csv; fi; done
- Rを起動し、上で作成したCSVを読み込ませる
library(DBI)
library(tidyverse)
con <- dbConnect(RSQLite::SQLite(), "temp.sqlite3")
callback <- function(df, pos) {
dbWriteTable(con, "temp_table", df, append = TRUE)
}
read_csv_chunked("temp.csv", DataFrameCallback$new(callback))
- 下のエラーメッセージが出て取り込めず
エラー: too many columns on temp_table
大規模データの取り込みに参考にさせていただいたサイトはこちら。
テーブルの最大カラム数を変更したRSQLiteをインストール
RSQLiteのソースデータをダウンロード
Rを起動して以下を実行
download.packages(pkgs = 'RSQLite', destdir = ".",type = "source")
(試行時にDLできたのは、RSQLite_2.2.18.tar.gz
)
tar.gzを解凍
Rは一度終了し、DLしたファイルを解凍
tar -zxvf RSQLite_2.2.18.tar.gz
カレントディレクトリにRSQLite
のディレクトリができる
sqlite3.cを書き換える
./RSQLite/src/vendor/sqlite3/sqlite3.c
を開いて、下の部分を探す
#ifndef SQLITE_MAX_COLUMN
# define SQLITE_MAX_COLUMN 2000
#endif
↑の部分を↓のように書き換える
#ifndef SQLITE_MAX_COLUMN
# define SQLITE_MAX_COLUMN 32766
#endif
32767より大きい値を設定するとエラーで弾かれそうです
DESCRIPTIONも書き換える
パッケージのバージョンが混在しないようにするため、DESCRIPTIONも書き換えときます。
※知識不足なので正しいやり方かどうかはわかりません。正しくはこうすべきってのがあれば教えてください。
./RSQLite/DESCRIPTION
を開いて、下の部分を探す
Version: 2.2.18
↑の部分を↓のように書き換える
Version: 2.2.18.1
末尾に「.1」を追加しただけ
バージョンには数字と記号(「.」、「-」)しか使えなさげ?でした。(詳細わかりません)
devtoolsでビルド
Rを起動し、以下を実行
devtools::build("RSQLite")
実行すると
Building the package will delete...
'~~~~~~~~/RSQLite/inst/doc'
Are you sure?
1: Yes
2: No
Selection:
と聞かれたので、「1」を入力し、Enter
ここで私の環境では実行時にPandocがないと言われましたが、Pandocをインストールすれば通りました。
成功するとカレントディレクトリにRSQLite_2.2.18.1.tar.gz
が作成される
RSQLiteをインストール
Rを起動して以下を実行
install.packages("RSQLite_2.2.18.1.tar.gz", repos = NULL)
これでRSQLiteで32766列以下のデータは扱えるはず
編集したRSQLiteをインストールした状態で、再度csvファイルを読み込ませる
- Rを起動し、CSVを読み込ませる
library(DBI)
library(tidyverse)
con <- dbConnect(RSQLite::SQLite(), "temp.sqlite3")
callback <- function(df, pos) {
dbWriteTable(con, "temp_table", df, append = TRUE)
}
read_csv_chunked("temp.csv", DataFrameCallback$new(callback))
読み込める!!
- 一応確認
dbListFields(con, 'temp_table')
[1] "A1" "A2" "A3" "A4" "A5" "A6" "A7" "A8"
[9] "A9" "A10" "A11" "A12" "A13" "A14" "A15" "A16"
[17] "A17" "A18" "A19" "A20" "A21" "A22" "A23" "A24"
・
・
・
[32745] "A32745" "A32746" "A32747" "A32748" "A32749" "A32750" "A32751" "A32752"
[32753] "A32753" "A32754" "A32755" "A32756" "A32757" "A32758" "A32759" "A32760"
[32761] "A32761" "A32762" "A32763" "A32764" "A32765" "A32766"
Windowsの場合どうやるのかわからん
似たような方法でできるとは思うが、申し訳ない…。
さいごに
本来正しく正規化されたデータであれば、このような列数は必要ないかと思う。
が、現実には「え…?」と思うようなCSVファイル等が渡されるわけで…。
こんな感じでデータベースに取り込んで、一度に全ての列を使うわけでもなければ、SELECT分なりでいい感じに列を抜き出してデータ眺めたり、加工したり、解析したりしてみたらいいと思います。