DuckDBをRで使う

R上でデータベース操作を行うために、DuckDBを利用する方法についてまとめます。

R
SQL
データ処理
公開

2025年9月19日

はじめに

Rで大量のデータを扱う際、メモリ制限やパフォーマンスの問題に直面することがあります。そんなとき、軽量で高速な組み込み型のデータベースであるDuckDBを利用することで、効率的にデータ操作が可能になります。本記事では、RでDuckDBを使用する方法について解説します。

DuckDBとは

DuckDBは、組み込み型のSQLデータベースであり、特に分析ワークロードに最適化されています。SQLiteのように軽量でありながら、大規模なデータセットを効率的に処理できる点が特徴です。DuckDBは、列指向ストレージを採用しており、分析クエリのパフォーマンスが向上します。

ちなみに組み込み型データベースとは、アプリケーションに組み込まれて動作するデータベースのことを指します。サーバー型のデータベースとは異なり、外部のデータベースサーバーを必要とせず、アプリケーション内で直接データベース操作が可能です。すなわちR上で完結するということです。

列指向については、ブログでparquetを扱った際に少し触れています。以下もご覧ください。簡潔に言うと、列指向データベースは、データを列ごとに格納するため、特定の列に対するクエリが高速に処理されるという特徴があります。

DuckDBのインストール

DuckDBをRで使用するには、duckdbパッケージをインストールします。

pak::pak("duckdb")

もしくは

install.packages("duckdb")

DuckDBの基本的な使い方

データベースへの接続

まずは、DuckDBデータベースに接続します。以下のコードでは、メモリ内データベースに接続していますが、ファイルベースのデータベースを使用することも可能です。

メモリ内データベースはRセッションが終了すると消えてしまいますが、ファイルベースのデータベースは永続的に保存されます。

ファイルベースのデータベースではディスクにデータが保存されるため、Rセッションを終了してもデータが保持されます。一方、メモリ内データベースはRセッションが終了するとデータが失われてしまいますが、読み書きの速度が速いという利点があります。

library(duckdb)
# メモリ内データベースに接続
con <- dbConnect(duckdb(), dbdir = ":memory:")

ファイルベースのデータベースに接続する場合は、以下のようにします。

# ファイルベースのデータベースに接続
con <- dbConnect(duckdb(), dbdir = here::here("data/iris.duckdb"))

この方法だと、dataフォルダにiris.duckdbという名前でデータベースファイルが作成されます。.duckdbファイルを置くパスは自由に変更してください。

データの読み込み

SQLを使う場合

DuckDBは様々なデータ形式をサポートしています。ここでは、CSVファイルを読み込む例を示します。

# CSVファイルを読み込む
dbExecute(con, "CREATE TABLE iris AS SELECT * FROM read_csv('data/iris.csv')")

さて、ここで突然SQL文が出てきましたが、なんとなく単語を見てもらえればわかる通り、read_csv('data/iris.csv')から全ての列を選択(*は全ての列を意味します)して、irisという名前のテーブルを作成(CREATE TABLE iris AS)しています。

ちなみに、僕はSQLはあまりわかりません。いちいち調べないと先ほどのコードも書けないレベルなのですが、この方法をとるメリットを先に説明しておきます。

この方法のメリットは、Rに一度もデータを読み込まずに、DuckDBが直接CSVファイルを処理できる点です。 そのため、大規模なCSVでもメモリを無駄に消費せず、高速にテーブルを作成できます。

また、CREATE TABLE ... AS SELECT ...という形で書けば、SQLの標準的な構文で柔軟に前処理(列の選択やフィルタリングなど)を行いながら、テーブルを作成できます。

Rを使う場合

とはいえ新しくSQLを覚えるのは面倒だしRで書けたら嬉しいという僕なので、ここでRを使うことの意義を示します。

それは、今提示したコードを皆さんおなじみdplyrを使って実行できる、ということです。データベースはなんだかよくわからないけれども、dplyrが使えるならうれしいですよね。

ただ、先ほどのメリットの裏返しで、一度R上でデータを読み込んでからデータベースに送り込むことになるので、メモリを多く消費することと、データが大きいと処理速度が遅くなることには注意してください。

dplyrを使って書いたコードをSQLに変換して実行してくれるのです。その場合のコードを見てみましょう。

library(tidyverse)  # `dplyr`だけでもOKです。
iris_data <- read_csv(here::here("data/iris.csv"))
duckdb_register(con, iris, "iris_data")

まずはデータベースに入れたいデータをread_csv()で読み込みます。ここではdataフォルダにあるiris.csvを読み込んでいます。

続いてduckdb_register()を使って、iris_datairisという名前でデータベースに登録しています。これでデータベース上にirisテーブルが作成されました。

ちなみにファイルベースのデータベースを使いたい場合は、duckdb_register()の代わりにdbWriteTable()を使います。

dbWriteTable(con, "iris", iris_data)

これで.duckdbファイルにirisテーブルが保存されます。

ちなみに、データベースにはテーブルを複数作成できるので、iris以外のデータもも好きな名前でテーブルを追加できます1

データの操作

ところで、データベースを使うメリットのひとつに、遅延評価(lazy evaluation) があります。これは、実際に結果を取り出すまでSQLが実行されない仕組みのことです。

通常のデータ処理は
データの読み込み → 書いた順に処理を実行 → 結果を返す
という流れですが、遅延評価では
データの読み込み → 書いた順に処理を記録 → 最後にまとめて実行(SQL発行) → 結果を返す
という流れになります。

このおかげで、大規模なデータセットでも不要な計算が省かれ、効率的に処理できます。

SQLを使う場合

SQLを使ってデータを操作する場合、以下のようにクエリを実行します。

# データをクエリする
result <- dbGetQuery(con, "SELECT * FROM iris WHERE Species = 'setosa'")

この例では、irisテーブルからSpeciessetosaの行を選択しています。

dplyrを使う場合

dplyrを使ってデータを操作する場合、以下のように記述します(おなじみの感じですね)。

result <- tbl(con, "iris") |> 
  filter(Species == "setosa") |> 
  collect()

tbl(con, "iris")でデータベース上のirisテーブルを参照し、filter()で条件を指定しています。最後にcollect()を使って結果をRのデータフレームとして取得します。

ここで重要なのは、collect()を呼び出すまではSQLは実行されないという点です。filter()などの処理を追加するたびに、裏でSQLクエリが少しずつ組み立てられていき、最後にcollect()を呼び出した時点でまとめて実行されます。

つまり、collect()を呼び出すまではデータはRに読み込まれず、操作はあくまで「SQLを組み立てているだけ」です。collect()を実行した瞬間に初めてSQLが発行され、結果がRにデータフレームとして取り込まれます。

データベースになじみがない方は違和感があるかもしれませんが(かく言う僕もですが)、con自体はデータそのものではなく、DuckDBデータベースへの接続(コネクション)を表すオブジェクトです。

普段のRではデータフレームを直接操作しますが、ここではまず接続オブジェクトconを通して「このデータベースの中にあるテーブルを参照しますよ」という指示を出します。

つまり、conはデータの実体ではなく、「データベースへの窓口」や「リモコン」のようなものと考えると分かりやすいです(ChatGPT曰く)。

とりあえず、collect()の前までは通常のデータ処理と同じようにdplyrの関数を使って操作できるので、慣れ親しんだ方法でデータを扱うことができます。

データベースからの切断

データベースの操作が終わったら、接続を切断します。

dbDisconnect(con, shutdown = TRUE)

shutdown = TRUEを指定すると、メモリ内データベースの場合はデータが消去され、ファイルベースのデータベースの場合は接続が閉じられます。

まとめ

今回は、RでDuckDBを使用する方法についてまとめました。

DuckDBは軽量で高速な組み込み型データベースであり、大規模なデータセットを効率的に処理できます。SQLを直接使う方法と、dplyrを使う方法の両方が利用可能であり、遅延評価によりパフォーマンスが向上します。

最近は仕事ででかいデータを扱うことが増えてきたので、今後もDuckDBを活用していきたいと思います(というか活用し始めています)。ぜひ試しあれ。

参考

  1. とはいえ、プロジェクトごとなどで.duckdbは分けた方が良いとは思います。↩︎