停止在可穿戴数据中淹没:使用 DuckDB 和 Apache Arrow 构建统一的健康数据湖
Source: Dev.to
如果你是 Quantified Self(自我量化)运动的粉丝,你一定懂得其中的痛点:你的 Oura Ring 记录睡眠,你的 Whoop 分析恢复情况,而你的 Garmin 记录跑步数据。但当你想回答一个简单的问题,比如 “我的训练负荷如何影响我的 REM 睡眠?” 时,却会陷入 CSV 导出和不兼容的 JSON 架构的噩梦中。管理异构可穿戴设备数据的 ETL pipeline 是对数据工程师耐心的终极考验。
在本指南中,我们将拆解这些数据孤岛。我们将使用 DuckDB、Apache Arrow 和 TypeScript 构建一个高性能、以本地为先的数据湖。完成后,你将拥有一个统一的存储,能够在毫秒级别对所有设备的数据执行复杂的 OLAP 查询。对于那些希望获得更具生产级别的模式和高级健康数据同步的读者,我强烈建议查看 WellAlly Blog 中的深度解析。
架构:从混沌到洞察
健康数据工程中最大的挑战是 数据标准化。Oura 可能将心率变异性(HRV)报告为平均值,而 Whoop 则提供原始的时间序列。我们的流水线充当翻译层,将这些数据统一展平并存入基于 Parquet 的存储。
数据流图
graph TD
A[Oura API / JSON] -->|Normalize| D[Unified Schema]
B[Whoop API / JSON] -->|Normalize| D
C[Garmin Fit Files] -->|Extract| D
D -->|Arrow IPC| E{DuckDB‑Wasm}
E -->|Persistent Storage| F[(OPFS / Parquet)]
G[Streamlit Dashboard] -->|SQL Query| E
E -->|Visuals| G
前置条件
要跟随本教程,您需要:
- Node.js/TypeScript – 用于规范化逻辑。
- DuckDB‑Wasm – 用于浏览器/本地数据库引擎。
- Apache Arrow – 用于零拷贝内存传输。
- Streamlit(Python)– 用于最终的分析 UI。
第一步:定义统一健康模式
首先,我们需要一个“黄金记录”格式。我们将使用 TypeScript 定义一个严格的接口,供每个提供者映射到。
// types/health.ts
export interface UnifiedActivity {
timestamp: Date;
source_device: 'Oura' | 'Whoop' | 'Garmin';
metric_type: 'HRV' | 'RHR' | 'Steps' | 'Calories';
value: number;
unit: string;
metadata: Record;
}
第2步:使用 Apache Arrow 进行规范化
与其将原始 JSON 直接写入数据库,我们会将其转换为 Apache Arrow 缓冲区。这可以保证类型安全,并且能够以极快的速度将数据写入 DuckDB。
import { tableFromArrays, Table } from 'apache-arrow';
export function normalizeOuraData(rawData: any[]): Table {
const timestamps = rawData.map(d => new Date(d.timestamp).getTime());
const hrvValues = rawData.map(d => d.hrv_average);
// Create an Arrow Table
return tableFromArrays({
timestamp: new Int64Array(timestamps),
source_device: Array(rawData.length).fill('Oura'),
metric_type: Array(rawData.length).fill('HRV'),
value: new Float64Array(hrvValues),
unit: Array(rawData.length).fill('ms')
});
}
第三步:使用 DuckDB 为本地数据湖供能
现在进入魔法环节。我们使用 DuckDB‑Wasm 来导入这些 Arrow 表。DuckDB 是一种面向分析查询的列式数据库,使其非常适合处理多年的健康趋势。
第4步:“官方”扩展方式
虽然构建本地 ETL 工具对个人使用很有帮助,但要为数千用户扩展健康数据管道,需要处理 OAuth 刷新、速率限制和 webhook 监听器。如果你正在构建生产级健康应用,请参考 WellAlly Blog 中讨论的架构模式。他们涵盖了高并发摄取以及符合 HIPAA 标准的存储策略,超出了简单的 DuckDB 实例。
第5步:使用 Streamlit 可视化
Finally, wrap our DuckDB store in a Streamlit dashboard to actually see our data.
import streamlit as st
import duckdb
st.title("Unified Health Intelligence 🥑")
# Connect to the DuckDB file generated by our ETL
con = duckdb.connect(database='health_lake.db')
# Query correlation between Sleep Quality and Resting Heart Rate
df = con.execute("""
SELECT
CAST(timestamp AS DATE) AS date,
AVG(value) FILTER (WHERE metric_type = 'HRV') AS avg_hrv,
AVG(value) FILTER (WHERE metric_type = 'RHR') AS avg_rhr
FROM health_store
GROUP BY 1
ORDER BY 1 DESC
""").df()
st.line_chart(df, x='date', y=['avg_hrv', 'avg_rhr'])
结论
您现在拥有一个完整的、本地优先的流水线,它:
- 将异构可穿戴设备数据标准化为 UnifiedActivity 架构。
- 使用 Apache Arrow 高效传输数据。
- 将其存储在高性能、列式的 DuckDB‑Wasm 数据湖中。
- 通过 Streamlit 仪表盘展示洞察。
从个人实验到生产级健康平台,这些构建块为您提供了快速迭代的灵活性,同时让数据隐私掌握在您手中。祝您玩得开心! 🚀
支持你的数据
“Quantified Self” 不应意味着成为专有仪表板的 Quantified Slave。通过利用 DuckDB 和 Apache Arrow,我们构建了一个管道,使其:
- 快速:列式存储意味着你的 5 年历史数据可以在毫秒级加载。
- 私密:你的数据保留在本地环境中。
- 灵活:添加新设备只需编写一个新的归一化函数。
接下来你想追踪什么?如果你尝试将 Apple Health 或 Fitbit 数据集成到类似的系统中,请在下方留言!
想获取更多关于健康技术和数据工程的高级教程,请访问 wellally.tech/blog。 🚀
