PyCon JP 2026 · Hiroshima
chDB

Rediscovering
DataFrames

100× Analytics Without Leaving Pandas
sai·hak·ken — rediscovery
Auxten Wang · ClickHouse · Creator of chDB@auxten
ABOUT · 自己紹介 jikoshōkai02 / 19
Auxten Wang

Auxten Wang

@auxten · auxten.com

Technical Director @ ClickHouse
Creator of chDB — acquired by ClickHouse in 2024
Builds embedded databases & data tooling for Python
ex Shopee principal engineer
chDB
ClickHouse
PANDAS · ai03 / 19

Everyone's first data tool

import pandas as pd
df = pd.read_csv("sales.csv")
df[df.amount > 100] \
  .groupby("region").amount.sum()
In every tutorial, every notebook, every team
Muscle memory for millions of engineers
The DataFrame dialect LLMs write best
This API took 15 years
to learn everywhere.
The pandas API is an asset. The engine underneath is the problem.
THE CEILING · 限界 genkai04 / 19

Then your data grew.

Memory
Error
a 10 GB DataFrame chokes a 16 GB laptop
1 / 16
cores doing the work while fifteen watch
> query
reading from S3 takes longer than the analysis itself
Your code didn't hit the ceiling. The execution model did.
WHY · 原因 gen'in05 / 19

The ceiling has three walls

EAGER

every step in the chain materializes a full intermediate copy

SINGLE-CORE

the GIL-era design: one thread computes, the rest idle

ALL-IN-RAM

the whole table decompresses into memory before row one is read
33 MB
Parquet on disk
1.5 GB
as a pandas DataFrame
×45 blow-up,
measured on my laptop.
Not fixable with another .apply() trick. It's the engine.
THE EXITS · 出口 deguchi06 / 19

The usual exits all cost the same thing

Spark

a cluster to babysit, for one notebook

Polars

fast — but a new API for the whole team

DuckDB

great engine — but now you write SQL

Rewrite

months of migration, retraining, re-testing
Every exit asks you to leave the API. What if you didn't have to?
THE FLIP · 転換 tenkan07 / 19

Change one line

- import pandas as pd + import chdb.datastore as pd
df  = pd.read_parquet("events.parquet")
top = (df[df.status == 200]
        .groupby("user_id").amount.sum()
        .sort_values(ascending=False).head(10))

Same code.
Different engine.
The rest of the file doesn't know. — one line.
CHDB08 / 19

chDB: a rocket engine on a bicycle

A rocket engine strapped to a bicycle
# pip install chdb
import chdb
chdb.query("SELECT count()
  FROM file('orders.parquet')")
# schema inferred · nothing loaded
The ClickHouse engine, in-process — like SQLite
No server, no config, no connection string
Columnar · vectorized · every core

Same engine that runs analytics at Anthropic, Cursor, Vercel, Cloudflare.

HOW · 仕組み shikumi09 / 19

Layered, not mixed — o-ko-no-mi-ya-ki

sōsu · sauce — your pandas calls

df[df.x > 1].groupby(…).sum() — untouched

men · noodles — the lazy op chain

every call recorded, nothing executed

kyabetsu · cabbage — one SQL plan

the whole chain compiles to a single query

kiji · batter — the chDB engine

columnar · vectorized · all cores

teppan · teppan — your Python process

everything cooks in-process, no server
len() · print() · for — the kote flip: everything cooks at once.
ENGINE · 秘密 himitsu10 / 19

The engine knocks down all three walls

A SQL COMPILER

an internal compiler rewrites your whole pandas chain into one SQL query for the ClickHouse engine
pandas → SQL → ClickHouse

EVERY CORE

pandas computes on 1 of 14 cores — the engine fills them all

STREAMING

top-10 of 10M rows keeps a 10-row heap — never the sorted table
RAM: 100% → 33%
…and each core is vectorized
one SIMD instruction processes a whole block of values
1 instruction
per tick
that's the ×51.7 sort
and the 33% RAM.
Not a faster pandas — a different machine underneath.
BENCHMARK · 勝負 shōbu11 / 19

Where the 100× lives

chDB
×1.35
DuckDB
×1.40
Polars
×9.99
pandas
×71.18
Worst single query: pandas 94.3 s vs chDB 0.037 s×2,550

ClickBench · DataFrame category · 43.77 GiB · same c6a.metal machine · relative runtime, lower is better

Same DataFrame workload. ×71 less waiting — before any tuning.
BENCHMARK · 勝負 shōbu12 / 19

10M rows, 16 everyday ops — faster on 14 of 16

DataStore faster (14)    pandas faster (2)    thin bar + blue % = the RAM we use vs pandas (33% = ⅓)   ·   linear scale
read parquet → answer · best of 2 · outputs identical 16/16   ·   peak RSS median: pandas 1,059 MB → DataStore 440 MB (42%)
Sort (single col)×51.733%
Sort (multi col)×39.034%
Mixed Filter+Sort×35.231%
Filter+Select+Sort×21.736%
Chain 5 filters×12.931%
Head / limit×6.847%
Filter+Sort+Head×6.647%
Multi-filter (4x)×4.937%
Filter+Sort+Select×4.241%
Filter (multi AND)×4.233%
Ultra-complex chain×4.036%
Complex pipeline×4.029%
Filter (single)×3.337%
GroupBy agg ×3×2.840%
Mask (replace)196%×8.5
Where (replace)199%×6.7
実測
thin blue bars:
⅓–½ the RAM
on every op we win.
Faster on 14 of 16 — bulk value-replace stays with pandas.
REACH · 到達 tōtatsu13 / 19

Your data lives on many islands

pd.read_parquet("https://…/house_0.parquet"# the lake pd.read_csv("crm_exports.tar :: users_*.csv"# archives pd.read_sql("SELECT …", "postgresql://prod")  # live DBs

measured this week, this laptop

2,772,030 rows on S3 — counted in 3.3 s, no download
parquet ⋈ CSVs-inside-a-tar ⋈ groupby — one chain
実測
70 table functions · 72 input formats — counted from the engine
Like the Miyajima torii: a gateway standing in the water. The data never moves.
PERFORMANCE · 性能 seinō14 / 19

It reads what the query needs — not the file

house_0.parquet · 33 MB · 2.77M rows · 14 columns    ▼ HTTP Range reads
type
price
date
town
street
…9 more columns
footer
fetched for avg(price) by type   ■ skipped   ■ footer = schema + stats, read first
3.3 s
len() on the remote file — footer + counts only, no download
2 / 14
columns fetched for the aggregation — ranged reads, no full scan
344 vs 975 MB
peak RAM, chDB streaming vs pandas on the same file
Ranged reads: you pay for the columns you touch — not the file.
MEMORY · 記憶 kioku15 / 19

Agent memory today: three databases

YOUR
AGENT
"what do I
remember?"

Vector DB

semantic recall — "which memories mean this?"

Metadata store

filters — "whose memory? which project?"

Recency cache

freshness — "what still matters today?"
3 systems · 3 hops · 3 bills
Every recall pays three round-trips — before the LLM starts.
MEMORY · 記憶 kioku16 / 19

One query does all three

SELECT text FROM memory
WHERE user_id = 'u1'  -- metadata   AND ts > now() - INTERVAL 30 DAY  -- recency ORDER BY cosineDistance(emb, q_vec)  -- vector LIMIT 3

top-3, measured — 10.6 ms

prefers dark mode           0.001
okonomiyaki: hiroshima-style  0.896
python 3.13 user            1.103
実測
One table — embeddings next to their rows
In-process — zero network hops
No servers — nothing to deploy or babysit
One table, in-process. No servers.
TRENDS · 傾向 keikō17 / 19

The gap grows — with size, and with depth

DATA SIZE ↑ — same op
×0.5
×1.4
×28
×52.7
100K rows1M10M100M
ClickBench*
CHAIN DEPTH ↑ — 10M rows
×3.3
×4.9
×12.9
×21.7
×35.2
1 op4 filters5 chained+select
+sort
+multi-
sort
実測

same laptop, shared linear scale · *100M point: published ClickBench DataFrame total (100M rows / 43.77 GiB), benchmark.clickhouse.com

Below ~1M rows keep pandas. Beyond it, the gap only widens.
CLOSE · まとめ matome18 / 19

Three takeaways

01

The pandas API is the asset. Fifteen years of muscle memory — keep it, swap the engine.

02

The gap grows with your data. More rows, deeper chains — the advantage compounds where pandas ends.

03

Your agent's memory is one query. Vector + metadata + recency — one table, in-process, no servers.

pandas is the language. chDB is the engine. You never had to leave.
← arigatō gozaimashita

Thank you.

pip install chdb
github.com/chdb-io/chdb
clickhouse.com/docs/chdb
auxten.com — slides & verified demos
Q&A
Auxten Wang · ClickHouse · Creator of chDB@auxten