Rで解析:Arrow形式のデータをRから直接扱う「nanoarrow」パッケージの紹介
Rで扱ったデータをほかの言語やライブラリへ受け渡す時は、共通のデータ形式でそろえておくと処理がスムーズになります。しかし、Apache ArrowをRから直接操作するには、C/C++のコードを介する必要があり、準備に手間がかかります。
「nanoarrow」パッケージは、Arrow C Data Interfaceを介したデータの受け渡しを簡単におこなえるパッケージです。データフレームやベクトルをArrowの配列・スキーマ・配列ストリームへ変換するコマンド、Arrowのデータ構造をRのオブジェクトへ戻すコマンドが収録されています。また、Arrow IPC形式によるファイルの読み書きや、「reticulate」パッケージを介したPythonオブジェクトとの相互変換も可能です。
本パッケージの利用で、R以外の環境で作成されたArrow形式のデータを、追加のコンパイル作業なしに扱えるのではないかと考えます。
パッケージバージョンは0.8.0.1。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。
パッケージのインストール
下記コマンドを実行してください。
# パッケージのインストール
install.packages("nanoarrow")
# パッケージの読み込み
library("nanoarrow")
# Pythonとの相互変換例で必要なパッケージを読み込み
# install.packages("reticulate")
library("reticulate")コマンド例
詳細はコメント、パッケージのヘルプを確認してください。
本パッケージでは、配列(array)、スキーマ(schema)、配列ストリーム(array stream)の3つがデータの基本単位になります。Rのオブジェクトを配列などへ変換し、必要に応じて配列ストリームとしてまとめ、最後にRのベクトルやデータフレームへ戻す、という流れで利用します。以降の例では、北海道の観測記録を模した架空のデータを使用します。
付属のArrow IPCデータを読み込む:example_ipc_streamコマンド
動作確認用に用意されたArrow IPCストリームをraw型のベクトルとして返します。読み込みにはread_nanoarrowコマンドを使用します。
| オプション | 意味 | 初期値 |
|---|---|---|
| compression | 圧縮方式を”none”または”zstd”で指定 | c(“none”, “zstd”) |
# 付属のArrow IPCストリームを取得
IPCサンプル <- example_ipc_stream()
# 取得したオブジェクトの型を確認
class(IPCサンプル)
[1] "raw"
# Arrow IPCストリームを読み込みデータフレームに変換
as.data.frame(read_nanoarrow(IPCサンプル))
some_col
1 0
2 1
3 2
# zstd圧縮を利用できるビルドかどうかを確認
nanoarrow_with_zstd()
[1] TRUERのオブジェクトをArrow配列に変換:as_nanoarrow_arrayコマンド
ベクトルやデータフレームをnanoarrowの配列に変換します。データフレームを渡した場合は、列をまとめたstruct型の配列になります。必要に応じて、結果を出力するためにカッコ()で囲っています。実行時には必要ありません。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 配列に変換するオブジェクト | なし |
| … | S3メソッドに引き渡す引数 | なし |
| schema | 変換先の型を指定するスキーマ、既定はinfer_nanoarrow_schemaコマンドによる推定 | NULL |
# 観測地点ごとの積雪データを作成
積雪データ <- data.frame(
観測地点 = c("札幌", "旭川", "恵庭", "函館", "釧路"),
最深積雪 = c(97L, 118L, 84L, 62L, 41L),
平均気温 = c(-3.2, -7.5, -4.8, -2.1, -5.4)
)
# 整数のベクトルをArrow配列に変換
(積雪配列 <- as_nanoarrow_array(積雪データ$最深積雪))
<nanoarrow_array int32[5]>
$ length : int 5
$ null_count: int 0
$ offset : int 0
$ buffers :List of 2
..$ :<nanoarrow_buffer validity<bool>[null] ``
..$ :<nanoarrow_buffer data<int32>[5][20 b]> `97 118 84 62 41`
$ dictionary: NULL
$ children : list()
# Arrow配列をRのベクトルに戻す
as.vector(積雪配列)
[1] 97 118 84 62 41
# データフレーム全体をstruct型のArrow配列に変換
(観測配列 <- as_nanoarrow_array(積雪データ))
<nanoarrow_array struct[5]>
$ length : int 5
$ null_count: int 0
$ offset : int 0
$ buffers :List of 1
..$ :<nanoarrow_buffer validity<bool>[null] ``
$ children :List of 3
以下省略
# struct型のArrow配列をデータフレームに戻す
as.data.frame(観測配列)
観測地点 最深積雪 平均気温
1 札幌 97 -3.2
2 旭川 118 -7.5
3 恵庭 84 -4.8
4 函館 62 -2.1
5 釧路 41 -5.4
# schemaオプションで倍精度実数型を指定して変換
as.vector(as_nanoarrow_array(積雪データ$最深積雪, schema = na_double()))
[1] 97 118 84 62 41配列ストリームに変換してバッチごとに取り出す:as_nanoarrow_array_streamコマンド
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 配列ストリームに変換するオブジェクト | なし |
| … | S3メソッドに引き渡す引数 | なし |
| schema | 変換先の型を指定するスキーマ、既定はinfer_nanoarrow_schemaコマンドによる推定 | NULL |
# 観測地点ごとの年間降雪量データを作成
降雪データ <- data.frame(
観測地点 = c("札幌", "旭川", "恵庭"),
降雪量合計 = c(479L, 674L, 402L)
)
# データフレームをArrow配列ストリームに変換
(降雪ストリーム <- as_nanoarrow_array_stream(降雪データ))
<nanoarrow_array_stream struct<観測地点: string, 降雪量合計: int32>>
$ get_schema:function ()
$ get_next :function (schema = x$get_schema(), validate = TRUE)
$ release :function ()
# ストリームが保持するスキーマを確認
降雪ストリーム$get_schema()
<nanoarrow_schema struct>
$ format : chr "+s"
$ name : chr ""
$ metadata : list()
$ flags : int 0
$ children :List of 2
..$ 観測地点 :<nanoarrow_schema string>
.. ..$ format : chr "u"
.. ..$ name : chr "観測地点"
.. ..$ metadata : list()
.. ..$ flags : int 2
.. ..$ children : list()
.. ..$ dictionary: NULL
..$ 降雪量合計:<nanoarrow_schema int32>
.. ..$ format : chr "i"
.. ..$ name : chr "降雪量合計"
.. ..$ metadata : list()
.. ..$ flags : int 2
.. ..$ children : list()
.. ..$ dictionary: NULL
$ dictionary: NULL
# 1つ目のバッチを取り出しデータフレームに変換
as.data.frame(降雪ストリーム$get_next())
観測地点 降雪量合計
1 札幌 479
2 旭川 674
3 恵庭 402
# 次のバッチを取り出す(データが尽きるとNULLを返す)
降雪ストリーム$get_next()
NULL
# ストリームを解放
降雪ストリーム$release()
<おすすめのRに関する書籍です>
データの型情報をスキーマとして取得する:as_nanoarrow_schemaコマンド
スキーマは列名と型の情報を保持するデータ構造です。推定はinfer_nanoarrow_schemaコマンド、内容の解析にはnanoarrow_schema_parseコマンド、要素の変更はnanoarrow_schema_modifyコマンドを使用します。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | スキーマに変換するオブジェクト | なし |
| … | S3メソッドに引き渡す引数 | なし |
| recursive | 子要素を含めて解析する場合にTRUEを指定、nanoarrow_schema_parseコマンドで使用 | FALSE |
| new_values | 割り当てる新しいスキーマ要素、nanoarrow_schema_modifyコマンドで使用 | なし |
| validate | スキーマの検証を省略する場合にFALSEを指定、nanoarrow_schema_modifyコマンドで使用 | TRUE |
実行結果は省略。
# 気温の観測記録データを作成
気温データ <- data.frame(
観測地点 = c("恵庭", "千歳"),
最低気温 = c(-12.4, -14.1),
観測日 = as.Date(c("2026-01-15", "2026-01-16"))
)
# データフレームからスキーマを推定
(気温スキーマ <- infer_nanoarrow_schema(気温データ))
# 最低気温列のスキーマを解析して型を確認
nanoarrow_schema_parse(気温スキーマ$children$最低気温)
# Arrow配列からスキーマを取り出す
as_nanoarrow_schema(as_nanoarrow_array(気温データ$観測日))
# スキーマの名前を変更
nanoarrow_schema_modify(気温スキーマ, list(name = "観測記録"))型を指定してArrow配列をRのオブジェクトに変換:convert_arrayコマンド
as.data.frameコマンドやas.vectorコマンドより細かく変換先の型を指定したい場合に使用します。toオプションに変換後の型を表すオブジェクトを渡します。
| オプション | 意味 | 初期値 |
|---|---|---|
| array | 変換元のnanoarrow配列 | なし |
| to | 変換先の型を表すオブジェクト、NULLで既定の変換を使用 | NULL |
| … | S3メソッドに引き渡す引数 | なし |
実行結果は省略
# 農作物の収穫量データを作成
収穫データ <- data.frame(
品目 = c("じゃがいも", "たまねぎ", "かぼちゃ"),
収穫量 = c(1250L, 890L, 340L)
)
# データフレームをArrow配列に変換
収穫配列 <- as_nanoarrow_array(収穫データ)
# 既定の型でRのオブジェクトに変換
str(convert_array(収穫配列))
# toオプションで収穫量を倍精度実数型に指定して変換
str(convert_array(収穫配列, to = data.frame(品目 = character(), 収穫量 = double())))
# 品目列だけを取り出し文字列ベクトルに変換
convert_array(収穫配列$children$品目, to = character())配列ストリームの解放時に処理を実行:array_stream_set_finalizerコマンド
配列ストリームが解放されたあとに実行する処理を登録します。開いたファイルを閉じるなど、後片付けを確実におこないたい場合に利用します。
| オプション | 意味 | 初期値 |
|---|---|---|
| array_stream | 対象のnanoarrow配列ストリーム | なし |
| finalizer | 引数なしで呼び出される関数 | なし |
# 観測記録をバッチに分けてリストにまとめる
観測バッチ <- list(
data.frame(記録番号 = 1:3, 観測値 = c(4.2, 5.8, 3.1)),
data.frame(記録番号 = 4:6, 観測値 = c(6.0, 2.7, 5.5))
)
# バッチのリストから配列ストリームを作成し解放時の処理を登録
観測ストリーム <- array_stream_set_finalizer(
basic_array_stream(観測バッチ),
function() message("配列ストリームを解放しました")
)
# 1つ目のバッチを取り出しデータフレームに変換
as.data.frame(観測ストリーム$get_next())
記録番号 観測値
1 1 4.2
2 2 5.8
3 3 3.1
# ストリームを解放し登録した処理を実行
観測ストリーム$release()
配列ストリームを解放しました
<おすすめのRに関する書籍です>
Arrow IPC形式でファイルに保存して読み込む:write_nanoarrowコマンド
データをArrow IPC形式で書き出し、read_nanoarrowコマンドで読み込みます。CSVファイルと異なり、列の型がそのまま保存されます。推奨される拡張子は.arrowsです。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 読み込み元または書き出し先のraw型ベクトル、コネクション、ファイルパス | なし |
| … | 現時点では未使用 | なし |
| lazy | スキーマの確認を読み込み時まで遅らせる場合にTRUEを指定、read_nanoarrowコマンドで使用 | FALSE |
| data | Arrow IPCストリームとして書き出すオブジェクト、write_nanoarrowコマンドで使用 | なし |
# 曜日ごとの来客数データを作成
来客データ <- data.frame(
曜日 = c("月", "火", "水", "木", "金"),
来客数 = c(48L, 52L, 61L, 57L, 88L),
売上高 = c(72500, 78300, 91200, 85600, 132400)
)
# 一時ファイルのパスを作成
保存先 <- tempfile(fileext = ".arrows")
# Arrow IPC形式でファイルに書き出し
write_nanoarrow(来客データ, 保存先)
# 書き出したファイルを読み込みデータフレームに変換
読込データ <- as.data.frame(read_nanoarrow(保存先))
# 読み込んだデータの型が保持されているかを確認
str(読込データ)
'data.frame': 5 obs. of 3 variables:
$ 曜日 : chr "月" "火" "水" "木" ...
$ 来客数: int 48 52 61 57 88
$ 売上高: num 72500 78300 91200 85600 132400
# 一時ファイルを削除
unlink(保存先)PythonのArrowオブジェクトと相互に変換:as_nanoarrow_schema.python.builtin.objectコマンド
「reticulate」パッケージ経由で作成したPythonオブジェクトを、Arrow PyCapsuleプロトコルを介してRのnanoarrowオブジェクトに変換します。逆方向はr_to_pyコマンドでおこないます。Python側にnanoarrowパッケージの導入が必要です。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | 変換するPythonオブジェクト | なし |
| … | 未使用 | なし |
| schema | 要求するスキーマ、生産者側の対応状況により反映されない場合がある | NULL |
# Python側で必要なパッケージを指定
py_require("nanoarrow")
# Pythonのnanoarrowモジュールを読み込み
nanoarrowPy <- import("nanoarrow", convert = FALSE)
# Python側で最低気温のArrow配列を作成
気温Python <- nanoarrowPy$Array(c(-12L, -8L, -5L), na_int32())
# PythonのArrow配列をRのnanoarrow配列に変換
気温配列 <- as_nanoarrow_array(気温Python)
# Rのベクトルに変換して内容を確認
as.vector(気温配列)
[1] -12 -8 -5
# Rのnanoarrow配列をPythonオブジェクトに変換
r_to_py(as_nanoarrow_array(c(31L, 28L, 25L)))
nanoarrow.Array<int32>[3]
31
28
25Arrow配列をRのベクトルとして扱う:as_nanoarrow_vctrコマンド
Arrow配列をRのオブジェクトへ変換せずに、ベクトルとして扱えるようにします。Rに対応する型がない場合や、変換の負荷が大きい場合に有効です。試験的な機能のため、仕様が変更される可能性があります。
| オプション | 意味 | 初期値 |
|---|---|---|
| x | as_nanoarrow_array_streamコマンドで扱えるオブジェクト | なし |
| … | as_nanoarrow_array_streamコマンドに引き渡す引数 | なし |
| schema | 省略可能なスキーマ | NULL |
| subclass | 最終的なクラス名の先頭に付加するnanoarrow_vctrのサブクラス | character() |
# 積雪深の観測値をArrow配列に変換
積雪配列 <- as_nanoarrow_array(c(97L, 118L, 84L, 62L, 41L))
# Arrow配列をRのベクトルとして扱えるオブジェクトに変換
(積雪ベクトル <- as_nanoarrow_vctr(積雪配列))
<nanoarrow_vctr int32[5]>
[1] 97 118 84 62 41
# 通常のベクトルと同じように長さを取得
length(積雪ベクトル)
[1] 5
# 一部の要素を取り出す
積雪ベクトル[2:3]
<nanoarrow_vctr int32[2]>
[1] 118 84
# データフレームの列としてそのまま利用
data.frame(
観測地点 = c("札幌", "旭川", "恵庭", "函館", "釧路"),
積雪深 = 積雪ベクトル
)
観測地点 積雪深
1 札幌 97
2 旭川 118
3 恵庭 84
4 函館 62
5 釧路 41
<おすすめのRに関する書籍です>
この記事が誰かの役に立ちますように。