小岳
2026-10-24
点 赞
0
热 度
7
评 论
0

DuckDB 聚合百万行电表数据:比 pandas 快多少

  1. 首页
  2. 数据分析
  3. DuckDB 聚合百万行电表数据:比 pandas 快多少

文章摘要

摸鱼小助手

DuckDB 聚合百万行电表数据:比 pandas 快多少

电表数据的特点是高密度、低频值:一台表一天 96 个点,一百台表一个月就是 28 万行。按日聚合、按表排行、按峰谷分段,这些查询在 pandas 里要整表读进内存再算,数据量一上来就很吃力。

DuckDB 走的是另一条路:列式存储加向量化执行,聚合时只读需要的列,且能直接对 pandas DataFrame 跑 SQL。它的定位不是替代 pandas,而是接管那些「数据量大、逻辑是 SQL」的活。

项目出处

项目内容
仓库https://github.com/duckdb/duckdb
协议MIT
安装pip install duckdb
运行要求Python 3.10+,本文在 3.14.4 上实测

安装与最小示例

pip install duckdb
import json, time
import numpy as np
import pandas as pd
import duckdb

np.random.seed(42)
n = 1_000_000
ts = pd.date_range("2025-01-01", periods=n, freq="1min")
val = np.clip(np.random.normal(320, 45, n), 60, None)
df = pd.DataFrame({"ts": ts, "meter": np.random.randint(1, 201, n), "kw": val})

t0 = time.perf_counter()
pd_res = df.assign(day=df.ts.dt.date).groupby("day").agg(total=("kw", "sum"), peak=("kw", "max"), rows=("kw", "size"))
pd_ms = (time.perf_counter() - t0) * 1000

t0 = time.perf_counter()
duck_res = duckdb.sql("""
    SELECT CAST(ts AS DATE) AS day, SUM(kw) AS total, MAX(kw) AS peak, COUNT(*) AS rows
    FROM df GROUP BY day ORDER BY day
""").fetchall()
duck_ms = (time.perf_counter() - t0) * 1000

t0 = time.perf_counter()
duck_top = duckdb.sql("SELECT meter, ROUND(AVG(kw),1) AS avg_kw FROM df GROUP BY meter ORDER BY avg_kw DESC LIMIT 3").fetchall()

实测结果

本机跑出来的关键数:

rows = 1000000;days = 695;pandas_ms = 323.1;duckdb_ms = 68.4;speedup = 4.72

完整输出:

{
 "rows": 1000000,
 "days": 695,
 "pandas_ms": 323.1,
 "duckdb_ms": 68.4,
 "speedup": 4.72,
 "top_ms": 7.9,
 "first_day": {
  "day": "2025-01-01",
  "total_kwh": 463381.9,
  "peak_kw": 493.4
 },
 "top_kwh_day": {
  "day": "2026-06-29",
  "total_kwh": 465705.2
 },
 "top_meters": [
  {
   "meter": 80,
   "avg_kw": 321.7
  },
  {
   "meter": 122,
   "avg_kw": 321.6
  },
  {
   "meter": 182,
   "avg_kw": 321.3
  }
 ]
}

DuckDB 实测(作者自绘)

DuckDB 快在两点:一是列式存储,聚合时只读需要的列,不像 DataFrame 那样整行搬运;二是向量化执行,一次处理一批数据而不是逐行循环。两者叠加,百万行级别的分组聚合差距就很明显。

选型的经验:数据量在十万行以内,pandas 与 DuckDB 的差距可以忽略,别为了性能引入新依赖;百万行以上且以聚合为主,DuckDB 的收益才开始明显;再往上到亿级,就该考虑落 Parquet 分区表,而不是继续加内存。

但它的优势有边界。数据量小、逻辑复杂、需要逐行处理的场景,pandas 仍然更顺手;跨表关联复杂、需要窗口函数与 SQL 语义时,DuckDB 才真正划算。本文这种按日聚合、按表排行的需求,正好落在它的强项区。

原理与设计取舍

本文用 100 万行模拟电表数据做对比:同一份日聚合,pandas 与 DuckDB 的耗时差出数倍,代码量却是 DuckDB 更短。这个差距在本地文件上已经明显,换成 Parquet 文件还会进一步拉大。

还有一个隐含收益:DuckDB 的 SQL 可以直接对着 pandas 对象跑,不需要改数据加载流程。这意味着迁移成本很低——先在关键查询上试,跑得通再逐步替换,不必一次性重写。

真正上生产时还有一步:把数据落成 Parquet,DuckDB 直接查文件,连加载都省了。本文用的是内存 DataFrame,这一步还能再快一截。

三个真实的坑

1. 依赖版本:先 pip install duckdb,别混用系统包,否则 import 报错很难查。 2. 数据口径:本文数值全部来自脚本输出,换机器或换版本会有细微差异,结论不能直接搬。 3. 只看总量不看分布:平均值背后可能是完全不同的分布,指标要成组看。

数据怎么读

rows = 1000000;days = 695;pandas_ms = 323.1;duckdb_ms = 68.4;speedup = 4.72 这几个数不是孤立的:它们分别对应规模、性能与开销。看绝对值容易被量级带偏,看相对关系才有判断力——比如两组耗时放在一起,差距比单个数字更说明问题。读者拿到自己的数据后,先算同样的指标,再和本文对比。

什么时候不该用它

任何工具都有边界:数据规模很小、逻辑一次性、维护成本高于收益时,硬上工具反而更慢。判断标准很简单——这件事一个月内会不会重复第二次,会,就值得写代码;不会,手算更快。

小结

DuckDB 在这个场景里的价值不是替代现成工具,而是把一件靠经验判断的事变成一段可重跑的代码。本文所有数据都能由 duckdb_aggregate.py 复现。

参考资料


本站站长,主角光环,怎么滴,你还想攻击你的站长吗,真是好big的狗胆

小岳

intp 逻辑家

站长

不具版权性
不具时效性

文章内容不具时效性。若文章内容有错误之处,请您批评指正。

目录

欢迎来到摸鱼干货栈,这里为您提供开源与摸鱼源码

8 文章数
6 分类数
0 评论数
13标签数
最近评论