dplyr
の基本おさらい
多くの関数はdata.frame
を第一引数にとりdata.frame
を返す
class(dplyr::select(mtcars, cyl))
もしくはパイプ演算子を使って
mtcars |>
dplyr::select(cyl) |>
class()
arrow_dplyr_query
Table
やDataset
をそれらのdplyrの関数の第一引数に渡すとarrow_dplyr_query
クラスオブジェクトが返ってくる
arrow::open_dataset("mtcars.parquet") |>
dplyr::select(cyl) |>
class()
arrow_dplyr_query
を第一引数に渡した場合も同じ挙動
data.frame
のようにパイプラインを繋げていける
compute
かcollect
に渡すとクエリが実行される(dbplyr
に類似)
- Arrowインメモリフォーマットのままdplyrで記述した処理を実行できる
遅延評価
データセットはcompute
かcollect
に繋げるまで読み込まれない
c("mtcars.parquet", "mtcars.parquet") |>
arrow::open_dataset(format = "parquet") |>
dplyr::collect()
遅延評価
クエリもcompute
かcollect
に繋げるまで評価されない
c("mtcars.parquet", "mtcars.parquet") |>
arrow::open_dataset(format = "parquet") |>
dplyr::filter(cyl == 6) |>
dplyr::select(dplyr::starts_with("d")) |>
dplyr::collect()
- dplyrクエリはarrowパッケージによって翻訳され
libarrow
がクエリを実行する
- 翻訳可能な関数はarrowに登録されているもののみなので、非対応の関数を含めるとエラーになる(データセットに対するクエリの場合)
- 対応している関数は徐々に増えており、NEWSで確認可能
プッシュダウン
クエリもcompute
かcollect
に繋げるまで評価されない
c("mtcars.parquet", "mtcars.parquet") |>
arrow::open_dataset(format = "parquet") |>
dplyr::filter(cyl == 6) |>
dplyr::select(dplyr::starts_with("d")) |>
dplyr::collect()
- Parquetデータセットに対してクエリを実行するとき、クエリを解析し必要な列と行のみをファイルから読み込む(プッシュダウン)
- CSVと比べた場合のParquetの大きな利点
- 読み込むデータの少ないほど読み込み時間は短縮される
- 読み込むデータの少ないほど省メモリで処理できる
実行結果
クエリもcompute
かcollect
に繋げるまで評価されない
c("mtcars.parquet", "mtcars.parquet") |>
arrow::open_dataset(format = "parquet") |>
dplyr::filter(cyl == 6) |>
dplyr::select(dplyr::starts_with("d")) |>
dplyr::collect()
disp drat
1 160.0 3.90
2 160.0 3.90
3 258.0 3.08
4 225.0 2.76
5 167.6 3.92
6 167.6 3.92
7 145.0 3.62
8 160.0 3.90
9 160.0 3.90
10 258.0 3.08
11 225.0 2.76
12 167.6 3.92
13 167.6 3.92
14 145.0 3.62
データセットの作成
列の値毎に分割した複数のParquetファイルをデータセットとして書き込める(パーティショニング)
fs::dir_create("test_data")
c("mtcars.parquet") |>
arrow::open_dataset(format = "parquet") |>
arrow::write_dataset("test_data", partitioning = "cyl")
なお上記のようにデータセットからデータセットに直接変換する場合等はバッチ毎に逐次処理されるので、メモリに乗り切らないデータを加工可能……かもしれない
Hive-style パーティション
fs::dir_create("test_data")
arrow::write_dataset(mtcars, "test_data", partitioning = "cyl")
上のコードを実行すると以下のような複数のディレクトリとParquetファイルが生成される
$ tree test_data
test_data
├── cyl=4
│ └── part-0.parquet
├── cyl=6
│ └── part-0.parquet
└── cyl=8
└── part-0.parquet
3 directories, 3 files
Parquetファイルにはcyl
列が含まれておらず代わりにディレクトリ名がkey=value
の形式になっている
パーティショニングを利用する場合の注意
- Parquetファイルのサイズとパーティション数
- そのまま読み込み可能なツールは限られる
>>> import pyarrow.dataset as ds
>>> ds.dataset("test_data", partitioning="hive")
<pyarrow._dataset.FileSystemDataset object at 0x7f1162853730>
dplyrクエリ中での型変更
as.integer
等の一般的な関数は登録済みのものが多い
任意のArrowタイプに変換するにはmutate
等の中でcast
を使用する
cast
は単体の関数として存在しないため、ヘルプを検索してもヒットしない
mtcars |>
arrow::arrow_table() |>
dplyr::transmute(cyl = cast(cyl, arrow::int8())) |>
dplyr::compute()
Table
32 rows x 1 columns
$cyl <int8>
See $metadata for additional Schema metadata