Анализ вариабельности сердечного ритма по ЭКГ сигналу
Анализ вариабельности сердечного ритма по ЭКГ сигналу
В проекте рассматривается анализ вариабельности сердечного ритма по ЭКГ сигналу. На основе найденных R-пиков рассчитываются RR-интервалы и частота сердечных сокращений, после чего выполняется визуализация результатов с использованием RR-тахограммы, гистограммы RR-интервалов и скаттерограммы. Дополнительно проводится сравнение нормального ритма, брадикардии и тахикардии.
Настройка отображения
Функция engee.clear() выполняет очистку рабочего пространств:
engee.clear()
Для визуализации результатов доступны два режима отрисовки графиков:
-
Для статической, быстрой отрисовки - используйте
gr(). -
Для интерактивной, динамической визуализации с возможностью масштабирования и просмотра значений - используйте
plotlyjs().
Выберите одну из команд, закомментировав вторую. Оба бэкенда являются взаимоисключающими, и активирован будет последний вызванный.
#gr()
plotlyjs()
Подключим с помощью функции include файл "read.jl" для чтения файлов:
include("$(@__DIR__)/read.jl")
Установка необходимых библиотек
На данном этапе выполняется проверка наличия необходимых библиотек Julia и их автоматическая установка при отсутствии в рабочем окружении. В рассматриваемом примере используется библиотека Statistics, необходимая для выполнения базовых статистических расчетов при анализе вариабельности сердечного ритма, включая обработку RR-интервалов, расчет средних значений и оценку распределения данных.
let
installed_packages = collect(x.name for (_, x) in Pkg.dependencies() if x.is_direct_dep)
list_packages = ["Statistics"]
for pack in list_packages
pack in installed_packages || Pkg.add(pack)
end
end
using Statistics
Подключается библиотека EngeeDSP и импортируются функции, необходимые для цифровой обработки ЭКГ сигнала, включая фильтрацию, спектральный анализ, поиск пиков и выполнение свертки.
import EngeeDSP.Functions: fft, butter, filter, findpeaks, conv
Обработка и анализ ЭКГ сигнала
В данном разделе рассматривается детекция R-пиков и анализ RR-интервалов ЭКГ сигнала. На основе найденных R-пиков рассчитываются RR-интервалы, после чего применяются основные методы анализа вариабельности сердечного ритма:
-
RR-тахограмма — показывает изменение длительности сердечных циклов во времени;
-
гистограмма RR-интервалов — отражает распределение RR-интервалов на выбранном фрагменте записи;
-
скаттерограмма — позволяет оценить взаимосвязь между соседними сердечными циклами.
Совместное использование этих методов позволяет наглядно проанализировать характер сердечного ритма и оценить его вариабельность.
Функция обработки ЭКГ сигнала
Представим функцию detect_r, предназначенную для обработки ЭКГ сигнала и обнаружения R-пиков. Внутри функции выполняются основные этапы, подробно рассмотренные в примере "Детекция R-пиков на ЭКГ сигнале": фильтрация сигнала, подготовка данных к детекции, поиск R-пиков, уточнение их положения, а также расчет RR-интервалов и частоты сердечных сокращений.
function detect_r(ecg, fs;
f_center = 60.0,
bw = 6.0,
filter_order = 6,
smooth_window = 0.020,
min_peak_distance = 0.30,
search_window = 0.05
)
N = length(ecg)
# Подавление сетевой помехи
f1 = f_center - bw / 2
f2 = f_center + bw / 2
wn1 = f1 / (fs / 2)
wn2 = f2 / (fs / 2)
b, a = butter(filter_order, [wn1, wn2], "stop")
ecg_filt = filter(b, a, ecg)
# Подготовка сигнала для детекции R-пиков
d_ecg = vcat(0.0, diff(ecg_filt))
sq_ecg = d_ecg .^ 2
win = max(3, Int(round(smooth_window * fs)))
kernel = ones(win) ./ win
det_sig = conv(sq_ecg, kernel)
det_sig = det_sig[1:N]
# Поиск R-пиков
thr = mean(det_sig) + 1.5*std(det_sig)
min_dist = Int(round(min_peak_distance * fs))
_, locs = findpeaks(
det_sig,
out = :data,
MinPeakDistance = min_dist,
MinPeakHeight = thr
)
# Уточнение положения R-пиков
search_radius = Int(round(search_window * fs))
r_locs = Int[]
for idx in locs
l = max(1, idx - search_radius)
r = min(N, idx + search_radius)
seg = ecg_filt[l:r]
_, imax = findmax(seg)
push!(r_locs, l + imax - 1)
end
r_locs = unique(sort(r_locs))
# Время и амплитуды R-пиков
r_times = (r_locs .- 1) ./ fs
r_vals = ecg_filt[r_locs]
# RR-интервалы и ЧСС
rr = diff(r_times)
rr_ms = rr .* 1000
rr_time = r_times[2:end]
heart_rate = 60.0 ./ rr
return ecg_filt, r_times, r_vals, rr_ms, rr_time, heart_rate
end
Загрузка и обработка сигнала
Выполняется загрузка фрагмента ЭКГ сигнала из записи 122 базы MIT-BIH. В качестве анализируемого сигнала выбирается первый канал записи, после чего он передается в функцию detect_r.
data = "$(@__DIR__)/mitdb/122"
hdr = read_header(data)
fs = Float64(hdr.fs)
# Выбор фрагмента сигнала для анализа
t_start = 10.0 * 60.0 # начало фрагмента, с
t_dur = 10.0 * 60.0 # длительность фрагмента, с
# Перевод времени в номера отсчетов
sampfrom = Int(round(t_start * fs))
sampto = sampfrom + Int(round(t_dur * fs))
# Чтение выбранного фрагмента ЭКГ сигнала
_, raw, phys = read_dat_212(data; sampfrom=sampfrom, sampto=sampto)
# Выбор первого канала ЭКГ сигнала
ecg = phys === nothing ? Float64.(raw[:, 1]) : Float64.(phys[:, 1])
# Формирование временной оси
N = length(ecg)
t = collect(0:N-1) ./ fs
# Обработка сигнала и детекция R-пиков
ecg_filt, r_times, r_vals, rr_ms, rr_time, heart_rate = detect_r(ecg, fs)
Построим отфильтрованный ЭКГ сигнал с отображением найденных R-пиков.
plot(t, ecg_filt,
xlabel="Время, с",
ylabel="Амплитуда, мВ",
label="ЭКГ сигнал"
)
scatter!(r_times, r_vals, markersize=3, label="R-пики")
Анализ RR-интервалов
Рассчитаем основные показатели сердечного ритма: средний RR-интервал, стандартное отклонение RR-интервалов и среднюю частоту сердечных сокращений.
mean_rr = mean(rr_ms) # средний RR-интервал, мс
std_rr = std(rr_ms) # стандартное отклонение RR-интервалов, мс
mean_hr = mean(heart_rate) # средняя ЧСС, уд/мин
println("Средний RR-интервал: ", round(mean_rr, digits=2), " мс")
println("Стандартное отклонение RR-интервалов: ", round(std_rr, digits=2), " мс")
println("Средняя ЧСС: ", round(mean_hr, digits=2), " уд/мин")
RR-тахограмма
RR-тахограмма отражает изменение длительности RR-интервалов во времени, то есть показывает, как меняется продолжительность последовательных сердечных циклов на выбранном фрагменте ЭКГ сигнала. По этой зависимости можно оценить стабильность сердечного ритма, выявить плавные колебания RR-интервалов, а также заметить резкие изменения, которые могут быть связаны с артефактами, ошибками детекции или особенностями ритма.
plot(rr_time, rr_ms,
xlabel="Время, с",
ylabel="RR-интервал, мс",
title="RR-тахограмма",
legend=false
)
Гистограмма RR-интервалов
Гистограмма RR-интервалов показывает распределение длительности сердечных циклов. По оси X откладываются значения RR-интервалов в миллисекундах, а по оси Y – количество интервалов, попавших в соответствующий диапазон. Такая визуализация позволяет определить, около какого значения сосредоточена основная часть RR-интервалов, насколько выражен их разброс и присутствуют ли отдельные интервалы, заметно отличающиеся от основной группы.
histogram(rr_ms,
bins=30,
xlabel="RR-интервал, мс",
ylabel="Количество интервалов",
title="Гистограмма RR-интервалов",
legend=false
)
Скаттерограмма RR-интервалов
Скаттерограмма RR-интервалов показывает взаимосвязь между соседними сердечными циклами. По оси X откладывается текущий RR-интервал , а по оси Y – следующий RR-интервал . Такой график позволяет оценить, насколько сильно длительность очередного сердечного цикла отличается от предыдущего. Если точки расположены компактно и близко к диагонали , соседние RR-интервалы имеют близкие значения, а ритм является относительно стабильным. Увеличение разброса точек отражает более выраженную вариабельность сердечного ритма или возможное наличие нерегулярных сокращений.
rr_n = rr_ms[1:end-1]
rr_next = rr_ms[2:end]
# Границы осей для корректного отображения
rr_min = minimum(vcat(rr_n, rr_next))
rr_max = maximum(vcat(rr_n, rr_next))
scatter(rr_n, rr_next,
xlabel="RR(n), мс",
ylabel="RR(n+1), мс",
title="Скаттерограмма RR-интервалов",
legend=false,
markersize=3,
xlim=(0, 1200),
ylim=(0, 1200)
)
plot!([0, 1200], [0, 1200],
linestyle=:dash,
linewidth=2
)
Анализ нормального ритма, брадикардии и тахикардии
В данном разделе рассматривается сравнение нормального ритма, брадикардии и тахикардии по RR-интервалам и частоте сердечных сокращений. В общем случае нормальный сердечный ритм взрослого человека в состоянии покоя находится в диапазоне примерно 60-100 уд/мин. Брадикардия соответствует замедленному ритму, при котором ЧСС обычно ниже 60 уд/мин, а тахикардия - учащенному ритму, при котором ЧСС превышает 100 уд/мин.
В рамках примера указанные типы ритма рассматриваются как демонстрационные случаи для анализа ЭКГ сигналов. Для каждого сигнала будет выполнена детекция R-пиков, расчет RR-интервалов и средней ЧСС, а также построены RR-тахограмма, гистограмма RR-интервалов и скаттерограмма. Это позволит сравнить, как меняются длительность сердечных циклов и характер распределения RR-интервалов при нормальном ритме, брадикардии и тахикардии.
Загрузка, обработка и отображение ЭКГ сигналов
Для удобства работы сформируем функцию load_mitdb, предназначенную для загрузки выбранного фрагмента ЭКГ записи из базы MIT-BIH.
function load_mitdb(record_name;
t_start_min = 5.0,
t_dur_min = 10.0,
channel = 1
)
data = "$(@__DIR__)/mitdb/$record_name"
# Считывание информации о записи
hdr = read_header(data)
fs = Float64(hdr.fs)
# Перевод времени в номера отсчетов
t_start = t_start_min * 60.0
t_dur = t_dur_min * 60.0
sampfrom = Int(round(t_start * fs))
sampto = sampfrom + Int(round(t_dur * fs))
# Чтение выбранного фрагмента ЭКГ сигнала
_, raw, phys = read_dat_212(data; sampfrom=sampfrom, sampto=sampto)
# Выбор канала ЭКГ сигнала
ecg = phys === nothing ? Float64.(raw[:, channel]) : Float64.(phys[:, channel])
# Формирование временной оси
N = length(ecg)
t = collect(0:N-1) ./ fs
return ecg, fs, t
end
Для демонстрации нормального сердечного ритма загрузим данные записи 100 и отобразим результат обработки: отфильтрованный ЭКГ сигнал с отмеченными R-пиками.
ecg_norm, fs_norm, t_norm = load_mitdb(
"100";
t_start_min = 5.0,
t_dur_min = 5.0,
channel = 1
)
ecg_filt_norm, r_times_norm, r_vals_norm, rr_ms_norm, rr_time_norm, heart_rate_norm = detect_r(ecg_norm, fs_norm)
p_norm = plot(t_norm, ecg_filt_norm,
xlabel = "Время, с",
ylabel = "Амплитуда, мВ",
label = "Нормальный ритм"
)
scatter!(p_norm, r_times_norm, r_vals_norm,markersize = 2,label = "R-пики")
Аналогично выполним обработку записи 123, выбранной для демонстрации брадикардии. На графике будут показаны отфильтрованный ЭКГ сигнал и найденные R-пики, по которым далее рассчитываются RR-интервалы.
ecg_brady, fs_brady, t_brady = load_mitdb(
"123";
t_start_min = 5.0,
t_dur_min = 5.0,
channel = 1
)
ecg_filt_brady, r_times_brady, r_vals_brady, rr_ms_brady, rr_time_brady, heart_rate_brady = detect_r(ecg_brady, fs_brady)
p_brady = plot(t_brady, ecg_filt_brady,
xlabel = "Время, с",
ylabel = "Амплитуда, мВ",
label = "Брадикардия"
)
scatter!(p_brady, r_times_brady, r_vals_brady,markersize = 2,label = "R-пики")
Для анализа учащенного сердечного ритма используем запись 209. После фильтрации и детекции R-пиков отобразим обработанный ЭКГ сигнал, что позволит визуально оценить уменьшение расстояния между соседними сердечными циклами.
ecg_tachy, fs_tachy, t_tachy = load_mitdb(
"209";
t_start_min = 6.0,
t_dur_min = 5.0,
channel = 1
)
ecg_filt_tachy, r_times_tachy, r_vals_tachy, rr_ms_tachy, rr_time_tachy, heart_rate_tachy = detect_r(ecg_tachy, fs_tachy)
p_tachy = plot(t_tachy, ecg_filt_tachy,
xlabel = "Время, с",
ylabel = "Амплитуда, мВ",
label = "Тахикардия"
)
scatter!(p_tachy, r_times_tachy, r_vals_tachy, markersize = 2,label = "R-пики")
Отобразим обработанные ЭКГ сигналы для разных типов сердечного ритма.
plot(p_norm, p_brady, p_tachy,
layout = (3, 1),
size = (1000, 850),
margin = 40*Plots.px
)
По полученным осциллограммам ЭКГ сигналов с различными типами сердечного ритма можно заметить различия в расстоянии между соседними R-пиками. При брадикардии интервалы между сердечными сокращениями увеличиваются по сравнению с нормальным ритмом, что соответствует снижению ЧСС. При тахикардии, наоборот, R-пики расположены ближе друг к другу, что указывает на учащение сердечного ритма и уменьшение длительности RR-интервалов.
Анализ RR-интервалов
Для удобства вывода численных показателей сформируем функцию print_hr, которая рассчитывает и отображает основные характеристики RR-интервалов и частоты сердечных сокращений.
function print_hr(name, rr_ms, heart_rate)
mean_rr = mean(rr_ms) # средний RR-интервал, мс
std_rr = std(rr_ms) # стандартное отклонение RR-интервалов, мс
mean_hr = mean(heart_rate) # средняя ЧСС, уд/мин
println(name)
println("Средний RR-интервал: ", round(mean_rr, digits=2), " мс")
println("Стандартное отклонение RR-интервалов: ", round(std_rr, digits=2), " мс")
println("Средняя ЧСС: ", round(mean_hr, digits=2), " уд/мин")
println()
end
print_hr("Нормальный ритм", rr_ms_norm, heart_rate_norm)
print_hr("Брадикардия", rr_ms_brady, heart_rate_brady)
print_hr("Тахикардия", rr_ms_tachy, heart_rate_tachy)
По результатам статистического анализа видно, что полученные значения соответствуют ожидаемым различиям между типами сердечного ритма. Для нормального ритма средний RR-интервал составил 771,8 мс, а средняя ЧСС — 77,99 уд/мин, что соответствует диапазону нормального сердечного ритма.
При брадикардии средний RR-интервал увеличился до 1172,63 мс, а средняя ЧСС снизилась до 51,79 уд/мин. Это подтверждает замедление сердечного ритма по сравнению с нормальным состоянием. При тахикардии, наоборот, средний RR-интервал уменьшился до 549,12 мс, а средняя ЧСС увеличилась до 115,26 уд/мин, что соответствует учащенному сердечному ритму.
Таким образом, статистические показатели подтверждают результаты визуального анализа ЭКГ сигналов.
RR-тахограмма
Построим RR-тахограммы для рассматриваемых типов сердечного ритма: нормального ритма, брадикардии и тахикардии.
p_rr_norm = plot(rr_time_norm, rr_ms_norm,
xlabel = "Время, с",
ylabel = "RR-интервал, мс",
title = "Нормальный ритм",
legend = false
)
p_rr_brady = plot(rr_time_brady, rr_ms_brady,
xlabel = "Время, с",
ylabel = "RR-интервал, мс",
title = "Брадикардия",
legend = false
)
p_rr_tachy = plot(rr_time_tachy, rr_ms_tachy,
xlabel = "Время, с",
ylabel = "RR-интервал, мс",
title = "Тахикардия",
legend = false
)
plot(p_rr_norm, p_rr_brady, p_rr_tachy,
layout = (3, 1),
size = (1000, 850),
margin = 40*Plots.px
)
Гистограмма RR-интервалов
Рассмотрим распределение RR-интервалов для анализируемых типов сердечного ритма. Гистограммы, построенные на одном графике, позволяют сравнить диапазоны значений и определить, где сосредоточена основная часть сердечных циклов для каждого случая.
histogram(rr_ms_norm,
bins = 30,
alpha = 0.5,
xlabel = "RR-интервал, мс",
ylabel = "Количество интервалов",
title = "Гистограммы RR-интервалов",
label = "Нормальный ритм"
)
histogram!(rr_ms_brady,
bins = 30,
alpha = 0.5,
label = "Брадикардия"
)
histogram!(rr_ms_tachy,
bins = 30,
alpha = 0.5,
label = "Тахикардия"
)
По гистограммам видно, что распределения RR-интервалов для анализируемых типов ритма занимают разные диапазоны. При нормальном ритме основная часть значений сосредоточена в области около 750–800 мс, что соответствует средней ЧСС около 78 уд/мин.
Для брадикардии распределение смещено в область больших RR-интервалов, примерно 1000–1300 мс. Это подтверждает увеличение длительности сердечных циклов и снижение частоты сердечных сокращений. При тахикардии распределение, наоборот, смещается в область меньших RR-интервалов, что соответствует учащенному сердечному ритму.
Так как анализ выполняется на реальных ЭКГ записях, распределения могут иметь неидеальную форму. В частности, для тахикардии заметны два характерных максимума, что указывает на наличие двух преобладающих диапазонов RR-интервалов в выбранном фрагменте записи. Это может быть связано с изменением ритма внутри анализируемого участка или особенностями реального клинического сигнала.
Таким образом, гистограммы позволяют наглядно сравнить длительность сердечных циклов и показать различия между нормальным ритмом, брадикардией и тахикардией по распределению RR-интервалов.
Скаттерограмма RR-интервалов
Рассмотрим скаттерограммы RR-интервалов для анализируемых типов сердечного ритма.
rr_n_norm = rr_ms_norm[1:end-1]
rr_next_norm = rr_ms_norm[2:end]
rr_n_brady = rr_ms_brady[1:end-1]
rr_next_brady = rr_ms_brady[2:end]
rr_n_tachy = rr_ms_tachy[1:end-1]
rr_next_tachy = rr_ms_tachy[2:end]
rr_min = 0
rr_max = 1800
# Нормальный ритм
p_scatter_norm = scatter(rr_n_norm, rr_next_norm,
xlabel = "RR(n), мс",
ylabel = "RR(n+1), мс",
title = "Нормальный ритм",
legend = false,
markersize = 3,
xlim = (rr_min, rr_max),
ylim = (rr_min, rr_max)
)
plot!(p_scatter_norm, [rr_min, rr_max], [rr_min, rr_max],
linestyle = :dash,
linewidth = 2
)
# Брадикардия
p_scatter_brady = scatter(rr_n_brady, rr_next_brady,
xlabel = "RR(n), мс",
ylabel = "RR(n+1), мс",
title = "Брадикардия",
legend = false,
markersize = 3,
xlim = (rr_min, rr_max),
ylim = (rr_min, rr_max)
)
plot!(p_scatter_brady, [rr_min, rr_max], [rr_min, rr_max],
linestyle = :dash,
linewidth = 2
)
# Тахикардия
p_scatter_tachy = scatter(rr_n_tachy, rr_next_tachy,
xlabel = "RR(n), мс",
ylabel = "RR(n+1), мс",
title = "Тахикардия",
legend = false,
markersize = 3,
xlim = (rr_min, rr_max),
ylim = (rr_min, rr_max)
)
plot!(p_scatter_tachy, [rr_min, rr_max], [rr_min, rr_max],
linestyle = :dash,
linewidth = 2
)
# Отображение скаттерограмм
plot(p_scatter_norm, p_scatter_brady, p_scatter_tachy,
layout = (3, 1),
size = (800, 1400),
margin = 40*Plots.px
)
Для нормального ритма точки расположены достаточно компактно и преимущественно сосредоточены вблизи диагональной линии. Это показывает, что соседние RR-интервалы имеют близкие значения, а изменение длительности сердечных циклов выражено умеренно.
Для брадикардии точки смещены в область больших RR-интервалов и распределены более широко. Четко выраженного компактного участка не наблюдается, что указывает на большую изменчивость длительности сердечных циклов. Такой характер также согласуется с гистограммой RR-интервалов, где распределение имеет более широкий диапазон значений.
Для тахикардии можно заметить два выраженных участка расположения точек: один в области коротких RR-интервалов, другой – в области более длинных интервалов. Это показывает, что в выбранном фрагменте присутствуют два характерных диапазона длительности сердечных циклов. Такой результат согласуется с гистограммой, где также наблюдаются два заметных максимума распределения.
Для более наглядного сравнения все три скатерограммы дополнительно отображаются на одном графике.
p_scatter_all = scatter(rr_n_norm, rr_next_norm,
xlabel = "RR(n), мс",
ylabel = "RR(n+1), мс",
label = "Нормальный ритм",
markersize = 3,
xlim = (rr_min, rr_max),
ylim = (rr_min, rr_max),
legend_position = :outertopright
)
scatter!(p_scatter_all, rr_n_brady, rr_next_brady,
label = "Брадикардия",
markersize = 3
)
scatter!(p_scatter_all, rr_n_tachy, rr_next_tachy,
label = "Тахикардия",
markersize = 3
)
plot!(p_scatter_all, [rr_min, rr_max], [rr_min, rr_max],
linestyle = :dash,
linewidth = 2,
label = false
)
На общей скатерограмме RR интервалов видно, что три типа ритма занимают разные области значений.
Используемые материалы
В работе использовались реальные экспериментальные записи электрокардиограммы, полученные из открытого ресурса PhysioNet. В качестве исходных данных применялась база MIT-BIH Arrhythmia Database. Для первичного анализа вариабельности сердечного ритма использовалась запись 122. Для сравнения различных типов сердечного ритма дополнительно применялись записи 100, 123 и 209, соответствующие демонстрационным примерам нормального ритма, брадикардии и тахикардии.
Заключение
В данном примере был рассмотрен анализ вариабельности сердечного ритма по ЭКГ сигналу. На основе найденных R-пиков были рассчитаны RR-интервалы и частота сердечных сокращений, после чего построены основные зависимости для оценки характера сердечного ритма.
Работа примера включает следующие основные этапы:
Загрузка данных. Исходные данные ЭКГ были получены из открытой базы данных PhysioNet MIT-BIH Arrhythmia Database. Для анализа использовались реальные экспериментальные записи электрокардиограммы.
Детекция R-пиков. Для обработки ЭКГ сигнала применялась функция detect_r, в которой выполняются фильтрация сигнала, подготовка к детекции, поиск R-пиков и уточнение их положения. Внутри данной функции используются функции библиотеки EngeeDSP, включая butter, filter, conv и findpeaks.
Расчет RR-интервалов и ЧСС. На основе временных координат R-пиков были рассчитаны RR-интервалы и частота сердечных сокращений. Дополнительно определялись средний RR-интервал, стандартное отклонение RR-интервалов и средняя ЧСС.
Анализ вариабельности сердечного ритма. Для оценки характера изменения сердечного ритма были построены RR-тахограмма, гистограмма RR-интервалов и скаттерограмма. Эти зависимости позволяют оценить длительность сердечных циклов, распределение RR-интервалов и взаимосвязь между соседними интервалами.
Сравнение типов сердечного ритма. Для демонстрации различий были рассмотрены нормальный ритм, брадикардия и тахикардия. По результатам анализа видно, что при брадикардии RR-интервалы увеличиваются, а средняя ЧСС снижается. При тахикардии, наоборот, RR-интервалы становятся короче, а средняя ЧСС возрастает.
В результате выполнения примера была показана последовательность анализа вариабельности сердечного ритма по ЭКГ сигналу: от детекции R-пиков до построения тахограммы, гистограммы и скаттерограммы RR-интервалов.