Sys.setenv(NOT_CRAN = "true")
install.packages("polars", repos = "https://community.r-multiverse.org")
# または開発版
# install.packages('polars', repos = c("https://rpolars.r-universe.dev", "https://cloud.r-project.org"))はじめに
Rでデータ処理を行う際、メモリ不足や処理速度の問題に悩まされることがあります。特に大規模データを扱う場合、{tidyverse}の関数だけでは限界を感じることも少なくありません。
そこで注目されているのが{polars}です。{polars}はRustで書かれた高速なデータフレームライブラリで、大規模データの処理を効率的に行うことができます。この記事では、Rで{polars}を使う方法と、tidyverseの文法で使える{tidypolars}について解説します。
polarsとは
polarsは、Rustで実装されたデータフレームライブラリです。Pythonでよく知られていますが、R版も提供されており、以下のような特徴があります:
- 高速な処理: Rustベースで実装されており、{dplyr}や{data.table}よりも高速
- 遅延評価: クエリを最適化してから実行するため、効率的
- メモリ効率: 列指向フォーマットのApache Arrowを内部で使用
- 並列処理: マルチコアを活用した並列処理が自動的に行われる
{polars}の独自の文法は、メソッドチェーンを使ったものですが、{tidyverse}に慣れた人には少し馴染みにくいかもしれません。そこで登場するのが{tidypolars}です。
インストール
polarsのインストール
{polars}はCRANにはまだ登録されていないため、以下のコマンドでインストールします。
tidypolarsのインストール
{tidypolars}もCRANには登録されていないため、同様にインストールします。
Sys.setenv(NOT_CRAN = "true")
install.packages("tidypolars", repos = c("https://community.r-multiverse.org", "https://cloud.r-project.org"))
# または開発版
# pak::pak("etiennebacher/tidypolars")polarsの基本的な使い方
まずはpolars本来の文法で基本的な使い方を見ていきます。
そもそも{polars}はPythonで一般的に使われており、R版はそのラッパーとして提供されています。Rで使う場合も、Python版と似たようなメソッドチェーンのスタイルを採用しています。
Pythonの場合はimport polars as plとしてpl.read_csv()のように使いますが、Rではpl$read_csv()のように$でアクセスします。
library(polars)
# データの読み込み(CSVファイルから)
df <- pl$read_csv(here::here("data/251125_r_polars/data.csv"))
# 遅延評価を使う場合(LazyFrame)
lf <- pl$scan_csv(here::here("data/251125_r_polars/data.csv"))
# データ処理の例
result <- lf$
filter(pl$col("age") > 30)$
select(c("name", "age", "city"))$
group_by("city")$
agg(pl$col("age")$mean())$
collect() # ここで初めて実行される
print(result)shape: (4, 2)
┌─────────┬───────────┐
│ city ┆ age │
│ --- ┆ --- │
│ str ┆ f64 │
╞═════════╪═══════════╡
│ Fukuoka ┆ 36.0 │
│ Osaka ┆ 39.666667 │
│ Tokyo ┆ 42.25 │
│ Nagoya ┆ 39.0 │
└─────────┴───────────┘
主な関数
pl$read_csv(): CSVファイルを即座に読み込む(Eager evaluation)pl$scan_csv(): CSVファイルをスキャンする(Lazy evaluation)filter(): 行のフィルタリングselect(): 列の選択group_by(): グループ化agg(): 集計collect(): 遅延評価の結果を取得
tidypolarsで使いやすく
{tidypolars}を使うと、{polars}の高速性を保ちながら、{tidyverse}の文法で書くことができます。
ここでは例として、irisデータセットを使って{tidypolars}の基本的な使い方を見ていきます1。
library(tidypolars)
library(tidyverse)
# データの読み込み
df <- read_csv_polars(here::here("data/iris.csv"))
result <- df |>
filter(Sepal.Length > 5.0) |>
select(Sepal.Length, Sepal.Width, Species) |>
group_by(Species) |>
summarise(
avg_sepal_length = mean(Sepal.Length),
avg_sepal_width = mean(Sepal.Width)
)
print(result)shape: (3, 3)
┌────────────┬──────────────────┬─────────────────┐
│ Species ┆ avg_sepal_length ┆ avg_sepal_width │
│ --- ┆ --- ┆ --- │
│ str ┆ f64 ┆ f64 │
╞════════════╪══════════════════╪═════════════════╡
│ versicolor ┆ 5.997872 ┆ 2.804255 │
│ virginica ┆ 6.622449 ┆ 2.983673 │
│ setosa ┆ 5.313636 ┆ 3.713636 │
└────────────┴──────────────────┴─────────────────┘
tidypolarsの利点
- tidyverseの文法が使える: {dplyr}に慣れている人はすぐに使える
- 高速性は維持: 内部的には{polars}のエンジンを使用
- 学習コストが低い: 新しい文法を覚える必要がない
対応している関数
{tidypolars}は{dplyr}および{tidyr}のS3メソッドとして実装されているため、これらのパッケージを読み込む必要があります({tidyverse}を一緒に読み込んでおけば安心です!)。 この点を踏まえた上で、{tidypolars}では以下の主要な動詞がサポートされています:
filter(): 行のフィルタリングselect(): 列の選択mutate(): 列の追加・変更summarise()/summarize(): 集計arrange(): 並び替えgroup_by(): グループ化left_join(),inner_join()など: 結合操作
DataFrameとLazyFrameの使い分け
{tidypolars}では、2つのデータ読み込み方法があります:
DataFrame(即座に評価)
# データを即座に読み込む
df <- read_csv_polars("data.csv")- 小〜中規模データに適している
- 即座に結果を確認できる
- メモリに全データが読み込まれる
LazyFrame(遅延評価)
# データをスキャンするだけで、まだ読み込まれない
lf <- scan_csv_polars("data.csv")
result <- lf |>
filter(...) |>
collect()- 大規模データに適している
- クエリ最適化の恩恵を受けられる
- 必要な部分だけメモリに読み込む
大規模データの場合はLazyFrameを使うことを推奨します。この場合は、最後にcollect()を呼び出して結果を取得してください。
実践例
例1: 大規模CSVファイルの処理
library(tidypolars)
library(tidyverse)
# 大規模CSVファイルを効率的に処理
result <- read_csv_polars(here::here("data/251125_r_polars/large_dataset.csv")) |>
filter(!is.na(value)) |>
mutate(
value_log = log(value),
value_category = case_when(
value < 100 ~ "small",
value < 1000 ~ "medium",
TRUE ~ "large"
)
) |>
summarise(
count = n(),
mean_value = mean(value),
median_value = median(value),
.by = value_category
) |>
arrange(desc(mean_value))
print(result)shape: (3, 4)
┌────────────────┬───────┬────────────┬──────────────┐
│ value_category ┆ count ┆ mean_value ┆ median_value │
│ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ u32 ┆ f64 ┆ f64 │
╞════════════════╪═══════╪════════════╪══════════════╡
│ large ┆ 4 ┆ 1100.0 ┆ 1075.0 │
│ medium ┆ 31 ┆ 472.741935 ┆ 490.0 │
│ small ┆ 15 ┆ 79.6 ┆ 82.0 │
└────────────────┴───────┴────────────┴──────────────┘
例2: データの書き出し
# 処理結果をParquet形式で保存
result |>
write_parquet_polars("output/result.parquet")
# 処理結果をCSV形式で保存
result |>
write_csv_polars("output/result.csv")Parquet形式については以下の記事もご覧ください。
polarsとtidyverseの使い分け
polars/tidypolarsの使用が適しているケース
- 大規模データ: 数百MB以上のデータを扱う場合
- 処理速度が重要: パフォーマンスが求められる場合
- メモリ制限: メモリが限られている環境での処理
注意点
tidypolarsの制約
{tidypolars}は{polars}のラッパーなので、一部の{dplyr}/{tidyr}機能は未対応です:
rowwise()を使った行ごとの操作- 一部の複雑なウィンドウ関数
- カスタム関数の中での一部の集計関数
ただし、across()、pivot_longer()、pivot_wider()などの主要な関数はサポートされています。
データ型の違い
{polars}とRのデータフレームでは、データ型の扱いが異なる場合があります。必要に応じてas.data.frame()で通常のデータフレームに変換してください。
# polarsのDataFrameをRのdata.frameに変換
df_r <- as.data.frame(df_polars)
# tibbleに変換する場合
df_tibble <- tibble::as_tibble(df_polars)
# または collect() を使う方法(LazyFrameの場合)
df_r <- df_polars |>
collect() |> # まずDataFrameに変換
as.data.frame() # 次にR data.frameに変換おわりに
この記事では、Rで{polars}を使う方法と、tidyverseライクな文法で使える{tidypolars}について解説しました。
大規模データを扱う際は、{polars}の高速性が大きなメリットになります。特に{tidypolars}を使えば、既存の{tidyverse}の知識を活かしながら高速なデータ処理が実現できます。
すべてのケースで{polars}が最適とは限りませんが、データサイズや処理内容に応じて、{tidyverse}や{polars}などを使い分けることが重要だと思います。
ぜひ{polars}を試してみてください!
参考
注
iris.csvはデフォルトで使えるirisデータフレームをCSV化しただけです。↩︎