DataStoreには、出力をpandas互換向けに整形するか、Raw SQLのパフォーマンス向けに最適化するかを制御する2つの互換性モードがあります。
概要
| Mode | compat_mode value |
Description |
|---|---|---|
| Pandas (デフォルト) | "pandas" |
pandas の動作との完全な互換性。行順を保持し、MultiIndex、set_index、dtype の補正、安定ソート時のタイブレーク、-If/isNaN ラッパーに対応します。 |
| Performance | "performance" |
SQLファーストで実行します。pandas 互換性のためのオーバーヘッドをすべて排除。最大のスループットを実現しますが、結果の構造が pandas と異なる場合があります。 |
パフォーマンスモードで無効化される機能
| Overhead | Pandas mode behavior | Performance mode behavior |
|---|---|---|
| 行順序の保持 | _row_id の挿入、rowNumberInAllBlocks()、__orig_row_num__ のサブクエリ |
無効 — 行順は保証されません |
| 安定ソートのタイブレーク | rowNumberInAllBlocks() ASC を ORDER BY に追加 |
無効 — 同順位の並び順は任意になる場合があります |
| Parquet の preserve_order | input_format_parquet_preserve_order=1 |
無効 — Parquet の並列読み取りが可能になります |
| GroupBy の自動 ORDER BY | ORDER BY group_key を追加 (pandas のデフォルト sort=True) |
無効 — グループは任意の順序で返されます |
| GroupBy の dropna WHERE | WHERE key IS NOT NULL を追加 (pandas のデフォルト dropna=True) |
無効 — NULL のグループも含まれます |
| GroupBy の set_index | グループキーをインデックスとして設定 | 無効 — グループキーはカラムのままになります |
| MultiIndex カラム | agg({'col': ['sum','mean']}) は MultiIndex カラムを返します |
無効 — フラットなカラム名 (col_sum, col_mean) になります |
-If/isNaN ラッパー |
skipna のために sumIf(col, NOT isNaN(col)) を使用 |
無効 — 単純な sum(col) (ClickHouse はネイティブで NULL をスキップ) |
count に対する toInt64 |
pandas の int64 に合わせるために toInt64(count()) を使用 |
無効 — ネイティブ SQL の Dtype が返されます |
全 NaN の sum に対する fillna(0) |
すべて NaN の sum は 0 を返す (pandas の動作) | 無効 — NULL を返します |
| Dtype 補正 | abs() の unsigned→signed など |
無効 — ネイティブ SQL の Dtype |
| インデックスの保持 | SQL 実行後に元のインデックスを復元 | 無効 |
first()/last() |
argMin/argMax(col, rowNumberInAllBlocks()) |
any(col) / anyLast(col) — 高速ですが非決定論的です |
| 単一 SQL 集約 | ColumnExpr の groupby は中間 DataFrame をマテリアライズします | 遅延実行の処理チェーンに LazyGroupByAgg を挿入 — 単一の SQL クエリ |
パフォーマンスモードを有効にする
configオブジェクトを使用する
from chdb.datastore.config import config
# Enable performance mode
config.use_performance_mode()
# Back to pandas compatibility
config.use_pandas_compat()
# Check current mode
print(config.compat_mode) # 'pandas' or 'performance'モジュールレベルの関数を使用する
from chdb.datastore.config import set_compat_mode, CompatMode, is_performance_mode
# Enable performance mode
set_compat_mode(CompatMode.PERFORMANCE)
# Check
print(is_performance_mode()) # True
# Back to default
set_compat_mode(CompatMode.PANDAS)便利なインポートの利用
from chdb import use_performance_mode, use_pandas_compat
use_performance_mode()
# ... high-performance operations ...
use_pandas_compat()パフォーマンスモードを使うべき場面
次のような場合は、パフォーマンスモードを使用してください。
- 大規模なデータセット (数十万〜数百万行) を処理する場合
- 集約処理の多いワークロード (groupby、sum、mean、count) を実行する場合
- 行順が重要でない場合 (例: 集計結果、レポート、ダッシュボード)
- SQL のスループットを最大化し、オーバーヘッドを最小限に抑えたい場合
- メモリ使用量が気になる場合 (Parquet の並列読み取り、中間 DataFrame なし)
次のような場合は、pandasモードのままにしてください。
- pandas と完全に同じ挙動 (行順、MultiIndex、dtypes) が必要な場合
first()/last()が実際の最初/最後の行を返すことに依存している場合- 行順に依存する
shift()、diff()、cumsum()を使用する場合 - DataStore の出力を pandas と比較するテストを作成している場合
挙動の違い
行の順序
パフォーマンスモードでは、どの操作でも行の順序は保証されません。これには、次のものが含まれます。
- Filter の結果
- GroupBy の集計結果
- 明示的に
sort_values()を指定しないhead()/tail() first()/last()の集計結果
順序どおりの結果が必要な場合は、明示的に sort_values() を追加してください。
config.use_performance_mode()
ds = pd.read_csv("data.csv")
# Unordered (fast)
result = ds.groupby("region")["revenue"].sum()
# Ordered (still fast, just adds ORDER BY)
result = ds.groupby("region")["revenue"].sum().sort_values()GroupBy の結果
| 観点 | Pandasモード | パフォーマンスモード |
|---|---|---|
| グループキーの位置 | インデックス (set_index経由) |
通常のカラム |
| グループの順序 | キーでソート (デフォルト) | 任意の順序 |
| NULL のグループ | 除外 (デフォルトはdropna=True) |
含まれる |
| カラムのフォーマット | 複数集約では MultiIndex | フラットな名前 (col_func) |
first()/last() |
決定論的 (行の順序) | 非決定論的 (any()/anyLast()) |
集計
config.use_performance_mode()
# Sum of all-NaN group returns NULL (not 0)
# Count returns native uint64 (not forced int64)
# No -If wrappers: sum() instead of sumIf()
result = ds.groupby("cat")["val"].sum()単一SQLでの実行
パフォーマンスモードでは、ColumnExpr の groupby 集計 (例: ds[condition].groupby('col')['val'].sum()) は、pandasモードで使われる 2 段階の処理ではなく、単一のSQLクエリとして実行されます。
config.use_performance_mode()
# Pandas mode: two SQL queries (filter → materialize → groupby)
# Performance mode: one SQL query (WHERE + GROUP BY in same query)
result = ds[ds["rating"] > 3.5].groupby("category")["revenue"].sum()
# Generated SQL (single query):
# SELECT category, sum(revenue) FROM data WHERE rating > 3.5 GROUP BY categoryこれにより、中間DataFrameを実体化する必要がなくなり、メモリ使用量と実行時間を大幅に削減できます。
実行エンジンとの比較
パフォーマンスモード (compat_mode) と実行エンジン (execution_engine) は、それぞれ独立した設定軸です。
| Config | Controls | Values |
|---|---|---|
execution_engine |
どのエンジンで計算を実行するか | auto, chdb, pandas |
compat_mode |
pandas 互換性のために出力を整形するかどうか | pandas, performance |
compat_mode='performance' を設定すると、execution_engine='chdb' も自動的に設定されます。これは、パフォーマンスモードが SQL 実行向けに設計されているためです。
from chdb.datastore.config import config
# These are independent
config.use_chdb() # Force chDB engine, keep pandas compat
config.use_performance_mode() # Force chDB + remove pandas overheadパフォーマンスモードでのテスト
パフォーマンスモード向けのテストを作成する際は、結果の行順やデータ構造のフォーマットが pandas と異なる場合があります。次の方法を使用してください。
ソートして比較 (集計、フィルター)
# Sort both sides by the same columns before comparing
ds_result = ds.groupby("cat")["val"].sum()
pd_result = pd_df.groupby("cat")["val"].sum()
ds_sorted = ds_result.sort_index()
pd_sorted = pd_result.sort_index()
np.testing.assert_array_equal(ds_sorted.values, pd_sorted.values)値範囲チェック (先頭/末尾)
# first() with any() returns an arbitrary element from the group
result = ds.groupby("cat")["val"].first()
for group_key in groups:
assert result.loc[group_key] in group_values[group_key]スキーマと件数 (ORDER BY なしの LIMIT)
# head() without sort_values: row set is non-deterministic
result = ds.head(5)
assert len(result) == 5
assert set(result.columns) == expected_columnsベストプラクティス
1. スクリプトの冒頭で有効化する
from chdb.datastore.config import config
config.use_performance_mode()
# All subsequent operations benefit
ds = pd.read_parquet("data.parquet")
result = ds[ds["amount"] > 100].groupby("region")["amount"].sum()2. 順序が重要な場合は明示的にソートを指定する
# For display or downstream processing that expects order
result = (ds
.groupby("region")["revenue"].sum()
.sort_values(ascending=False)
)3. バッチ/ETLワークロードで使用する
config.use_performance_mode()
# ETL pipeline — order doesn't matter, throughput does
summary = (ds
.filter(ds["date"] >= "2024-01-01")
.groupby(["region", "product"])
.agg({"revenue": "sum", "quantity": "sum", "rating": "mean"})
)
summary.to_df().to_parquet("summary.parquet")4. セッション内でモードを切り替える
# Performance mode for heavy computation
config.use_performance_mode()
aggregated = ds.groupby("cat")["val"].sum()
# Back to pandas mode for exact-match comparison
config.use_pandas_compat()
detailed = ds[ds["val"] > 100].head(10)- 実行エンジン — 実行エンジンの選択 (auto/chdb/pandas)
- Performance Guide — 一般的な最適化のポイント
- pandas との主な違い — 動作の違い