AnyMath 文档
Notebook

读取EDF文件

此示例演示了以EDF(欧洲数据格式)格式(一种用于存储生物医学信号的标准格式)下载、分析和可视化记录数据的过程。

关于EDF格式

EDF(欧洲数据格式)是多通道生物信号存储和交换的开放标准,广泛用于医学和科学研究。 用于记录脑电图、心电图、肌电图、呼吸信号、眼动、血氧饱和度等生理数据。

EDF旨在确保来自不同制造商和不同软件的设备之间的兼容性。 由于其固定的结构,这种格式很容易被许多数据分析工具处理。

使用EDF格式

功能 engee.clear() 清理工作区:

In [ ]:
engee.clear()

我们将使用函数进行连接 include 文件"edfread。jl"用于读取EDF文件:

In [ ]:
include("$(@__DIR__)/edfread.jl")
Out[0]:
edfread (generic function with 1 method)

功能 edfread 它用于读取EDF格式的数据。 结构 hdr 此函数返回的信息包含有关记录的完整元信息:

一般记录参数:

  • ver -EDF格式版本

  • patientID -病人ID

  • recordID -记录ID

  • startdatestarttime -开始录制的日期和时间

  • bytes -以字节为单位的标头大小

  • records -文件中的数据块数

  • duration -以秒为单位的一个区块的持续时间

  • ns -记录中的通道数

每个通道的参数:

  • labels -频道名称

  • transducers -传感器类型

  • physicalDims -物理测量单位

  • physicalMinsphysicalMaxs -最小和最大物理值

  • digitalMinsdigitalMaxs -最小和最大数字值

  • prefilters -在录制过程中应用的过滤器

  • samples -每个通道的一个块中的样本数

检查测试数据

来自[EDF/BDF测试文件]资源的标准化测试数据用于验证读取和处理EDF文件的算法的正确性(https://teuniz.net/edf_bdf_testfiles /)。

文件用于演示如何使用EDF格式。 test_generator.edf. 该文件包含用于测试和验证读取算法的多通道数据。

要使用EDF格式的数据,请使用以下函数 edfread,这将执行文件读取,元数据提取和信号加载。 由于她的工作,我们将得到两个对象。:

*标题结构 hdr 带记录参数;
*阵列 record,包含来自所有通道的数据。

In [ ]:
hdr, record = edfread("$(@__DIR__)/test_generator.edf")
Out[0]:
(EDFHeader(0.0, "test file", "EDF generator", "02.10.08", "14.27.00", 4352, 900, 1.0, 16, ["F4", "F3", "X10", "FP2", "P4", "C4", "P3", "C3", "X9", "FP1", "F8", "F7", "DC01", "DC04", "DC03", "DC02"], ["AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "AgAgCl", "Respiration", "SaO2", "BPM", ""], ["uV", "uV", "mV", "uV", "uV", "uV", "uV", "uV", "uV", "uV", "uV", "mV", "V", "%", "BPM", ""], [-3200.0, -3200.0, -1.6, -3200.0, -3200.0, -3200.0, -3200.0, -3200.0, -3200.0, -3200.0, -3200.0, -16.0, 0.0, -1200.0, -1200.0, -32768.0], [3200.0, 3200.0, 1.6, 3200.0, 3200.0, 3200.0, 3200.0, 3200.0, 3200.0, 3200.0, 3200.0, 16.0, 12.0, 1200.0, 1200.0, 32767.0], [-32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, 0.0, -32768.0, -32768.0, -32768.0], [32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0], ["HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz", "HP:0.015Hz"], [200, 100, 200, 200, 50, 100, 200, 200, 200, 200, 200, 200, 200, 25, 25, 25]), [-799.9633783474479 -799.9633783474479 … 800.0610360875868 800.0610360875868; -800.7446402685588 -750.646219577325 … NaN NaN; … ; 60.004577706569066 60.004577706569066 … NaN NaN; 16384.0 16384.0 … NaN NaN])

为了便于审查和验证从标题结构上传的数据 hdr 记录的关键参数进行提取显示。

In [ ]:
println("格式版本:        ", hdr.ver)
println("病人ID:           ", strip(hdr.patientID))
println("记录的描述:       ", strip(hdr.recordID))
println("开始日期/时间:     ", hdr.startdate, " ", hdr.starttime)
println("频道数目:    ", hdr.ns)
println("参赛作品数目:    ", hdr.records)
println("总工期:    ", hdr.records * hdr.duration, " sec")
Версия формата:        0.0
ID пациента:           test file
Описание записи:       EDF generator
Дата/время начала:     02.10.08 14.27.00
Количество каналов:    16
Количество записей:    900
Общая длительность:    900.0 сек

让我们创建一个具有每个通道特征的表:其名称,值的物理范围,测量单位和采样率。

In [ ]:
println("  非也。. /通道/范围(最小/最大)/单位/频率,Hz")
println("----------------------------------------------------------")

for ch in 1:hdr.ns
    label = hdr.labels[ch]

    # 我们取矩阵的一行并删除NaN(填充)
    row = record[ch, :]
    row = row[.!isnan.(row)]

    dataMin = round(minimum(row); digits = 2)
    dataMax = round(maximum(row); digits = 2)

    units = hdr.physicalDims[ch]
    units = units == "" ? "-" : units

    fs = round(hdr.samples[ch] / hdr.duration; digits = 2)

    println(
        lpad(ch, 2), " | ",
        rpad(label, 8), " | ",
        lpad(string(dataMin), 8), " / ",
        rpad(string(dataMax), 8), " | ",
        rpad(units, 6), " | ",
        fs
    )
end
 № |  Канал   | Диапазон (мин/макс) | Ед.изм | Частота, Гц
----------------------------------------------------------
 1 | F4       |  -799.96 / 800.06   | uV     | 200.0
 2 | F3       |  -800.74 / 800.84   | uV     | 100.0
 3 | X10      |     -0.8 / 0.8      | mV     | 200.0
 4 | FP2      |  -3200.0 / 3200.0   | uV     | 200.0
 5 | P4       |   -798.3 / 798.4    | uV     | 50.0
 6 | C4       |   -798.3 / 798.4    | uV     | 100.0
 7 | P3       |  -798.99 / 799.08   | uV     | 200.0
 8 | C3       |   -798.3 / 798.4    | uV     | 200.0
 9 | X9       |   -798.3 / 798.4    | uV     | 200.0
10 | FP1      |  -799.96 / 800.06   | uV     | 200.0
11 | F8       |  -692.74 / 692.83   | uV     | 200.0
12 | F7       |     -4.0 / 4.0      | mV     | 200.0
13 | DC01     |      0.0 / 6.0      | V      | 200.0
14 | DC04     |    100.0 / 100.0    | %      | 25.0
15 | DC03     |     60.0 / 60.0     | BPM    | 25.0
16 | DC02     |  16384.0 / 16384.0  | -      | 25.0

为了验证读取和解释数据的正确性,我们会将上传的元数据与[EDF/BDF测试文件]页面上提供的参考信息进行比较(https://teuniz.net/edf_bdf_testfiles /)。

 signal label  waveform       physical range         f         sf
 --------------------------------------------------------------------
    1    F4     block          +800uV/-800uV          1Hz       200Hz
    2    F3     triangle       +800uV/-800uV          3Hz       100Hz
    3    X10    impulse        +0.8mV/-0.8mV          5Hz       200Hz
    4    FP2    noise          +3200uV/-3200uV        -Hz       200Hz
    5    P4     sine           +800uV/-800uV          1Hz        50Hz
    6    C4     sine           +800uV/-800uV          2Hz       100Hz
    7    P3     sine           +800uV/-800uV          3Hz       200Hz
    8    C3     sine           +800uV/-800uV          4Hz       200Hz
    9    X9     sine           +800uV/-800uV          8Hz       200Hz
   10    FP1    sine           +800uV/-800uV         16Hz       200Hz
   11    F8     sine           +800uV/-800uV         32Hz       200Hz
   12    F7     triangle       +4mV/-4mV              5Hz       200Hz
   13    DC01   sine square    +6V/-0V                5Hz       200Hz
   14    DC04   DC             +100%                  -Hz        25Hz
   15    DC03   DC             +60BPM                 -Hz        25Hz
   16    DC02   DC             +16384                 -Hz        25Hz

因此,上传的数据对应于测试文件的描述,这证实了功能正常工作。 edfread.

让我们建立多通道记录的前5秒的波形,以比较所得到的图形与测试图像。

In [ ]:
t_max = 5.0     # 时间限制,与
nchan = size(record, 1) # 频道数目

plt = plot(
    layout = (nchan, 1),
    size   = (1000, 200*nchan),
    margin = 20*Plots.px
)

for ch in 1:nchan
    # 信道采样率
    fs = hdr.samples[ch] / hdr.duration

    # 每个通道的最大采样数
    n_time = min(Int(round(t_max * fs)), hdr.samples[ch] * hdr.records)

    # 时间和信号
    t = (0:n_time-1) ./ fs
    y = record[ch, 1:n_time]

    # 测量单位
    units = hdr.physicalDims[ch]
    units = units == "" ? "-" : units

    plot!(
        plt[ch],
        t, y,
        label  = "$(hdr.labels[ch])",
        xlabel = "时间,从",
        ylabel = units,
        legend = :topright
    )
end

display(plt)
image.png

测试功能操作 edfread 使用扩展的EDF+格式,我们将上传文件 test_generator_2.edf.

In [ ]:
hdr, record = edfread("$(@__DIR__)/test_generator_2.edf")
Out[0]:
(EDFHeader(0.0, "X X X X", "Startdate 10-DEC-2009 X X test_generator", "10.12.09", "12.44.02", 3328, 600, 1.0, 12, ["squarewave", "ramp", "pulse", "ECG", "noise", "sine1Hz", "sine8Hz", "sine85Hz", "sine15Hz", "sine17Hz", "sine50Hz", "EDFAnnotations"], ["", "", "", "", "", "", "", "", "", "", "", ""], ["uV", "uV", "uV", "uV", "uV", "uV", "uV", "uV", "uV", "uV", "uV", ""], [-1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1000.0, -1.0], [1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1000.0, 1.0], [-32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0, -32768.0], [32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0, 32767.0], ["", "", "", "", "", "", "", "", "", "", "", ""], [200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 51]), [99.99237048905174 99.99237048905174 … -99.96185244525817 -99.96185244525817; -99.96185244525817 -98.95475700007621 … 98.0086976424812 98.98527504386978; … ; 99.99237048905174 0.015259021896781633 … -99.96185244525817 0.015259021896781633; 0.37633325703822385 0.1568780041199359 … NaN NaN])

与之前的文件类似,我们提取并分析记录的关键参数。

In [ ]:
println("格式版本:         ", hdr.ver)
println("病人ID:            ", strip(hdr.patientID))
println("记录的描述:        ", strip(hdr.recordID))
println("开始日期/时间:      ", hdr.startdate, " ", hdr.starttime)
println("频道数目:     ", hdr.ns)
println("参赛作品数目:     ", hdr.records)
println("总工期:     ", hdr.records * hdr.duration, " 证券交易委员会")
Версия формата:         0.0
ID пациента:            X X X X
Описание записи:        Startdate 10-DEC-2009 X X test_generator
Дата/время начала:      10.12.09 12.44.02
Количество каналов:     12
Количество записей:     600
Общая длительность:     600.0 сек

让我们创建一个具有每个通道特征的表:其名称,值的物理范围,测量单位和采样率。

In [ ]:
println("  非也。. /通道/范围(最小/最大)/单位/频率,Hz")
println("------------------------------------------------------------")

for ch in 1:hdr.ns-1
    label = hdr.labels[ch]

    # 我们取矩阵的一行并删除NaN(填充)
    row = record[ch, :]
    row = row[.!isnan.(row)]

    dataMin = round(minimum(row); digits = 2)
    dataMax = round(maximum(row); digits = 2)

    units = hdr.physicalDims[ch]
    units = units == "" ? "-" : units

    fs = round(hdr.samples[ch] / hdr.duration; digits = 2)

    println(
        lpad(ch, 2), " | ",
        rpad(label, 10), " | ",
        lpad(string(dataMin), 8), " / ",
        rpad(string(dataMax), 8), " | ",
        rpad(units, 6), " | ",
        fs
    )
end
 № |   Канал    | Диапазон (мин/макс) | Ед.изм | Частота, Гц
------------------------------------------------------------
 1 | squarewave |   -99.96 / 99.99    | uV     | 200.0
 2 | ramp       |   -99.96 / 98.99    | uV     | 200.0
 3 | pulse      |     0.02 / 99.99    | uV     | 200.0
 4 | ECG        |   -17.32 / 61.48    | uV     | 200.0
 5 | noise      |     0.02 / 98.99    | uV     | 200.0
 6 | sine1Hz    |   -99.96 / 99.99    | uV     | 200.0
 7 | sine8Hz    |   -99.78 / 99.81    | uV     | 200.0
 8 | sine85Hz   |   -99.96 / 99.99    | uV     | 200.0
 9 | sine15Hz   |   -99.96 / 99.99    | uV     | 200.0
10 | sine17Hz   |   -99.96 / 99.99    | uV     | 200.0
11 | sine50Hz   |   -99.96 / 99.99    | uV     | 200.0

为了验证读取和解释数据的正确性,我们会将上传的元数据与[EDF/BDF测试文件]页面上提供的参考信息进行比较(https://teuniz.net/edf_bdf_testfiles /)。

 signal label/waveform  amplitude    f       sf
---------------------------------------------------
   1    squarewave        100 uV    0.1Hz   200 Hz
   2    ramp              100 uV    1 Hz    200 Hz
   3    pulse             100 uV    1 Hz    200 Hz
   4    ECG               100 uV    1 Hz    200 Hz
   5    noise             100 uV    - Hz    200 Hz
   6    sine 1 Hz         100 uV    1 Hz    200 Hz
   7    sine 8 Hz         100 uV    8 Hz    200 Hz
   8    sine 8.5 Hz       100 uV    8.5Hz   200 Hz
   9    sine 15 Hz        100 uV   15 Hz    200 Hz
  10    sine 17 Hz        100 uV   17 Hz    200 Hz
  11    sine 50 Hz        100 uV   50 Hz    200 Hz

因此,上传的数据对应于测试文件的描述,这证实了功能正常工作。 edfread.

为了检查EDF+数据的下载和解释的正确功能,我们将绘制记录的前10秒的图形。

In [ ]:
t_max = 10.0     # 时间限制,与
nchan = size(record, 1)-1

plt = plot(
    layout = (nchan, 1),
    size   = (1000, 200*nchan),
    margin = 30*Plots.px
)

for ch in 1:(nchan)
    # 信道采样率
    fs = hdr.samples[ch] / hdr.duration

    # 每个通道的最大采样数
    n_time = min(Int(round(t_max * fs)), hdr.samples[ch] * hdr.records)

    # 时间和信号
    t = (0:n_time-1) ./ fs
    y = record[ch, 1:n_time]

    # 测量单位
    units = hdr.physicalDims[ch]
    units = units == "" ? "-" : units

    plot!(
        plt[ch],
        t, y,
        label  = "$(hdr.labels[ch])",
        xlabel = "时间,从",
        ylabel = units,
        legend = :topright
    )
end

display(plt)
image.png

结论

在此示例中,考虑了使用EDF和**EDF+**格式的数据的原则。 使用测试文件的示例(test_generator.edftest_generator_2.edf),取自[EDF/BDF测试文件](https://teuniz.net/edf_bdf_testfiles /)。