Technology

Atrial Fibrillation and Atrial Flutter Unspecified คือ — อาการ วินิจฉัย และวิเคราะห์ ECG

atrial fibrillation and atrial flutter unspecified คอ
atrial fibrillation and atrial flutter unspecified คือ | SiamCafe Blog
2025-09-13· อ. บอม — SiamCafe.net· 1,628 คำ

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)

📖 บทความที่เกี่ยวข้อง

atrial fibrillation and atrial flutter คืออ่านบทความ → atrial flutter 2:1 คืออ่านบทความ → atrial flutter ekg คืออ่านบทความ → atrial flutter treatment คืออ่านบทความ → atrial flutter คืออ่านบทความ →

📚 ดูบทความทั้งหมด →