Atrial Fibrillation และ Atrial Flutter คืออะไร
Atrial Fibrillation (AF หรือ AFib) เป็นภาวะหัวใจเต้นผิดจังหวะ (arrhythmia) ที่พบบ่อยที่สุด เกิดจากการที่ห้องบนของหัวใจ (atria) ส่งสัญญาณไฟฟ้าที่ผิดปกติแบบสุ่มและรวดเร็ว ทำให้หัวใจห้องบนสั่นพลิ้ว (fibrillation) แทนที่จะบีบตัวอย่างเป็นจังหวะ ส่งผลให้หัวใจสูบฉีดเลือดไม่มีประสิทธิภาพ
Atrial Flutter เป็นภาวะที่คล้ายกันแต่มีรูปแบบที่เป็นระเบียบมากกว่า หัวใจห้องบนเต้นเร็วประมาณ 250-350 ครั้งต่อนาที แต่เป็นจังหวะสม่ำเสมอ สัญญาณไฟฟ้าเดินทางเป็นวงจร (re-entrant circuit) ส่วน AF มีอัตราการเต้นของห้องบน 350-600 ครั้งต่อนาทีแบบไม่เป็นจังหวะ
Unspecified หมายถึงกรณีที่ไม่สามารถระบุประเภทย่อยได้ชัดเจน ซึ่งอาจเกิดจากข้อมูลทางคลินิกไม่เพียงพอ หรือผู้ป่วยมีทั้ง AF และ Flutter สลับกัน รหัส ICD-10 ที่ใช้คือ I48.91 สำหรับ Unspecified atrial fibrillation และ I48.92 สำหรับ Unspecified atrial flutter
AF เพิ่มความเสี่ยงต่อโรคหลอดเลือดสมอง (stroke) ถึง 5 เท่าเพราะเลือดค้างในห้องบนอาจเกิดลิ่มเลือด ผู้ป่วย AF ส่วนใหญ่ต้องใช้ยาป้องกันลิ่มเลือด (anticoagulant) เช่น Warfarin หรือ NOACs ร่วมกับการควบคุมอัตราการเต้นของหัวใจ
สาเหตุ อาการ และการวินิจฉัย
ปัจจัยเสี่ยงและวิธีการวินิจฉัย AF/AFL
# สาเหตุและปัจจัยเสี่ยงของ Atrial Fibrillation
#
# Cardiac Causes:
# - ความดันโลหิตสูง (Hypertension) — ปัจจัยเสี่ยงอันดับ 1
# - โรคลิ้นหัวใจ (Valvular heart disease)
# - หัวใจวาย (Heart failure)
# - โรคหลอดเลือดหัวใจ (Coronary artery disease)
# - หลังผ่าตัดหัวใจ (Post-cardiac surgery)
# - Cardiomyopathy
#
# Non-Cardiac Causes:
# - ต่อมไทรอยด์ทำงานเกิน (Hyperthyroidism)
# - โรคปอดอุดกั้นเรื้อรัง (COPD)
# - ดื่มแอลกอฮอล์มากเกินไป (Holiday Heart Syndrome)
# - ภาวะหยุดหายใจขณะหลับ (Sleep apnea)
# - โรคือ้วน (Obesity)
# - เบาหวาน (Diabetes)
# - อายุมากกว่า 65 ปี
#
# อาการ:
# - ใจสั่น (Palpitations)
# - หายใจลำบาก (Dyspnea)
# - เวียนศีรษะ (Dizziness)
# - เหนื่อยง่าย (Fatigue)
# - เจ็บหน้าอก (Chest pain)
# - ผู้ป่วยบางรายไม่มีอาการเลย (Asymptomatic)
#
# การจำแนกประเภท AF:
# 1. Paroxysmal AF — เกิดเป็นครั้งคราว หายเองภายใน 7 วัน
# 2. Persistent AF — นานกว่า 7 วัน ต้องรักษาจึงจะหาย
# 3. Long-standing Persistent — นานกว่า 12 เดือน
# 4. Permanent AF — ยอมรับว่ารักษาไม่หายแล้ว
# CHA₂DS₂-VASc Score (ประเมินความเสี่ยง Stroke)
# C — Congestive heart failure (+1)
# H — Hypertension (+1)
# A₂ — Age ≥75 (+2)
# D — Diabetes mellitus (+1)
# S₂ — Stroke/TIA history (+2)
# V — Vascular disease (+1)
# A — Age 65-74 (+1)
# Sc — Sex category (female +1)
#
# Score 0: ไม่ต้องใช้ยาป้องกันลิ่มเลือด
# Score 1: พิจารณาใช้ยา
# Score ≥2: ควรใช้ยาป้องกันลิ่มเลือด
รหัส ICD-10 และการจำแนกประเภท
รหัส ICD-10 ที่เกี่ยวข้องกับ AF และ AFL
# ICD-10 Codes สำหรับ Atrial Fibrillation and Flutter
#
# I48 — Atrial fibrillation and flutter
#
# I48.0 — Paroxysmal atrial fibrillation
# AF ที่เกิดเป็นครั้งคราวและหายเอง
#
# I48.1 — Persistent atrial fibrillation
# AF ที่คงอยู่นานกว่า 7 วัน
#
# I48.11 — Long-standing persistent atrial fibrillation
# AF ที่คงอยู่นานกว่า 12 เดือน
#
# I48.19 — Other persistent atrial fibrillation
#
# I48.2 — Chronic atrial fibrillation
# AF แบบเรื้อรัง (Permanent)
#
# I48.20 — Chronic atrial fibrillation, unspecified
# I48.21 — Permanent atrial fibrillation
#
# I48.3 — Typical atrial flutter
# Flutter แบบ counterclockwise (common type)
#
# I48.4 — Atypical atrial flutter
# Flutter แบบอื่นๆ
#
# I48.91 — Unspecified atrial fibrillation
# AF ที่ไม่ระบุประเภท
#
# I48.92 — Unspecified atrial flutter
# Flutter ที่ไม่ระบุประเภท
# Python script สำหรับคำนวณ CHA2DS2-VASc Score
def cha2ds2_vasc(chf, hypertension, age, diabetes, stroke_tia, vascular, female):
score = 0
score += 1 if chf else 0
score += 1 if hypertension else 0
score += 2 if age >= 75 else (1 if age >= 65 else 0)
score += 1 if diabetes else 0
score += 2 if stroke_tia else 0
score += 1 if vascular else 0
score += 1 if female else 0
risk = {0: "Low (0.2%/yr)", 1: "Low-Moderate (0.6%/yr)",
2: "Moderate (2.2%/yr)", 3: "Moderate-High (3.2%/yr)",
4: "High (4.8%/yr)", 5: "High (7.2%/yr)",
6: "Very High (9.7%/yr)", 7: "Very High (11.2%/yr)",
8: "Very High (10.8%/yr)", 9: "Very High (12.2%/yr)"}
recommendation = "No anticoagulation" if score == 0 else \
"Consider anticoagulation" if score == 1 else \
"Anticoagulation recommended"
return {
"score": score,
"stroke_risk": risk.get(score, "Very High"),
"recommendation": recommendation
}
# ตัวอย่าง: ชาย อายุ 72 มีความดันสูงและเบาหวาน
result = cha2ds2_vasc(
chf=False, hypertension=True, age=72,
diabetes=True, stroke_tia=False, vascular=False, female=False
)
print(f"Score: {result['score']}")
print(f"Risk: {result['stroke_risk']}")
print(f"Recommendation: {result['recommendation']}")
วิเคราะห์ ECG Signal ด้วย Python
ใช้ Python วิเคราะห์สัญญาณ ECG เพื่อตรวจจับ AF
#!/usr/bin/env python3
# ecg_analysis.py — วิเคราะห์ ECG สำหรับตรวจจับ AF
import numpy as np
from scipy import signal
from scipy.stats import entropy
def load_ecg(filepath):
"""โหลด ECG data (MIT-BIH format)"""
import wfdb
record = wfdb.rdrecord(filepath)
ecg_signal = record.p_signal[:, 0]
fs = record.fs # sampling frequency
return ecg_signal, fs
def bandpass_filter(ecg, fs, lowcut=0.5, highcut=45):
"""Bandpass filter สำหรับลด noise"""
nyquist = fs / 2
low = lowcut / nyquist
high = highcut / nyquist
b, a = signal.butter(4, [low, high], btype='band')
return signal.filtfilt(b, a, ecg)
def detect_r_peaks(ecg, fs):
"""ตรวจจับ R-peaks ด้วย Pan-Tompkins algorithm"""
# Differentiate
diff = np.diff(ecg)
# Square
squared = diff ** 2
# Moving average
window = int(0.15 * fs)
integrated = np.convolve(squared, np.ones(window)/window, mode='same')
# Find peaks
threshold = np.mean(integrated) + 0.5 * np.std(integrated)
peaks, _ = signal.find_peaks(integrated, height=threshold, distance=int(0.3*fs))
return peaks
def calculate_rr_intervals(r_peaks, fs):
"""คำนวณ RR intervals จาก R-peaks"""
rr_intervals = np.diff(r_peaks) / fs * 1000 # milliseconds
return rr_intervals
def detect_af(rr_intervals):
"""ตรวจจับ AF จาก RR interval irregularity"""
if len(rr_intervals) < 10:
return {"af_detected": False, "reason": "Insufficient data"}
# 1. RMSSD (Root Mean Square of Successive Differences)
diffs = np.diff(rr_intervals)
rmssd = np.sqrt(np.mean(diffs ** 2))
# 2. Coefficient of Variation
cv = np.std(rr_intervals) / np.mean(rr_intervals) * 100
# 3. Shannon Entropy ของ RR intervals
hist, _ = np.histogram(rr_intervals, bins=20, density=True)
hist = hist[hist > 0]
se = entropy(hist)
# 4. Turning Point Ratio
tp = 0
for i in range(1, len(rr_intervals) - 1):
if ((rr_intervals[i] > rr_intervals[i-1] and rr_intervals[i] > rr_intervals[i+1]) or
(rr_intervals[i] < rr_intervals[i-1] and rr_intervals[i] < rr_intervals[i+1])):
tp += 1
tp_ratio = tp / (len(rr_intervals) - 2)
# AF Criteria
af_score = 0
if rmssd > 50: af_score += 1 # High variability
if cv > 15: af_score += 1 # High CV
if se > 2.5: af_score += 1 # High entropy
if tp_ratio > 0.7: af_score += 1 # High turning points
af_detected = af_score >= 3
return {
"af_detected": af_detected,
"af_score": af_score,
"rmssd": round(rmssd, 2),
"cv": round(cv, 2),
"entropy": round(se, 3),
"tp_ratio": round(tp_ratio, 3),
"mean_hr": round(60000 / np.mean(rr_intervals), 1),
"mean_rr": round(np.mean(rr_intervals), 1),
}
# pip install numpy scipy wfdb
สร้างระบบตรวจจับ AF อัตโนมัติด้วย Machine Learning
ใช้ ML model สำหรับจำแนก ECG ว่าเป็น AF หรือไม่
#!/usr/bin/env python3
# af_classifier.py — AF Detection with ML
import numpy as np
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report
import joblib
def extract_features(rr_intervals):
"""สร้าง features จาก RR intervals สำหรับ ML"""
if len(rr_intervals) < 5:
return None
diffs = np.diff(rr_intervals)
features = {
"mean_rr": np.mean(rr_intervals),
"std_rr": np.std(rr_intervals),
"cv_rr": np.std(rr_intervals) / np.mean(rr_intervals),
"median_rr": np.median(rr_intervals),
"min_rr": np.min(rr_intervals),
"max_rr": np.max(rr_intervals),
"range_rr": np.max(rr_intervals) - np.min(rr_intervals),
"rmssd": np.sqrt(np.mean(diffs ** 2)),
"sdsd": np.std(diffs),
"mean_diff": np.mean(np.abs(diffs)),
"pnn50": np.sum(np.abs(diffs) > 50) / len(diffs),
"pnn20": np.sum(np.abs(diffs) > 20) / len(diffs),
"skewness": float(np.mean(((rr_intervals - np.mean(rr_intervals)) / np.std(rr_intervals)) ** 3)) if np.std(rr_intervals) > 0 else 0,
"kurtosis": float(np.mean(((rr_intervals - np.mean(rr_intervals)) / np.std(rr_intervals)) ** 4)) if np.std(rr_intervals) > 0 else 0,
}
return features
def train_af_model(X_train, y_train):
"""Train AF detection model"""
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X_train)
model = GradientBoostingClassifier(
n_estimators=200, max_depth=5,
learning_rate=0.1, random_state=42
)
# Cross-validation
scores = cross_val_score(model, X_scaled, y_train, cv=5, scoring='f1')
print(f"Cross-validation F1: {scores.mean():.3f} (+/- {scores.std():.3f})")
model.fit(X_scaled, y_train)
# Save model
joblib.dump(model, "af_model.pkl")
joblib.dump(scaler, "af_scaler.pkl")
return model, scaler
def predict_af(rr_intervals, model=None, scaler=None):
"""ทำนาย AF จาก RR intervals"""
if model is None:
model = joblib.load("af_model.pkl")
scaler = joblib.load("af_scaler.pkl")
features = extract_features(rr_intervals)
if features is None:
return {"prediction": "unknown", "confidence": 0}
X = np.array([list(features.values())])
X_scaled = scaler.transform(X)
prediction = model.predict(X_scaled)[0]
probability = model.predict_proba(X_scaled)[0]
return {
"prediction": "AF" if prediction == 1 else "Normal",
"confidence": round(max(probability) * 100, 1),
"af_probability": round(probability[1] * 100, 1),
"features": features,
}
# pip install scikit-learn joblib numpy
ระบบ Real-time Heart Rate Monitoring
สร้างระบบ monitoring อัตราการเต้นหัวใจแบบ real-time
#!/usr/bin/env python3
# hr_monitor.py — Real-time Heart Rate Monitor
import time
import json
from datetime import datetime
from collections import deque
class HeartRateMonitor:
def __init__(self, window_size=30, alert_hr_high=100, alert_hr_low=50):
self.window_size = window_size
self.alert_hr_high = alert_hr_high
self.alert_hr_low = alert_hr_low
self.rr_buffer = deque(maxlen=window_size)
self.hr_history = deque(maxlen=1440) # 24 hours at 1/min
self.alerts = []
def add_rr_interval(self, rr_ms):
self.rr_buffer.append(rr_ms)
hr = 60000 / rr_ms
# Check alerts
if hr > self.alert_hr_high:
self.alerts.append({
"time": datetime.now().isoformat(),
"type": "HIGH_HR",
"value": round(hr, 1),
"message": f"Heart rate {hr:.0f} bpm exceeds {self.alert_hr_high}"
})
elif hr < self.alert_hr_low:
self.alerts.append({
"time": datetime.now().isoformat(),
"type": "LOW_HR",
"value": round(hr, 1),
"message": f"Heart rate {hr:.0f} bpm below {self.alert_hr_low}"
})
def get_current_hr(self):
if len(self.rr_buffer) < 3:
return None
recent = list(self.rr_buffer)[-5:]
return round(60000 / (sum(recent) / len(recent)), 1)
def get_hrv_metrics(self):
if len(self.rr_buffer) < 10:
return None
rr = list(self.rr_buffer)
import numpy as np
rr_arr = np.array(rr)
diffs = np.diff(rr_arr)
return {
"mean_hr": round(60000 / np.mean(rr_arr), 1),
"sdnn": round(np.std(rr_arr), 1),
"rmssd": round(np.sqrt(np.mean(diffs**2)), 1),
"pnn50": round(np.sum(np.abs(diffs) > 50) / len(diffs) * 100, 1),
"min_hr": round(60000 / np.max(rr_arr), 1),
"max_hr": round(60000 / np.min(rr_arr), 1),
}
def check_af(self):
if len(self.rr_buffer) < 20:
return {"status": "insufficient_data"}
rr = list(self.rr_buffer)
import numpy as np
rr_arr = np.array(rr)
cv = np.std(rr_arr) / np.mean(rr_arr) * 100
diffs = np.diff(rr_arr)
rmssd = np.sqrt(np.mean(diffs**2))
af_risk = "LOW"
if cv > 15 and rmssd > 50:
af_risk = "HIGH"
elif cv > 10 or rmssd > 40:
af_risk = "MODERATE"
return {
"af_risk": af_risk,
"cv": round(cv, 1),
"rmssd": round(rmssd, 1),
"irregularity": "IRREGULAR" if cv > 12 else "REGULAR",
}
def get_status(self):
hr = self.get_current_hr()
hrv = self.get_hrv_metrics()
af = self.check_af()
return {
"timestamp": datetime.now().isoformat(),
"heart_rate": hr,
"hrv": hrv,
"af_check": af,
"recent_alerts": self.alerts[-5:] if self.alerts else [],
}
# monitor = HeartRateMonitor(alert_hr_high=100, alert_hr_low=50)
# monitor.add_rr_interval(800) # 75 bpm
# print(json.dumps(monitor.get_status(), indent=2))
FAQ คำถามที่พบบ่อย
Q: AF กับ AFL อันไหนอันตรายกว่า?
A: ทั้งสองมีความเสี่ยงต่อ stroke เหมือนกัน แต่ AF พบบ่อยกว่ามากและมักจัดการยากกว่าเพราะจังหวะไม่สม่ำเสมอ AFL มักตอบสนองต่อการรักษาด้วย catheter ablation ได้ดีกว่า โดยมีอัตราสำเร็จมากกว่า 90% ส่วน AF ablation มีอัตราสำเร็จประมาณ 70-80% และอาจต้องทำซ้ำ
Q: Smartwatch ตรวจจับ AF ได้แม่นยำแค่ไหน?
A: Apple Watch และ Samsung Galaxy Watch มี FDA clearance สำหรับตรวจจับ AF ความแม่นยำประมาณ 98% สำหรับ positive predictive value แต่ sensitivity อาจต่ำกว่าเพราะตรวจแบบ spot check ไม่ใช่ continuous monitoring ผลจาก smartwatch ควรยืนยันด้วย 12-lead ECG จากแพทย์เสมอ
Q: Python Library ไหนดีสำหรับวิเคราะห์ ECG?
A: wfdb สำหรับอ่าน PhysioNet databases, neurokit2 สำหรับ ECG processing ครบวงจร, biosppy สำหรับ biosignal processing, heartpy สำหรับ HRV analysis และ scipy.signal สำหรับ signal processing ทั่วไป สำหรับ deep learning ใช้ TensorFlow หรือ PyTorch กับ 1D CNN
Q: CHA2DS2-VASc Score ใช้อย่างไร?
A: เป็น scoring system สำหรับประเมินความเสี่ยง stroke ในผู้ป่วย AF คะแนน 0 ในผู้ชาย (1 ในผู้หญิง) ถือว่าความเสี่ยงต่ำ ไม่ต้องใช้ยาป้องกันลิ่มเลือด คะแนน 2 ขึ้นไปควรใช้ยา anticoagulant เช่น Warfarin หรือ DOACs (Rivaroxaban, Apixaban, Dabigatran, Edoxaban)
