本文へスキップ
からだにいいもの

Rのトピックスを中心に『まだ、まだ、知らない、役に立つ情報?』を発信します。

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] TRUE

Rのオブジェクトを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()

データの型情報をスキーマとして取得する: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()
配列ストリームを解放しました

Arrow IPC形式でファイルに保存して読み込む:write_nanoarrowコマンド

データをArrow IPC形式で書き出し、read_nanoarrowコマンドで読み込みます。CSVファイルと異なり、列の型がそのまま保存されます。推奨される拡張子は.arrowsです。

オプション意味初期値
x読み込み元または書き出し先のraw型ベクトル、コネクション、ファイルパスなし
現時点では未使用なし
lazyスキーマの確認を読み込み時まで遅らせる場合にTRUEを指定、read_nanoarrowコマンドで使用FALSE
dataArrow 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
25

Arrow配列をRのベクトルとして扱う:as_nanoarrow_vctrコマンド

Arrow配列をRのオブジェクトへ変換せずに、ベクトルとして扱えるようにします。Rに対応する型がない場合や、変換の負荷が大きい場合に有効です。試験的な機能のため、仕様が変更される可能性があります。

オプション意味初期値
xas_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


この記事が誰かの役に立ちますように。

スポンサーリンク
価格および配送状況は変更される場合があります。購入時は商品ページをご確認ください。
当サイトに表示されている商品情報はAmazonから提供されたものであり、更新または削除される場合があります。
karada-goodはAmazonアソシエイトとして、適格販売により収入を得ています。