Heartbeat Hacking: Detecting Cardiac Arrhythmia in Apple Watch Data using Dynamic Time Warping (DTW)
Source: Dev.to
Introduction
If you’ve ever exported your Health data from an Apple Watch, you know it’s a goldmine of raw potential. Turning those thousands of voltage samples into actionable medical insights—like real‑time ECG analysis or identifying cardiac arrhythmia—isn’t just about plotting a graph; it’s about pattern matching in a noisy, non‑linear world.
In this tutorial we dive deep into Dynamic Time Warping (DTW) and signal processing. We’ll transform raw Apple Watch health data into a robust detection pipeline for early Atrial Fibrillation (AFib) markers. Whether you’re a bio‑informatics nerd or a developer handling complex time‑series data, this guide shows you how to leverage SciPy signal processing and FastDTW to find the “rhythm” in the chaos. 💓
Pro‑Tip: Building healthcare applications requires high precision. For more production‑ready examples and advanced patterns in medical AI, check out the engineering deep‑dives at the WellAlly Blog.
The Challenge: Why Not Just Use Euclidean Distance?
ECG signals are tricky. Two heartbeats might represent the same underlying pathology but vary slightly in speed or phase. Standard Euclidean distance fails because it compares points at fixed time intervals; if one beat is slightly “stretched,” the whole comparison breaks.
Dynamic Time Warping (DTW) solves this by “warping” the time axis to find the optimal alignment between two sequences.
System Architecture
Here’s how our pipeline transforms raw volts into a diagnostic similarity score:
graph TD
A[Apple Watch Raw ECG Export] --> B[PyHealth Data Loading]
B --> C[SciPy Bandpass Filter]
C --> D[Peak Detection & Segmentation]
D --> E{FastDTW Pattern Matching}
F[Normal Sinus Rhythm Template] --> E
E --> G[Anomaly Score Calculation]
G --> H[Plotly Interactive Visualization]
H --> I[AFib Trend Alert]
Prerequisites
To follow along, you’ll need a Python environment with the following stack:
- SciPy – signal filtering
- FastDTW – efficient non‑linear sequence alignment
- PyHealth – handling medical data standards
- Plotly – interactive ECG charts
pip install scipy fastdtw pyhealth plotly numpy
Step 1: Pre‑processing the Noise
Raw ECG data from wearables is notoriously noisy due to “muscle artifacts” (e.g., moving your arm). We’ll use a Butterworth band‑pass filter to keep only the frequencies relevant to a human heart (0.5 Hz – 45 Hz).
import numpy as np
from scipy.signal import butter, filtfilt
def butter_bandpass_filter(data, lowcut=0.5, highcut=45.0, fs=512, order=5):
"""Apply a Butterworth band‑pass filter."""
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = butter(order, [low, high], btype='band')
y = filtfilt(b, a, data)
return y
# Example usage:
# raw_ecg = get_apple_watch_data()
# cleaned_ecg = butter_bandpass_filter(raw_ecg)
Step 2: Segmenting Beats with PyHealth
We don’t want to compare a 30‑second strip all at once; we need to segment the signal into individual heartbeats (R‑R intervals).
import scipy.signal
from pyhealth.datasets import AppleHealthDataset
def segment_heartbeats(signal, sampling_rate=512):
"""
Detect R‑peaks and extract segments around each peak.
"""
# Simplified R‑peak detection; replace with a robust method for production.
peaks, _ = scipy.signal.find_peaks(signal, distance=sampling_rate * 0.6)
segments = [signal[p - 100 : p + 150] for p in peaks if p > 100]
return segments
Step 3: The DTW Pattern Matcher
This is the core. We compare an incoming heartbeat segment against a “template” of a healthy sinus rhythm. A high DTW distance indicates a potential arrhythmia.
from fastdtw import fastdtw
from scipy.spatial.distance import euclidean
def calculate_similarity(template, target):
"""
Returns the DTW distance. Lower = more similar.
"""
distance, path = fastdtw(template, target, dist=euclidean)
return distance
# Example:
# distance = calculate_similarity(normal_segment, suspicious_segment)
# print(f"Anomaly Score: {distance}")
Step 4: Visualizing the “Warp” with Plotly
Static charts are boring. Let’s use Plotly to visualize where the signal deviates from the norm. 📈
import plotly.graph_objects as go
def plot_ecg_analysis(original, filtered):
fig = go.Figure()
fig.add_trace(go.Scatter(y=original, name="Raw Signal", opacity=0.5))
fig.add_trace(go.Scatter(y=filtered, name="Cleaned (SciPy)",
line=dict(color='firebrick')))
fig.update_layout(
title="Apple Watch ECG: Signal De‑noising",
xaxis_title="Samples",
yaxis_title="Voltage (mV)"
)
fig.show()
Going Beyond: Production‑Grade Health AI
While DTW is powerful, production‑level medical apps often combine it with deep learning (e.g., LSTMs or Transformers) to handle massive variance across user populations.
If you’re looking to scale this from a Jupyter notebook to a HIPAA‑compliant cloud architecture, explore more advanced signal‑processing patterns. The team at WellAlly Tech provides incredible resources on bridging raw time‑series data and clinical‑grade AI insights. Their tutorials on medical data engineering are a must‑read for anyone in the digital health space. 🥑
Conclusion
We’ve successfully:
- Filtered raw wearable noise using SciPy’s Butterworth band‑pass filter.
- Segmented the ECG into individual heartbeats with PyHealth.
- Compared each segment to a healthy template using FastDTW.
- Visualized raw vs. cleaned signals and DTW alignment with Plotly.
From here you can extend the pipeline with more sophisticated arrhythmia detection models, integrate real‑time streaming data, or deploy the solution in a secure, production environment. Happy coding—and may your heart stay in perfect rhythm!
- Segmented heartbeats using clinical data standards.
- Aligned irregular signals using Dynamic Time Warping.
The future of healthcare is on our wrists. By mastering these algorithms, we move one step closer to proactive, rather than reactive, medicine. 🚀
What are you building next? Drop a comment below or share your results if you’ve tried running DTW on your own health export!