PyCon JP 2026 · 広島 Hiroshima
Rediscovering
DataFrames
100× Analytics Without Leaving Pandas
再発見
sai·hak·ken — rediscovery
ABOUT · 自己紹介 jikoshōkai02 / 19
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
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
→
1.5 GBas 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
非常口 hijōguchi — emergency exit
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
# 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
コテ kote
ソース 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 vectorizedone SIMD instruction processes a whole block of values
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
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
三本の矢 → 一本
sanbon-no-ya → ippon
three arrows, one query
One table, in-process. No servers.
TRENDS · 傾向 keikō17 / 19
The gap grows — with size, and with depth
DATA SIZE ↑ — same op
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
01The pandas API is the asset. Fifteen years of muscle memory — keep it, swap the engine.
02The gap grows with your data. More rows, deeper chains — the advantage compounds where pandas ends.
03Your 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