摆脱臃肿:使用 DuckDB 与 Parquet 构建高性能健康数据湖

发布: (2026年4月9日 GMT+8 08:10)
3 分钟阅读
原文: Dev.to

Source: Dev.to

架构概览:从 XML 混乱到 SQL 极速

目标是将慢速、占用大量内存的 XML 结构迁移到面向分析查询优化的列式、压缩存储格式。

graph TD
    A[Apple Health Export XML] -->|Python Streaming Parser| B(Raw Data Extraction)
    B -->|Schema Mapping| C[Apache Parquet Files]
    C -->|Zero‑Copy Load| D{DuckDB Engine}
    D -->|Sub‑second SQL| E[Evidence.dev Dashboard]
    D -->|Advanced Analytics| F[Pandas/Polars]

    style D fill:#fff2cc,stroke:#d6b656,stroke-width:2px
    style C fill:#dae8fc,stroke:#6c8ebf,stroke-width:2px

前置条件

  • Python 3.9+
  • DuckDB – “面向分析的 SQLite”
  • Pandas / PyArrow – 用于 Parquet 处理
  • 你的 export.xml(或示例文件)

步骤 1:解析巨兽(XML → Parquet)

标准的 xml.etree.ElementTree 在文件大于 2 GB 时会耗尽内存。这里我们使用 流式解析器,仅提取 HeartRate 记录。

import xml.etree.ElementTree as ET
import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq

def parse_health_data(xml_path: str, output_parquet: str):
    data = []
    # iterparse 处理大文件而无需一次性加载全部内容
    context = ET.iterparse(xml_path, events=('end',))

    for event, elem in context:
        if elem.tag == 'Record' and elem.get('type') == 'HKQuantityTypeIdentifierHeartRate':
            data.append({
                'timestamp': elem.get('startDate'),
                'value': float(elem.get('value')),
                'unit': elem.get('unit')
            })

        # 释放内存
        elem.clear()

        # 批量写入以保持低内存占用
        if len(data) > 100_000:
            save_batch(data, output_parquet)
            data = []

    # 写入剩余的行
    if data:
        save_batch(data, output_parquet)

def save_batch(data: list[dict], path: str):
    df = pd.DataFrame(data)
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    table = pa.Table.from_pandas(df)
    pq.write_to_dataset(table, root_path=path, partition_cols=None)

# 示例用法:
# parse_health_data('export.xml', 'heart_rate_lake')

步骤 2:DuckDB 的魔法

数据一旦以 Parquet 形式存储,就会按列组织。DuckDB 能在不将整个文件加载到内存的情况下直接查询它。

import duckdb

# 连接到持久化的 DuckDB 实例(或使用 ':memory:' 进行内存模式)
con = duckdb.connect(database='health_analytics.db')

# 在 Parquet 文件上直接创建视图(零拷贝!)
con.execute("""
    CREATE VIEW heart_rates AS
    SELECT *
    FROM read_parquet('heart_rate_lake/*.parquet')
""")

# 示例分析查询:每小时的静息心率趋势
res = con.execute("""
    SELECT
        date_trunc('hour', timestamp) AS hour,
        avg(value) AS avg_bpm,
        max(value) AS max_bpm
    FROM heart_rates
    WHERE value 
""")

结论

通过抛弃“臃肿”格式,采用现代数据栈(Python + Parquet + DuckDB),你可以把健康数据从静态备份转变为动态研究工具。恢复趋势、睡眠质量和心血管健康等洞察能够在毫秒级别被发现。

接下来该做什么?

  • yearmonth 对 Parquet 文件进行分区,以获得更快的扫描速度。
  • 集成 Evidence.dev,打造美观的代码驱动仪表盘。

你在分析自己的健康数据吗?在评论区告诉我你发现了哪些洞见吧!

0 浏览
Back to Blog

相关文章

阅读更多 »

了解 Python Selenium 架构

Python Selenium Architecture - Python 测试脚本 – 编写自动化代码,指示执行诸如打开网站、点击按钮或输入文本等操作……