AI

DuckDBの使い方完全ガイド【インストールから応用まで】

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などのリモートストレージからのデータ読み込み)などがあります。 必要な拡張機能は、INSTALLLOAD コマンドで利用できます。 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処理を、ぜひご自身のワークフローに取り入れてみてください。


参考文献

広告

-AI