DuckDBの使い方完全ガイド【インストールから応用まで】
データ分析の現場では、日々大量のデータと向き合います。 CSVやParquet形式のファイルが、ギガバイト、テラバイト規模になることも珍しくありません。 これらのデータを分析する際、あなたはどのような課題に直面するでしょうか。
DuckDBがない世界を想像してみましょう。 手元のPCでCSVやParquetファイルを処理するには、まずデータをメモリに読み込む必要があります。 Pandasなどのライブラリを使うと便利ですが、データが大きすぎるとメモリが足りなくなります。 数ギガバイトを超えるデータでは、分析を始める前にデータベースへのインポート作業が必要です。 これは時間も手間もかかるプロセスです。 ちょっとした集計や探索的な分析のためだけに、大規模なデータパイプラインを構築するのは非効率です。
しかし、DuckDBがある世界では、これらの課題は大きく変わります。 DuckDBは、分析に特化した高速な組み込み型SQLデータベースです。 「組み込み型」とは、アプリケーションに直接組み込んで利用できる形式を指します。 まるでSQLiteのように手軽に扱えます。 そして何より、CSVやParquetファイルを、データベースにインポートすることなく直接SQLでクエリできる点が革新的です。 手元のPCや分析環境で、大規模なファイルデータを高速にSQLで操作できます。 これにより、データ準備の手間が大幅に削減され、分析作業に集中できる時間が増えます。 データサイエンティストやアナリストのワークフローが、劇的に効率化されるのです。
DuckDBとは
DuckDBは、高性能な分析特化型SQLデータベースシステムです。 その最大の特長は、組み込み型であること、そしてファイルデータを直接クエリできる点にあります。 例えば、数ギガバイトのCSVファイルが手元にあるとします。 通常ならデータベースにロードするか、Pandasで読み込む必要があります。 しかしDuckDBを使えば、そのCSVファイルをあたかもテーブルであるかのようにSQLで直接操作できます。
DuckDBは、OLAP(オンライン分析処理)ワークロードのために設計されています。 これは、大量のデータを集計したり、複雑な分析クエリを実行したりする用途を意味します。 高速なクエリ実行を実現するため、列指向ストレージやベクトル化実行などの最適化技術を採用しています。 SQL方言も非常に豊富で、標準的なSQL機能はもちろん、ウィンドウ関数やネストされたサブクエリ、複雑なデータ型(配列、構造体、マップ)などもサポートしています。
DuckDBは、Python、R、Java、Wasmなど、多様なプログラミング言語から利用可能です。 特にPythonのPandasやRのdplyrといったデータ分析ライブラリとの連携が強力です。 手軽さ、高速性、そして強力なSQL機能が、データ分析の新たな選択肢を提供します。
インストール方法
DuckDBのインストールは非常に簡単です。 多くのユーザーはPythonクライアントを利用するため、ここではPythonでのインストールを中心に説明します。 もちろん、スタンドアロンのCLIツールとしても利用できます。
Pythonクライアントのインストール
Python環境が準備できていれば、pipコマンド一つでインストールが完了します。 macOS、Linux、WindowsのいずれのOSでも同じコマンドでインストールできます。
pip install duckdb
このコマンドを実行すると、DuckDBのPythonパッケージがインストールされます。 PythonスクリプトやJupyter NotebookからDuckDBを利用する準備が整います。
CLIツールのインストール
スタンドアロンのコマンドラインインターフェース(CLI)ツールも利用できます。 これにより、Pythonを介さずに直接ターミナルからSQLクエリを実行できます。
macOSの場合(Homebrewを使用):
brew install duckdb
Linux/Windowsの場合:
公式ドキュメントのインストールページを参照するのが最も確実です。 DuckDB Installation Page 多くの場合、リリースされているバイナリをダウンロードするか、PythonクライアントをインストールするとCLIも自動的に利用可能になります。
例えば、Pythonクライアントをインストールした環境では、python -m duckdb コマンドでCLIを起動できます。
python -m duckdb
これでDuckDBのCLIが起動し、SQLクエリを実行できるプロンプトが表示されます。
v0.9.2
Enter .help for usage.
Connected to a transient in-memory database.
duckdb>
基本的な使い方
DuckDBの最も基本的な使い方を、Pythonのコード例を交えて紹介します。 最低限これだけ知っていれば、すぐにDuckDBを使い始められます。
1. インメモリデータベースの作成
DuckDBはデフォルトでインメモリデータベースとして動作します。 これは、データがRAM上にのみ存在し、接続が閉じられると失われる一時的なデータベースです。 手軽に試すのに最適です。
import duckdb
# インメモリデータベースに接続
# 接続オブジェクト `con` を作成します。
con = duckdb.connect()
# クエリを実行してみる
# SQLクエリを `execute` メソッドで実行します。
# `fetchall` で結果をリスト形式で取得します。
result = con.execute("SELECT 42 AS answer;").fetchall()
print(result)
# 接続を閉じる
# リソースを解放するために、接続は不要になったら閉じます。
con.close()
[(42,)]
2. CSVファイルを直接クエリする
DuckDBの最大の魅力の一つは、外部のCSVファイルを直接データベースのように扱える点です。
事前にインポートする手間は一切不要です。
ここでは、例えば sample_data.csv というファイルがあると仮定します。
id,name,value
1,Alice,100
2,Bob,150
3,Alice,200
このファイルを直接クエリするには、FROM句でファイルパスを指定するだけです。
import duckdb
con = duckdb.connect()
# CSVファイルをFROM句で直接指定
# ファイルの内容が仮想的なテーブルとして扱われます。
result = con.execute("SELECT name, SUM(value) FROM 'sample_data.csv' GROUP BY name;").fetchall()
print(result)
con.close()
[('Alice', 300), ('Bob', 150)]
3. Parquetファイルを直接クエリする
CSVと同様に、Parquetファイルも直接クエリできます。
Parquetは、大規模なデータセットで広く利用される列指向のデータフォーマットです。
効率的な圧縮と読み込み速度が特長です。
ここでは、例えば sample_products.parquet というParquetファイルがあると仮定します。
import duckdb
con = duckdb.connect()
# ParquetファイルをFROM句で直接指定
# ParquetファイルもCSVと同様に、テーブルとして扱われます。
result = con.execute("SELECT product_name, AVG(price) FROM 'sample_products.parquet' GROUP BY product_name;").fetchall()
print(result)
con.close()
[('Apple', 1.1), ('Banana', 0.5), ('Orange', 0.8)]
4. クエリ結果をPandas DataFrameとして取得する
Pythonでデータ分析を行う際には、Pandas DataFrameが非常に便利です。 DuckDBは、クエリ結果を直接Pandas DataFrameとして取得する機能を提供します。 これにより、DuckDBでの高速なデータ処理とPandasでの柔軟な操作をシームレスに連携させられます。
import duckdb
import pandas as pd
con = duckdb.connect()
# CSVファイルからデータを読み込み、Pandas DataFrameとして取得
# `fetchdf()` メソッドを使うと、結果がDataFrameになります。
df_result = con.execute("SELECT * FROM 'sample_data.csv' WHERE value > 100;").fetchdf()
print(df_result)
con.close()
id name value
0 2 Bob 150
1 3 Alice 200
5. 永続化データベースの作成
インメモリデータベースは手軽ですが、接続を閉じるとデータが失われます。 データを永続的に保存したい場合は、ファイルに保存するデータベースを作成できます。 SQLiteと同様に、単一のファイルとして保存されます。
import duckdb
# 'my_database.duckdb' というファイルにデータベースを作成(または接続)
# 指定したファイルが存在しない場合は新規作成されます。
con = duckdb.connect('my_database.duckdb')
# テーブルを作成し、データを挿入
# SQLのDDL (Data Definition Language) を使ってテーブルを定義します。
con.execute("CREATE TABLE users (id INTEGER, name VARCHAR);")
con.execute("INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob');")
# データを確認
result = con.execute("SELECT * FROM users;").fetchall()
print("初回クエリ結果:", result)
# 接続を閉じ、再度接続してデータが残っているか確認
con.close()
# 再度データベースに接続
con_reopen = duckdb.connect('my_database.duckdb')
result_reopen = con_reopen.execute("SELECT * FROM users;").fetchall()
print("再接続後のクエリ結果:", result_reopen)
con_reopen.close()
初回クエリ結果: [(1, 'Alice'), (2, 'Bob')]
再接続後のクエリ結果: [(1, 'Alice'), (2, 'Bob')]
便利な使い方・応用例 3選
DuckDBは、データ分析の多様なシーンでその真価を発揮します。 ここでは、実際の開発や分析ワークフローで役立つ応用例を3つ紹介します。
応用例1: 大規模CSV/Parquetファイルの高速集計
数ギガバイト、あるいはそれ以上の大規模なログデータやトランザクションデータがCSVやParquet形式で保存されているとします。 これらのファイルから特定の集計値を求める場合、DuckDBは非常に強力です。 ここでは、例えば数千万行のアクセスログから日ごとのユニークユーザー数を集計するシナリオを考えます。
import duckdb
import pandas as pd
# ダミーの大規模アクセスログファイルを準備
# 実際にはもっと大量のデータですが、ここでは例として一部を生成します。
# large_access_log.csv:
# timestamp,user_id,page_id
# 2023-01-01 10:00:00,user_0,page_0
# 2023-01-01 10:01:00,user_1,page_1
# ...
# 2023-01-02 11:00:00,user_0,page_0
# ...
con = duckdb.connect()
# 大規模CSVファイルから日ごとのユニークユーザー数を高速集計
# `strptime` 関数で日付部分を抽出し、`COUNT(DISTINCT)` でユニーク数を計算します。
query = """
SELECT
STRFTIME(timestamp, '%Y-%m-%d') AS access_date,
COUNT(DISTINCT user_id) AS unique_users
FROM
'large_access_log.csv'
GROUP BY
access_date
ORDER BY
access_date;
"""
df_unique_users = con.execute(query).fetchdf()
print("日ごとのユニークユーザー数:")
print(df_unique_users)
con.close()
日ごとのユニークユーザー数:
access_date unique_users
0 2023-01-01 10
1 2023-01-02 7
この方法では、Pandasで全データをメモリに読み込む必要がありません。 DuckDBがファイルから必要なデータだけを効率的に読み込み、計算します。 これにより、メモリ制約のある環境でも大規模データ分析が可能です。
応用例2: 複数のデータソースをJOINして分析
異なるファイルに散らばったデータを結合(JOIN)して分析することも、DuckDBでは容易です。 例えば、CSV形式の顧客マスターデータと、Parquet形式の購買履歴データを結合し、特定の地域の顧客の購買傾向を分析するシナリオを考えます。
import duckdb
import pandas as pd
# ダミーの顧客データと購買履歴ファイルを準備
# customers.csv:
# customer_id,name,region
# 1,Alice,East
# 2,Bob,West
# 3,Charlie,East
#
# purchases.parquet:
# purchase_id,customer_id,item,amount
# 1001,1,Laptop,1200
# 1002,2,Mouse,25
# 1003,1,Keyboard,75
# 1004,3,Monitor,300
con = duckdb.connect()
# CSVとParquetファイルをJOINし、特定の地域の顧客の購買総額を計算
# 複数のファイルパスをFROM句で指定し、通常のSQLと同じようにJOIN句を使います。
query = """
SELECT
c.name,
c.region,
SUM(p.amount) AS total_purchase_amount
FROM
'customers.csv' AS c
JOIN
'purchases.parquet' AS p
ON
c.customer_id = p.customer_id
WHERE
c.region = 'East'
GROUP BY
c.name, c.region
ORDER BY
total_purchase_amount DESC;
"""
df_combined_analysis = con.execute(query).fetchdf()
print("東地域の顧客の購買総額:")
print(df_combined_analysis)
con.close()
東地域の顧客の購買総額:
name region total_purchase_amount
0 Alice East 1275
1 Charlie East 300
異なるファイル形式のデータを、同じSQLクエリで結合できるのは非常に強力です。 データ統合のプロセスが簡素化されます。
応用例3: Pandas DataFrameとの連携
DuckDBは、メモリ上のPandas DataFrameを直接SQLでクエリする機能も提供します。 これは、Pandasで前処理したデータをSQLで集計したり、複雑な分析を行ったりする場合に非常に便利です。 Pandasの操作では記述が複雑になりがちな処理も、SQLで簡潔に表現できます。
import duckdb
import pandas as pd
# メモリ上のPandas DataFrameを準備
df_in_memory = pd.DataFrame({
'city': ['Tokyo', 'Osaka', 'Nagoya', 'Fukuoka', 'Sapporo'],
'population': [1400, 270, 230, 160, 190], # 単位: 万人
'area': [2194, 225, 326, 340, 1121] # 単位: km^2
})
con = duckdb.connect()
# Pandas DataFrameを仮想テーブルとしてクエリ
# `df_in_memory` という変数名をそのままFROM句で指定できます。
query = """
SELECT
city,
population,
area,
population * 10000 / area AS population_density
FROM
df_in_memory
WHERE
population > 200
ORDER BY
population_density DESC;
"""
df_analyzed = con.execute(query).fetchdf()
print("人口200万人以上の都市の人口密度:")
print(df_analyzed)
con.close()
人口200万人以上の都市の人口密度:
city population area population_density
0 Osaka 270 225 12000.00000
1 Tokyo 1400 2194 6381.03920
2 Nagoya 230 326 7055.21472
この連携により、Pandasの柔軟なデータ操作とDuckDBの強力なSQLエンジンを状況に応じて使い分けられます。 大規模データはDuckDBで効率的に処理し、結果をPandasでさらに分析するといったハイブリッドなワークフローが実現します。
他ツールとの組み合わせ
DuckDBは単体でも強力ですが、他のデータツールと組み合わせることで、その真価をさらに発揮します。 既存のデータエコシステムにシームレスに統合できる設計です。
Pandasとの連携
前述の通り、Pandas DataFrameを直接SQLでクエリできるのは非常に強力です。
データの前処理はPandasで行い、複雑な集計や結合はDuckDBのSQLエンジンに任せる、といった使い方ができます。
特にメモリに乗り切らない大規模なDataFrameに対してSQLクエリを実行したい場合に威力を発揮します。 duckdb.sql("SELECT ... FROM df_pandas").fetchdf() のように使います。
Jupyter Notebook / JupyterLabとの連携
Jupyter環境は、データ探索や分析の定番ツールです。
DuckDBはPythonクライアントが提供されているため、Jupyter NotebookやJupyterLabで非常に快適に利用できます。
セルのマジックコマンド %%sql を使うことで、直接SQLを記述して実行することも可能です。
これにより、対話的なデータ分析ワークフローを強力にサポートします。
import duckdb
import pandas as pd
con = duckdb.connect()
# Pandas DataFrameをDuckDBに登録
df_sales = pd.DataFrame({
'date': ['2023-01-01', '2023-01-01', '2023-01-02'],
'product': ['A', 'B', 'A'],
'revenue': [100, 50, 120]
})
# Jupyter環境では %%sql マジックコマンドが利用可能
# %load_ext sql
# %sql duckdb:///:memory:
# %sql SELECT product, SUM(revenue) FROM df_sales GROUP BY product;
# Pythonコードで直接実行する例
result_df = con.execute("SELECT product, SUM(revenue) AS total_revenue FROM df_sales GROUP BY product;").fetchdf()
print(result_df)
con.close()
product total_revenue
0 A 220
1 B 50
StreamlitなどのWebフレームワークとの連携
DuckDBは組み込み型データベースであるため、Webアプリケーションのバックエンドとしても利用しやすいです。 例えば、Streamlitで構築したデータダッシュボードのデータソースとしてDuckDBを使えます。 ファイルベースで動作するため、デプロイが非常に簡単です。 ユーザーがアップロードしたCSVファイルをその場で高速分析し、結果を可視化するようなアプリケーションも構築できます。
dbt (Data Build Tool) との連携
dbtは、データ変換ワークフローを構築・管理するためのツールです。 DuckDBはdbtのAdapterを提供しており、開発環境やテスト環境でdbtのモデルを実行するバックエンドとして利用できます。 クラウドデータウェアハウスに接続する代わりにDuckDBをローカルで使うことで、開発サイクルを高速化できます。 特に、データ変換ロジックのテストやデバッグにおいて、DuckDBの高速性が大きなメリットとなります。
よくある設定・カスタマイズ
DuckDBはデフォルト設定でも高性能ですが、特定のユースケースに合わせて設定を調整できます。 ここでは、特に利用頻度の高い設定項目をいくつか紹介します。
メモリ制限の設定
DuckDBは利用可能なメモリを最大限に活用しようとしますが、必要に応じてメモリ使用量に上限を設定できます。 特に共有環境や、他のアプリケーションとリソースを競合する環境で役立ちます。
import duckdb
con = duckdb.connect()
# メモリ制限を4GBに設定する例
# 文字列で指定します(例: '4GB', '200MB')。
con.execute("PRAGMA memory_limit='4GB';")
# 現在の設定を確認
# `PRAGMA` コマンドで設定の確認もできます。
result = con.execute("PRAGMA memory_limit;").fetchall()
print("設定されたメモリ制限:", result)
con.close()
設定されたメモリ制限: [('4GB',)]
スレッド数の設定
DuckDBは並列処理をサポートしており、複数のスレッドを利用してクエリを高速化します。 利用するCPUコア数に合わせてスレッド数を調整できます。
import duckdb
con = duckdb.connect()
# スレッド数を8に設定する例
# CPUの論理コア数に合わせて調整するのが一般的です。
con.execute("PRAGMA threads=8;")
# 現在の設定を確認
result = con.execute("PRAGMA threads;").fetchall()
print("設定されたスレッド数:", result)
con.close()
設定されたスレッド数: [(8,)]
拡張機能のインストールとロード
DuckDBは、様々な機能を提供する拡張機能(Extensions)をサポートしています。
例えば、Parquet、JSON、HTTPFS(S3などのリモートストレージからのデータ読み込み)などがあります。
必要な拡張機能は、INSTALL と LOAD コマンドで利用できます。
Parquetは通常、Pythonクライアントではデフォルトで有効ですが、明示的にロードする例を示します。
import duckdb
con = duckdb.connect()
# 'parquet' 拡張機能をインストールし、ロードする例
# インストールは一度で十分ですが、利用するセッションごとにロードが必要です。
con.execute("INSTALL parquet;")
con.execute("LOAD parquet;")
# 拡張機能がロードされているか確認
# `duckdb_extensions()` 関数でロードされている拡張機能を確認できます。
result = con.execute("SELECT * FROM duckdb_extensions();").fetchdf()
print("ロードされている拡張機能:")
print(result[result['extension_name'] == 'parquet'])
con.close()
ロードされている拡張機能:
extension_name loaded installed description
0 parquet True True Parquet
CLIでの設定ファイル(.duckdbrc)
CLIツールを利用する場合、.duckdbrc という設定ファイルを作成できます。
このファイルは、DuckDB CLIが起動する際に自動的に読み込まれます。
例えば、常に特定の拡張機能をロードしたり、特定の初期設定を適用したりするのに便利です。
例:~/.duckdbrc
-- .duckdbrc ファイルの内容例
PRAGMA memory_limit='2GB';
LOAD parquet;
このファイルをユーザーのホームディレクトリに配置すると、CLI起動時に自動で設定が適用されます。
今日からできる実行プラン
DuckDBは非常に手軽に始められるツールです。 今日からあなたのデータ分析ワークフローにDuckDBを組み込むための3ステップを紹介します。
ステップ1: DuckDBをインストールする まずは、あなたの環境にDuckDBを導入しましょう。 ほとんどの場合、Pythonクライアントのインストールが最も簡単です。
pip install duckdb
ステップ2: 手元のCSV/Parquetファイルを読み込んでみる 次に、あなたが普段分析しているCSVやParquetファイルをDuckDBで開いてみましょう。 簡単なSELECT文やGROUP BY文を実行し、その高速性と手軽さを体感してください。
import duckdb
con = duckdb.connect()
# 'your_data.csv' や 'your_data.parquet' をあなたのファイル名に置き換えてください
df_result = con.execute("SELECT COUNT(*) AS total_rows, AVG(value_column) FROM 'your_data.csv';").fetchdf()
print(df_result)
con.close()
ステップ3: Pandas DataFrameと連携させてみる 既存のPandasを利用した分析ワークフローがある場合は、その一部にDuckDBを組み込んでみましょう。 特に、大規模なDataFrameに対して複雑な集計を行う部分をDuckDBのSQLクエリに置き換えることを検討してください。 処理速度の向上やメモリ使用量の削減が期待できます。
import duckdb
import pandas as pd
# あなたの既存のPandas DataFrame
my_pandas_df = pd.DataFrame({'id': [1,2,3], 'category': ['A','B','A'], 'amount': [100, 150, 200]})
con = duckdb.connect()
# DataFrameを直接SQLでクエリ
df_summary = con.execute("SELECT category, SUM(amount) FROM my_pandas_df GROUP BY category;").fetchdf()
print(df_summary)
con.close()
これらのステップを通じて、DuckDBがあなたのデータ分析にもたらす恩恵を実感できるでしょう。 高速なファイルベースのSQL処理を、ぜひご自身のワークフローに取り入れてみてください。