【Python追加】DuckDBをRで使う【duckplyrも】

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

R
Python
SQL
データ処理
公開

2025年9月19日

最終更新

2026年1月18日

はじめに

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

Note2026/01/17 追記

PythonでDuckDBを使う方法についても追記しました。RとPythonの両方でDuckDBを活用する方法を紹介しています。

DuckDBとは

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

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

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

Parquetファイルについて調べてみる
ファイルサイズを小さくできるということだけは知っているが…
yo5uke.com

DuckDBのインストール

Rでのインストール

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

# install.packages("pak")
pak::pak("duckdb")

もしくは

install.packages("duckdb")

Pythonでのインストール

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

# pipを使用する場合
pip install duckdb

# uvを使用する場合
uv add duckdb

uvについては以下の記事をご参照ください。

Preview image for 【renv】仮想環境を設定する【venv】
【renv】仮想環境を設定する【venv】
Rは[renv]{.fira-code}、Pythonは[venv]{.fira-code}を使います。注目のuvも紹介!
yo5uke.com

DuckDBの基本的な使い方

DuckDBでは、CSVなどのファイルを直接読み込んでSQLクエリを実行したり、RやPythonのデータフレームをデータベースに登録して操作したりすることができます。以下では、RとPythonでの基本的な使い方を紹介します。

データベースへの接続

まずは、DuckDBデータベースに接続します。DuckDBにはメモリ内データベース永続データベースの2種類があります。

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

永続データベースではファイルにデータが保存されるため、セッションを終了してもデータが保持されます。新しくセッションを開始した際にも、同じデータベースファイル(.duckdbファイル)に接続してデータを再利用できます。

一方、メモリ内データベースはセッションが終了するとデータが失われてしまいます。必要に応じて使い分けてください。

Rでの接続例

library(duckdb)
# メモリ内データベースに接続
con <- dbConnect(duckdb())

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

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

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

Pythonでの接続例

PythonでDuckDBに接続する場合は、以下のようにします。

import duckdb

# メモリ内データベースに接続
con = duckdb.connect()
# 永続データベースに接続する場合
con = duckdb.connect('data/iris.duckdb')

データの読み込み

SQLを使う場合

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

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

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

ここでは先ほど接続したconは使っていない点に注意してください。この方法を採るメリットは、R/Pythonに一度もデータを読み込まずに、DuckDBが直接CSVファイルを処理できる点です。R/Pythonに読み込まれるのはクエリの結果だけなので、メモリ消費を抑えられます。

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

Rを使う場合

SQLになじみがない方にとっては、使い慣れたRのコードでデータベース操作ができると嬉しいと思います。

実はRでDuckDBを使う場合には、今提示したコードを皆さんおなじみdplyrを使って実行できるというメリットがあります!dplyrを使って書いたコードをSQLに変換して実行してくれるのです。

データベースやSQLはなんだかよくわからないけれども、dplyrが使えるならうれしいですよね1

その場合のコードを見てみましょう。

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以外のデータもも好きな名前でテーブルを追加できます2

データの操作

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

通常のデータ処理は

データの読み込み → 書いた順に処理を実行 → 結果を返す

という流れですが、遅延評価では

データの読み込み → 書いた順に処理を記録 → 最後にまとめて実行(SQL発行) → 結果を返す

という流れになります。

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

SQLを使う場合

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

# データをクエリする
result <- dbGetQuery(con, "SELECT * FROM iris WHERE Species = 'setosa'")
# データをクエリする
result = con.sql("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の関数を使って操作できるので、慣れ親しんだ方法でデータを扱うことができます。

テーブルの削除

追加したテーブルを削除する場合は、以下のようにします。

dbExecute(con, "DROP TABLE iris")
con.sql("DROP TABLE iris")

これでirisテーブルがデータベースから削除されます。

もし安全に削除したい場合は、

dbExecute(con, "DROP TABLE IF EXISTS iris")
con.sql("DROP TABLE IF EXISTS iris")

とすることで、テーブルが存在しない場合でもエラーにならずに処理を続行できます。

データベースからの切断

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

dbDisconnect(con, shutdown = TRUE)
con.close()

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

{duckplyr}を使う

duckplyrパッケージを使うと、DuckDBとdplyrの連携がさらに便利になります。duckplyrは、DuckDB専用のdplyr拡張パッケージであり、DuckDBの機能をより活用できます。

duckplyrとは

duckplyrは、dplyrのドロップイン置き換えとして機能するパッケージです。既存のdplyrコードをそのまま使いながら、裏側でDuckDBの高速な分析エンジンを活用できます。

先述のアプローチとの違い

これまで紹介してきたduckdbパッケージを使った方法とduckplyrを使った方法の違いを整理します。

duckdb × dbplyr(データベース操作)

library(dplyr)
library(DBI)

# データベースに接続
con <- dbConnect(duckdb::duckdb())

# データベース上のテーブルを参照
tbl(con, "my_table") |>
  filter(value > 100) |>
  summarise(total = sum(value))
  • データベースに接続する
  • dplyrの構文をSQLに翻訳して実行
  • データは基本的にDB上に存在
  • collect()で初めてRのメモリに読み込まれる

duckplyr(メモリ内データの高速化)

library(duckplyr)

df <- data.frame(x = 1:1000000, y = rnorm(1000000))

df |>
  filter(y > 0) |>
  summarise(mean_x = mean(x))
  • データベース接続不要
  • Rのメモリ上のデータをDuckDBエンジンで高速処理
  • SQLへ翻訳はされない(データはR上に存在)
  • 既存のdplyrコードがそのまま高速化される

duckplyrの特徴

  1. ドロップイン置き換え
    • library(duckplyr)するだけで、多くのdplyr動詞が高速化
    • 既存のコードを書き換える必要がほとんどない
  2. 自動フォールバック
    • duckplyrで対応できない操作は、自動的に通常のdplyrにフォールバック
    • エラーで止まることなく処理が継続されます
  3. 大規模データに最適
    • DuckDBの並列処理エンジンを活用
    • メモリ効率の良い処理が可能
  4. 互換性
    • dplyrの多くの関数に対応
    • tidyverseエコシステムとシームレスに連携

簡単な使用例

library(tidyverse)
library(duckplyr)
#> Loading required package: dplyr
#> ✔ Overwriting dplyr methods with duckplyr methods.
#> ℹ Turn off with `duckplyr::methods_restore()`.

# 通常のdplyr構文がそのまま使えます
mtcars |>
  group_by(cyl) |>
  summarise(
    mean_mpg = mean(mpg),
    mean_hp = mean(hp),
    .groups = "drop"
  )
# A tibble: 3 × 3
    cyl mean_mpg mean_hp
  <dbl>    <dbl>   <dbl>
1     4     26.7    82.6
2     6     19.7   122. 
3     8     15.1   209. 

注意点として、読み込み順序が重要です。library(tidyverse)の後にlibrary(duckplyr)を読み込むことで、dplyrのメソッドがduckplyrで上書きされます。逆の順序で読み込むと、通常のdplyrが優先されてしまいます。

小さいデータだとあまり速さを実感できないかもしれませんが、大きいデータを扱うときに効果を発揮します。

まとめ

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

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

また、Rのduckplyrパッケージを使うことで、既存のdplyrコードをほぼそのまま高速化できるため、データ処理の効率化が図れます。

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

参考

Preview image for Python API
Python API
The latest stable version of the DuckDB Python client is {{ site.current_duckdb_version }}. Installation The DuckDB Python API can be installed using pip: pip install duckdb. Please see the installation page for details. It is also possible to install DuckDB using conda: conda install python-duckdb -c conda-forge. Python version: DuckDB requires Python 3.9 or newer. Basic API Usage The most straight-forward manner of running SQL queries using DuckDB is using the duckdb.sql command. import duckdb duckdb.sql("SELECT 42").show() This will run queries using an in-memory database that is stored globally inside the Python module. The result of the query is returned…
duckdb.org

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

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