番外续2-MIT-BIH Arrhythmia Database

1Goldberger, A., Amaral, L., Glass, L., Hausdorff, J., Ivanov, P. C., Mark, R., ... & Stanley, H. E. (2000). PhysioBank, PhysioToolkit, and PhysioNet: Components of a new research resource for complex physiologic signals. Circulation Online. 101 (23), pp. e215--e220. RRID:SCR_007345.

SHA256SUMS.txt文件同样中记录了各个文件的命名,但需要调整细节,否则将报错:

Matlab 复制代码
% 文件路径
filePath = 'D:\ECG-Tran\mit-bih-arrhythmia-database\SHA256SUMS.txt';
 
% 读取所有行
fid = fopen(filePath, 'r');
lines = textscan(fid, '%s', 'Delimiter', '\n', 'Whitespace', '');
fclose(fid);
lines = lines{1};
 
% 提取每行末尾的文件名
fileNames = {};
for i = 1:length(lines)
    token = regexp(lines{i}, '\S+$', 'match', 'once');
    if ~isempty(token)
        fileNames{end+1} = token;
    end
end
 
% 提取扩展名(包括短横线,如 '.hea-'),无扩展名的留空
extensions = cellfun(@(f) regexp(f, '\.[^.]*$', 'match', 'once'), fileNames, 'UniformOutput', false);
 
% 将空后缀替换为 'no_extension'
emptyIdx = cellfun(@isempty, extensions);
extensions(emptyIdx) = {'no_extension'};
 
 
 
% 获取唯一扩展名及其对应的合法字段名
uniqueExt = unique(extensions);
fieldNames = cellfun(@ext2fieldname, uniqueExt, 'UniformOutput', false);
 
% 分组并排序
sortedGroups = struct();
extOriginalMap = struct();  % 存储每个字段名对应的原始扩展名(用于显示)
for i = 1:length(uniqueExt)
    ext = uniqueExt{i};
    fieldName = fieldNames{i};
    idx = strcmp(extensions, ext);
    groupFiles = fileNames(idx);
    
    % 排序:优先按文件名开头的数字升序,否则按字母序
    nums = cellfun(@(f) regexp(f, '^\d+', 'match', 'once'), groupFiles, 'UniformOutput', false);
    hasNum = ~cellfun(@isempty, nums);
    if all(hasNum)
        numVals = cellfun(@str2double, nums);
        [~, sortIdx] = sort(numVals);
    else
        [~, sortIdx] = sort(groupFiles);
    end
    sortedGroups.(fieldName) = groupFiles(sortIdx);
    extOriginalMap.(fieldName) = ext;  % 保存原始扩展名
    % 保存为 MAT 文件
    save('sorted_filenames_by_ext.mat', 'sortedGroups', 'extOriginalMap');
end
 
% 显示结果(使用原始扩展名展示)
extFieldNames = fieldnames(sortedGroups);
for i = 1:length(extFieldNames)
    fieldName = extFieldNames{i};
    originalExt = extOriginalMap.(fieldName);
    if strcmp(originalExt, 'no_extension')
        displayExt = '无扩展名';
    else
        displayExt = originalExt;
    end
    fprintf('后缀: %s, 共 %d 个文件\n', displayExt, length(sortedGroups.(fieldName)));
    disp(sortedGroups.(fieldName)');
end
 
% 将扩展名转换为合法的结构体字段名(例如 '.atr' -> 'atr', '.hea-' -> 'hea_')

function validName = ext2fieldname(ext)
    if strcmp(ext, 'no_extension')
        validName = 'no_extension';
        return;
    end
    % 去掉开头的点号
    if startsWith(ext, '.')
        ext = ext(2:end);
    end
    % 将非字母数字下划线替换为下划线(例如 '-' -> '_')
    validName = regexprep(ext, '[^a-zA-Z0-9_]', '_');
    % 如果结果为空或首字符是数字,则添加前缀 'ext_'
    if isempty(validName) || ismember(validName(1), '0123456789')
        validName = ['ext_' validName];
    end
end

这里的文件相较于前文使用的数据库要更多了:

Matlab 复制代码
后缀: .1, 共 1 个文件
    {'mitdbdir/src/tab.1'}

后缀: .2, 共 1 个文件
    {'mitdbdir/src/tab.2'}

后缀: .3, 共 1 个文件
    {'mitdbdir/src/tab.3'}

后缀: .4, 共 1 个文件
    {'mitdbdir/src/tab.4'}

后缀: .5, 共 1 个文件
    {'mitdbdir/src/tab.5'}

后缀: .at_, 共 8 个文件
    {'108.at_'}
    {'117.at_'}
    {'119.at_'}
    {'203.at_'}
    {'209.at_'}
    {'214.at_'}
    {'215.at_'}
    {'222.at_'}

后缀: .atr, 共 72 个文件
    {'100.atr'          }
    {'101.atr'          }
    {'102-0.atr'        }
    {'102.atr'          }
    {'103.atr'          }
    {'104.atr'          }
    {'105.atr'          }
    {'106.atr'          }
    {'107.atr'          }
    {'108.atr'          }
    {'109.atr'          }
    {'111.atr'          }
    {'112.atr'          }
    {'113.atr'          }
    {'114.atr'          }
    {'115.atr'          }
    {'116.atr'          }
    {'117.atr'          }
    {'118.atr'          }
    {'119.atr'          }
    {'121.atr'          }
    {'122.atr'          }
    {'123.atr'          }
    {'124.atr'          }
    {'200.atr'          }
    {'201.atr'          }
    {'202.atr'          }
    {'203.atr'          }
    {'205.atr'          }
    {'207.atr'          }
    {'208.atr'          }
    {'209.atr'          }
    {'210.atr'          }
    {'212.atr'          }
    {'213.atr'          }
    {'214.atr'          }
    {'215.atr'          }
    {'217.atr'          }
    {'219.atr'          }
    {'220.atr'          }
    {'221.atr'          }
    {'222.atr'          }
    {'223.atr'          }
    {'228.atr'          }
    {'230.atr'          }
    {'231.atr'          }
    {'232.atr'          }
    {'233.atr'          }
    {'234.atr'          }
    {'x_mitdb/x_108.atr'}
    {'x_mitdb/x_109.atr'}
    {'x_mitdb/x_111.atr'}
    {'x_mitdb/x_112.atr'}
    {'x_mitdb/x_113.atr'}
    {'x_mitdb/x_114.atr'}
    {'x_mitdb/x_115.atr'}
    {'x_mitdb/x_116.atr'}
    {'x_mitdb/x_117.atr'}
    {'x_mitdb/x_121.atr'}
    {'x_mitdb/x_122.atr'}
    {'x_mitdb/x_123.atr'}
    {'x_mitdb/x_124.atr'}
    {'x_mitdb/x_220.atr'}
    {'x_mitdb/x_221.atr'}
    {'x_mitdb/x_222.atr'}
    {'x_mitdb/x_223.atr'}
    {'x_mitdb/x_228.atr'}
    {'x_mitdb/x_230.atr'}
    {'x_mitdb/x_231.atr'}
    {'x_mitdb/x_232.atr'}
    {'x_mitdb/x_233.atr'}
    {'x_mitdb/x_234.atr'}

后缀: .bat, 共 1 个文件
    {'mitdbdir/src/printdir.bat'}

后缀: .c, 共 3 个文件
    {'mitdbdir/src/dbnotes-html.c'}
    {'mitdbdir/src/dbnotes.c'     }
    {'mitdbdir/src/dbtab.c'       }

后缀: .dat, 共 71 个文件
    {'100.dat'          }
    {'101.dat'          }
    {'102.dat'          }
    {'103.dat'          }
    {'104.dat'          }
    {'105.dat'          }
    {'106.dat'          }
    {'107.dat'          }
    {'108.dat'          }
    {'109.dat'          }
    {'111.dat'          }
    {'112.dat'          }
    {'113.dat'          }
    {'114.dat'          }
    {'115.dat'          }
    {'116.dat'          }
    {'117.dat'          }
    {'118.dat'          }
    {'119.dat'          }
    {'121.dat'          }
    {'122.dat'          }
    {'123.dat'          }
    {'124.dat'          }
    {'200.dat'          }
    {'201.dat'          }
    {'202.dat'          }
    {'203.dat'          }
    {'205.dat'          }
    {'207.dat'          }
    {'208.dat'          }
    {'209.dat'          }
    {'210.dat'          }
    {'212.dat'          }
    {'213.dat'          }
    {'214.dat'          }
    {'215.dat'          }
    {'217.dat'          }
    {'219.dat'          }
    {'220.dat'          }
    {'221.dat'          }
    {'222.dat'          }
    {'223.dat'          }
    {'228.dat'          }
    {'230.dat'          }
    {'231.dat'          }
    {'232.dat'          }
    {'233.dat'          }
    {'234.dat'          }
    {'x_mitdb/x_108.dat'}
    {'x_mitdb/x_109.dat'}
    {'x_mitdb/x_111.dat'}
    {'x_mitdb/x_112.dat'}
    {'x_mitdb/x_113.dat'}
    {'x_mitdb/x_114.dat'}
    {'x_mitdb/x_115.dat'}
    {'x_mitdb/x_116.dat'}
    {'x_mitdb/x_117.dat'}
    {'x_mitdb/x_121.dat'}
    {'x_mitdb/x_122.dat'}
    {'x_mitdb/x_123.dat'}
    {'x_mitdb/x_124.dat'}
    {'x_mitdb/x_220.dat'}
    {'x_mitdb/x_221.dat'}
    {'x_mitdb/x_222.dat'}
    {'x_mitdb/x_223.dat'}
    {'x_mitdb/x_228.dat'}
    {'x_mitdb/x_230.dat'}
    {'x_mitdb/x_231.dat'}
    {'x_mitdb/x_232.dat'}
    {'x_mitdb/x_233.dat'}
    {'x_mitdb/x_234.dat'}

后缀: .dvi, 共 1 个文件
    {'mitdbdir/src/title.dvi'}

后缀: .gz, 共 9 个文件
    {'mitdbdir/src/contents.gz'}
    {'mitdbdir/src/cover.gz'   }
    {'mitdbdir/src/extext.gz'  }
    {'mitdbdir/src/fdtext.gz'  }
    {'mitdbdir/src/index.gz'   }
    {'mitdbdir/src/intro.gz'   }
    {'mitdbdir/src/notes.gz'   }
    {'mitdbdir/src/tables.gz'  }
    {'mitdbdir/src/title.gz'   }

后缀: .h, 共 1 个文件
    {'mitdbdir/src/notes.h'}

后缀: .hea, 共 71 个文件
    {'100.hea'          }
    {'101.hea'          }
    {'102.hea'          }
    {'103.hea'          }
    {'104.hea'          }
    {'105.hea'          }
    {'106.hea'          }
    {'107.hea'          }
    {'108.hea'          }
    {'109.hea'          }
    {'111.hea'          }
    {'112.hea'          }
    {'113.hea'          }
    {'114.hea'          }
    {'115.hea'          }
    {'116.hea'          }
    {'117.hea'          }
    {'118.hea'          }
    {'119.hea'          }
    {'121.hea'          }
    {'122.hea'          }
    {'123.hea'          }
    {'124.hea'          }
    {'200.hea'          }
    {'201.hea'          }
    {'202.hea'          }
    {'203.hea'          }
    {'205.hea'          }
    {'207.hea'          }
    {'208.hea'          }
    {'209.hea'          }
    {'210.hea'          }
    {'212.hea'          }
    {'213.hea'          }
    {'214.hea'          }
    {'215.hea'          }
    {'217.hea'          }
    {'219.hea'          }
    {'220.hea'          }
    {'221.hea'          }
    {'222.hea'          }
    {'223.hea'          }
    {'228.hea'          }
    {'230.hea'          }
    {'231.hea'          }
    {'232.hea'          }
    {'233.hea'          }
    {'234.hea'          }
    {'x_mitdb/x_108.hea'}
    {'x_mitdb/x_109.hea'}
    {'x_mitdb/x_111.hea'}
    {'x_mitdb/x_112.hea'}
    {'x_mitdb/x_113.hea'}
    {'x_mitdb/x_114.hea'}
    {'x_mitdb/x_115.hea'}
    {'x_mitdb/x_116.hea'}
    {'x_mitdb/x_117.hea'}
    {'x_mitdb/x_121.hea'}
    {'x_mitdb/x_122.hea'}
    {'x_mitdb/x_123.hea'}
    {'x_mitdb/x_124.hea'}
    {'x_mitdb/x_220.hea'}
    {'x_mitdb/x_221.hea'}
    {'x_mitdb/x_222.hea'}
    {'x_mitdb/x_223.hea'}
    {'x_mitdb/x_228.hea'}
    {'x_mitdb/x_230.hea'}
    {'x_mitdb/x_231.hea'}
    {'x_mitdb/x_232.hea'}
    {'x_mitdb/x_233.hea'}
    {'x_mitdb/x_234.hea'}

后缀: .htm, 共 5 个文件
    {'mitdbdir/foreword.htm'}
    {'mitdbdir/intro.htm'   }
    {'mitdbdir/mitdbdir.htm'}
    {'mitdbdir/records.htm' }
    {'mitdbdir/tables.htm'  }

后缀: .pl, 共 1 个文件
    {'mitdbdir/src/makelinks.pl'}

后缀: .tr, 共 6 个文件
    {'mitdbdir/src/contents.tr'}
    {'mitdbdir/src/cover.tr'   }
    {'mitdbdir/src/extext.tr'  }
    {'mitdbdir/src/fdtext.tr'  }
    {'mitdbdir/src/index.tr'   }
    {'mitdbdir/src/intro.tr'   }

后缀: .xws, 共 439 个文件
    {'100.xws'                     }
    {'101.xws'                     }
    {'102.xws'                     }
    {'103.xws'                     }
    {'104.xws'                     }
    {'105.xws'                     }
    {'106.xws'                     }
    {'107.xws'                     }
    {'108.xws'                     }
    {'109.xws'                     }
    {'111.xws'                     }
    {'112.xws'                     }
    {'113.xws'                     }
    {'114.xws'                     }
    {'115.xws'                     }
    {'116.xws'                     }
    {'117.xws'                     }
    {'118.xws'                     }
    {'119.xws'                     }
    {'121.xws'                     }
    {'122.xws'                     }
    {'123.xws'                     }
    {'124.xws'                     }
    {'200.xws'                     }
    {'201.xws'                     }
    {'202.xws'                     }
    {'203.xws'                     }
    {'205.xws'                     }
    {'207.xws'                     }
    {'208.xws'                     }
    {'209.xws'                     }
    {'210.xws'                     }
    {'212.xws'                     }
    {'213.xws'                     }
    {'214.xws'                     }
    {'215.xws'                     }
    {'217.xws'                     }
    {'219.xws'                     }
    {'220.xws'                     }
    {'221.xws'                     }
    {'222.xws'                     }
    {'223.xws'                     }
    {'228.xws'                     }
    {'230.xws'                     }
    {'231.xws'                     }
    {'232.xws'                     }
    {'233.xws'                     }
    {'234.xws'                     }
    {'mitdbdir/samples/1001103.xws'}
    {'mitdbdir/samples/1002513.xws'}
    {'mitdbdir/samples/1002609.xws'}
    {'mitdbdir/samples/1002755.xws'}
    {'mitdbdir/samples/1010134.xws'}
    {'mitdbdir/samples/1010148.xws'}
    {'mitdbdir/samples/1010513.xws'}
    {'mitdbdir/samples/1010954.xws'}
    {'mitdbdir/samples/1012432.xws'}
    {'mitdbdir/samples/1020055.xws'}
    {'mitdbdir/samples/1020112.xws'}
    {'mitdbdir/samples/1020128.xws'}
    {'mitdbdir/samples/1020230.xws'}
    {'mitdbdir/samples/1020451.xws'}
    {'mitdbdir/samples/1020935.xws'}
    {'mitdbdir/samples/1021612.xws'}
    {'mitdbdir/samples/1030109.xws'}
    {'mitdbdir/samples/1031721.xws'}
    {'mitdbdir/samples/1031915.xws'}
    {'mitdbdir/samples/1032213.xws'}
    {'mitdbdir/samples/1032333.xws'}
    {'mitdbdir/samples/1032858.xws'}
    {'mitdbdir/samples/1040342.xws'}
    {'mitdbdir/samples/1040513.xws'}
    {'mitdbdir/samples/1040552.xws'}
    {'mitdbdir/samples/1040617.xws'}
    {'mitdbdir/samples/1040822.xws'}
    {'mitdbdir/samples/1042651.xws'}
    {'mitdbdir/samples/1042910.xws'}
    {'mitdbdir/samples/1050527.xws'}
    {'mitdbdir/samples/1050757.xws'}
    {'mitdbdir/samples/1051516.xws'}
    {'mitdbdir/samples/1051752.xws'}
    {'mitdbdir/samples/1052202.xws'}
    {'mitdbdir/samples/1052645.xws'}
    {'mitdbdir/samples/1052727.xws'}
    {'mitdbdir/samples/1052808.xws'}
    {'mitdbdir/samples/1052907.xws'}
    {'mitdbdir/samples/1060019.xws'}
    {'mitdbdir/samples/1060137.xws'}
    {'mitdbdir/samples/1060253.xws'}
    {'mitdbdir/samples/1060423.xws'}
    {'mitdbdir/samples/1060757.xws'}
    {'mitdbdir/samples/1061052.xws'}
    {'mitdbdir/samples/1061227.xws'}
    {'mitdbdir/samples/1061617.xws'}
    {'mitdbdir/samples/1062513.xws'}
    {'mitdbdir/samples/1062552.xws'}
    {'mitdbdir/samples/1070044.xws'}
    {'mitdbdir/samples/1071230.xws'}
    {'mitdbdir/samples/1071954.xws'}
    {'mitdbdir/samples/1072038.xws'}
    {'mitdbdir/samples/1072552.xws'}
    {'mitdbdir/samples/1080022.xws'}
    {'mitdbdir/samples/1080451.xws'}
    {'mitdbdir/samples/1080741.xws'}
    {'mitdbdir/samples/1080813.xws'}
    {'mitdbdir/samples/1081055.xws'}
    {'mitdbdir/samples/1081808.xws'}
    {'mitdbdir/samples/1082005.xws'}
    {'mitdbdir/samples/1082420.xws'}
    {'mitdbdir/samples/1082810.xws'}
    {'mitdbdir/samples/1082900.xws'}
    {'mitdbdir/samples/1090013.xws'}
    {'mitdbdir/samples/1090128.xws'}
    {'mitdbdir/samples/1090336.xws'}
    {'mitdbdir/samples/1090446.xws'}
    {'mitdbdir/samples/1090527.xws'}
    {'mitdbdir/samples/1091401.xws'}
    {'mitdbdir/samples/1091713.xws'}
    {'mitdbdir/samples/1091921.xws'}
    {'mitdbdir/samples/1092609.xws'}
    {'mitdbdir/samples/1092803.xws'}
    {'mitdbdir/samples/1092828.xws'}
    {'mitdbdir/samples/1092910.xws'}
    {'mitdbdir/samples/1110025.xws'}
    {'mitdbdir/samples/1110241.xws'}
    {'mitdbdir/samples/1110352.xws'}
    {'mitdbdir/samples/1110404.xws'}
    {'mitdbdir/samples/1110831.xws'}
    {'mitdbdir/samples/1111153.xws'}
    {'mitdbdir/samples/1111541.xws'}
    {'mitdbdir/samples/1111954.xws'}
    {'mitdbdir/samples/1112702.xws'}
    {'mitdbdir/samples/1121140.xws'}
    {'mitdbdir/samples/1121230.xws'}
    {'mitdbdir/samples/1121249.xws'}
    {'mitdbdir/samples/1130420.xws'}
    {'mitdbdir/samples/1130822.xws'}
    {'mitdbdir/samples/1131148.xws'}
    {'mitdbdir/samples/1131227.xws'}
    {'mitdbdir/samples/1132210.xws'}
    {'mitdbdir/samples/1132901.xws'}
    {'mitdbdir/samples/1140000.xws'}
    {'mitdbdir/samples/1140120.xws'}
    {'mitdbdir/samples/1140339.xws'}
    {'mitdbdir/samples/1140356.xws'}
    {'mitdbdir/samples/1140435.xws'}
    {'mitdbdir/samples/1140536.xws'}
    {'mitdbdir/samples/1140831.xws'}
    {'mitdbdir/samples/1140926.xws'}
    {'mitdbdir/samples/1141137.xws'}
    {'mitdbdir/samples/1142002.xws'}
    {'mitdbdir/samples/1142929.xws'}
    {'mitdbdir/samples/1150055.xws'}
    {'mitdbdir/samples/1150322.xws'}
    {'mitdbdir/samples/1151552.xws'}
    {'mitdbdir/samples/1152128.xws'}
    {'mitdbdir/samples/1152705.xws'}
    {'mitdbdir/samples/1152842.xws'}
    {'mitdbdir/samples/1160044.xws'}
    {'mitdbdir/samples/1160131.xws'}
    {'mitdbdir/samples/1161000.xws'}
    {'mitdbdir/samples/1161232.xws'}
    {'mitdbdir/samples/1161637.xws'}
    {'mitdbdir/samples/1162308.xws'}
    {'mitdbdir/samples/1162547.xws'}
    {'mitdbdir/samples/1170356.xws'}
    {'mitdbdir/samples/1171158.xws'}
    {'mitdbdir/samples/1172227.xws'}
    {'mitdbdir/samples/1180339.xws'}
    {'mitdbdir/samples/1180831.xws'}
    {'mitdbdir/samples/1180923.xws'}
    {'mitdbdir/samples/1181347.xws'}
    {'mitdbdir/samples/1182232.xws'}
    {'mitdbdir/samples/1182325.xws'}
    {'mitdbdir/samples/1182541.xws'}
    {'mitdbdir/samples/1182623.xws'}
    {'mitdbdir/samples/1182648.xws'}
    {'mitdbdir/samples/1182858.xws'}
    {'mitdbdir/samples/1182953.xws'}
    {'mitdbdir/samples/1190155.xws'}
    {'mitdbdir/samples/1190238.xws'}
    {'mitdbdir/samples/1190451.xws'}
    {'mitdbdir/samples/1190842.xws'}
    {'mitdbdir/samples/1192005.xws'}
    {'mitdbdir/samples/1192533.xws'}
    {'mitdbdir/samples/1210106.xws'}
    {'mitdbdir/samples/1210333.xws'}
    {'mitdbdir/samples/1211648.xws'}
    {'mitdbdir/samples/1212432.xws'}
    {'mitdbdir/samples/1212601.xws'}
    {'mitdbdir/samples/1220109.xws'}
    {'mitdbdir/samples/1221347.xws'}
    {'mitdbdir/samples/1222825.xws'}
    {'mitdbdir/samples/1232210.xws'}
    {'mitdbdir/samples/1232511.xws'}
    {'mitdbdir/samples/1232741.xws'}
    {'mitdbdir/samples/1232836.xws'}
    {'mitdbdir/samples/1240443.xws'}
    {'mitdbdir/samples/1240509.xws'}
    {'mitdbdir/samples/1240521.xws'}
    {'mitdbdir/samples/1240905.xws'}
    {'mitdbdir/samples/1241027.xws'}
    {'mitdbdir/samples/1241738.xws'}
    {'mitdbdir/samples/1242013.xws'}
    {'mitdbdir/samples/1242322.xws'}
    {'mitdbdir/samples/1242603.xws'}
    {'mitdbdir/samples/1242741.xws'}
    {'mitdbdir/samples/2000142.xws'}
    {'mitdbdir/samples/2000538.xws'}
    {'mitdbdir/samples/2001814.xws'}
    {'mitdbdir/samples/2002052.xws'}
    {'mitdbdir/samples/2002449.xws'}
    {'mitdbdir/samples/2002612.xws'}
    {'mitdbdir/samples/2002831.xws'}
    {'mitdbdir/samples/2002901.xws'}
    {'mitdbdir/samples/2002918.xws'}
    {'mitdbdir/samples/2002951.xws'}
    {'mitdbdir/samples/2010000.xws'}
    {'mitdbdir/samples/2010615.xws'}
    {'mitdbdir/samples/2010752.xws'}
    {'mitdbdir/samples/2010853.xws'}
    {'mitdbdir/samples/2011814.xws'}
    {'mitdbdir/samples/2012016.xws'}
    {'mitdbdir/samples/2012233.xws'}
    {'mitdbdir/samples/2012350.xws'}
    {'mitdbdir/samples/2012415.xws'}
    {'mitdbdir/samples/2012519.xws'}
    {'mitdbdir/samples/2012620.xws'}
    {'mitdbdir/samples/2012803.xws'}
    {'mitdbdir/samples/2021016.xws'}
    {'mitdbdir/samples/2021224.xws'}
    {'mitdbdir/samples/2021241.xws'}
    {'mitdbdir/samples/2021822.xws'}
    {'mitdbdir/samples/2021845.xws'}
    {'mitdbdir/samples/2021859.xws'}
    {'mitdbdir/samples/2022110.xws'}
    {'mitdbdir/samples/2022126.xws'}
    {'mitdbdir/samples/2022213.xws'}
    {'mitdbdir/samples/2022558.xws'}
    {'mitdbdir/samples/2022755.xws'}
    {'mitdbdir/samples/2022935.xws'}
    {'mitdbdir/samples/2030500.xws'}
    {'mitdbdir/samples/2031314.xws'}
    {'mitdbdir/samples/2031502.xws'}
    {'mitdbdir/samples/2032202.xws'}
    {'mitdbdir/samples/2032325.xws'}
    {'mitdbdir/samples/2032404.xws'}
    {'mitdbdir/samples/2032446.xws'}
    {'mitdbdir/samples/2032639.xws'}
    {'mitdbdir/samples/2032651.xws'}
    {'mitdbdir/samples/2032715.xws'}
    {'mitdbdir/samples/2050457.xws'}
    {'mitdbdir/samples/2051454.xws'}
    {'mitdbdir/samples/2051522.xws'}
    {'mitdbdir/samples/2051603.xws'}
    {'mitdbdir/samples/2051615.xws'}
    {'mitdbdir/samples/2051957.xws'}
    {'mitdbdir/samples/2052418.xws'}
    {'mitdbdir/samples/2052430.xws'}
    {'mitdbdir/samples/2052757.xws'}
    {'mitdbdir/samples/2070000.xws'}
    {'mitdbdir/samples/2070033.xws'}
    {'mitdbdir/samples/2070045.xws'}
    {'mitdbdir/samples/2070057.xws'}
    {'mitdbdir/samples/2070128.xws'}
    {'mitdbdir/samples/2070207.xws'}
    {'mitdbdir/samples/2070241.xws'}
    {'mitdbdir/samples/2070425.xws'}
    {'mitdbdir/samples/2070519.xws'}
    {'mitdbdir/samples/2070656.xws'}
    {'mitdbdir/samples/2071735.xws'}
    {'mitdbdir/samples/2072536.xws'}
    {'mitdbdir/samples/2072610.xws'}
    {'mitdbdir/samples/2072714.xws'}
    {'mitdbdir/samples/2072910.xws'}
    {'mitdbdir/samples/2081457.xws'}
    {'mitdbdir/samples/2081702.xws'}
    {'mitdbdir/samples/2081910.xws'}
    {'mitdbdir/samples/2082106.xws'}
    {'mitdbdir/samples/2082300.xws'}
    {'mitdbdir/samples/2082808.xws'}
    {'mitdbdir/samples/2082858.xws'}
    {'mitdbdir/samples/2082937.xws'}
    {'mitdbdir/samples/2090047.xws'}
    {'mitdbdir/samples/2090550.xws'}
    {'mitdbdir/samples/2091257.xws'}
    {'mitdbdir/samples/2091421.xws'}
    {'mitdbdir/samples/2092325.xws'}
    {'mitdbdir/samples/2092817.xws'}
    {'mitdbdir/samples/2092833.xws'}
    {'mitdbdir/samples/2092910.xws'}
    {'mitdbdir/samples/2100347.xws'}
    {'mitdbdir/samples/2100656.xws'}
    {'mitdbdir/samples/2101314.xws'}
    {'mitdbdir/samples/2101555.xws'}
    {'mitdbdir/samples/2101732.xws'}
    {'mitdbdir/samples/2101757.xws'}
    {'mitdbdir/samples/2102033.xws'}
    {'mitdbdir/samples/2102915.xws'}
    {'mitdbdir/samples/2120005.xws'}
    {'mitdbdir/samples/2121842.xws'}
    {'mitdbdir/samples/2122421.xws'}
    {'mitdbdir/samples/2122438.xws'}
    {'mitdbdir/samples/2122645.xws'}
    {'mitdbdir/samples/2122846.xws'}
    {'mitdbdir/samples/2130339.xws'}
    {'mitdbdir/samples/2131448.xws'}
    {'mitdbdir/samples/2131505.xws'}
    {'mitdbdir/samples/2131538.xws'}
    {'mitdbdir/samples/2131730.xws'}
    {'mitdbdir/samples/2131755.xws'}
    {'mitdbdir/samples/2132443.xws'}
    {'mitdbdir/samples/2132558.xws'}
    {'mitdbdir/samples/2132651.xws'}
    {'mitdbdir/samples/2132856.xws'}
    {'mitdbdir/samples/2140030.xws'}
    {'mitdbdir/samples/2140221.xws'}
    {'mitdbdir/samples/2140235.xws'}
    {'mitdbdir/samples/2140325.xws'}
    {'mitdbdir/samples/2140356.xws'}
    {'mitdbdir/samples/2140508.xws'}
    {'mitdbdir/samples/2140538.xws'}
    {'mitdbdir/samples/2141353.xws'}
    {'mitdbdir/samples/2142317.xws'}
    {'mitdbdir/samples/2142752.xws'}
    {'mitdbdir/samples/2150255.xws'}
    {'mitdbdir/samples/2150311.xws'}
    {'mitdbdir/samples/2150946.xws'}
    {'mitdbdir/samples/2151558.xws'}
    {'mitdbdir/samples/2152025.xws'}
    {'mitdbdir/samples/2152216.xws'}
    {'mitdbdir/samples/2152457.xws'}
    {'mitdbdir/samples/2152730.xws'}
    {'mitdbdir/samples/2152757.xws'}
    {'mitdbdir/samples/2152942.xws'}
    {'mitdbdir/samples/2170000.xws'}
    {'mitdbdir/samples/2170033.xws'}
    {'mitdbdir/samples/2170123.xws'}
    {'mitdbdir/samples/2170418.xws'}
    {'mitdbdir/samples/2170612.xws'}
    {'mitdbdir/samples/2170651.xws'}
    {'mitdbdir/samples/2170705.xws'}
    {'mitdbdir/samples/2171227.xws'}
    {'mitdbdir/samples/2171401.xws'}
    {'mitdbdir/samples/2171530.xws'}
    {'mitdbdir/samples/2171656.xws'}
    {'mitdbdir/samples/2172227.xws'}
    {'mitdbdir/samples/2190249.xws'}
    {'mitdbdir/samples/2190508.xws'}
    {'mitdbdir/samples/2190522.xws'}
    {'mitdbdir/samples/2191350.xws'}
    {'mitdbdir/samples/2191900.xws'}
    {'mitdbdir/samples/2191957.xws'}
    {'mitdbdir/samples/2192211.xws'}
    {'mitdbdir/samples/2192243.xws'}
    {'mitdbdir/samples/2192443.xws'}
    {'mitdbdir/samples/2192609.xws'}
    {'mitdbdir/samples/2192855.xws'}
    {'mitdbdir/samples/2200646.xws'}
    {'mitdbdir/samples/2201041.xws'}
    {'mitdbdir/samples/2201810.xws'}
    {'mitdbdir/samples/2202029.xws'}
    {'mitdbdir/samples/2202358.xws'}
    {'mitdbdir/samples/2202544.xws'}
    {'mitdbdir/samples/2202940.xws'}
    {'mitdbdir/samples/2210000.xws'}
    {'mitdbdir/samples/2210243.xws'}
    {'mitdbdir/samples/2211300.xws'}
    {'mitdbdir/samples/2211356.xws'}
    {'mitdbdir/samples/2211749.xws'}
    {'mitdbdir/samples/2211912.xws'}
    {'mitdbdir/samples/2211942.xws'}
    {'mitdbdir/samples/2212044.xws'}
    {'mitdbdir/samples/2220645.xws'}
    {'mitdbdir/samples/2221732.xws'}
    {'mitdbdir/samples/2221948.xws'}
    {'mitdbdir/samples/2222007.xws'}
    {'mitdbdir/samples/2222232.xws'}
    {'mitdbdir/samples/2222443.xws'}
    {'mitdbdir/samples/2222543.xws'}
    {'mitdbdir/samples/2222609.xws'}
    {'mitdbdir/samples/2230935.xws'}
    {'mitdbdir/samples/2231235.xws'}
    {'mitdbdir/samples/2231339.xws'}
    {'mitdbdir/samples/2231351.xws'}
    {'mitdbdir/samples/2231721.xws'}
    {'mitdbdir/samples/2231755.xws'}
    {'mitdbdir/samples/2231907.xws'}
    {'mitdbdir/samples/2232358.xws'}
    {'mitdbdir/samples/2232547.xws'}
    {'mitdbdir/samples/2232916.xws'}
    {'mitdbdir/samples/2232951.xws'}
    {'mitdbdir/samples/2280019.xws'}
    {'mitdbdir/samples/2280038.xws'}
    {'mitdbdir/samples/2280050.xws'}
    {'mitdbdir/samples/2280435.xws'}
    {'mitdbdir/samples/2281918.xws'}
    {'mitdbdir/samples/2282008.xws'}
    {'mitdbdir/samples/2282137.xws'}
    {'mitdbdir/samples/2282149.xws'}
    {'mitdbdir/samples/2282642.xws'}
    {'mitdbdir/samples/2300000.xws'}
    {'mitdbdir/samples/2300012.xws'}
    {'mitdbdir/samples/2300511.xws'}
    {'mitdbdir/samples/2300926.xws'}
    {'mitdbdir/samples/2302904.xws'}
    {'mitdbdir/samples/2310002.xws'}
    {'mitdbdir/samples/2310224.xws'}
    {'mitdbdir/samples/2312011.xws'}
    {'mitdbdir/samples/2312110.xws'}
    {'mitdbdir/samples/2312322.xws'}
    {'mitdbdir/samples/2320058.xws'}
    {'mitdbdir/samples/2320151.xws'}
    {'mitdbdir/samples/2321151.xws'}
    {'mitdbdir/samples/2321306.xws'}
    {'mitdbdir/samples/2321509.xws'}
    {'mitdbdir/samples/2321817.xws'}
    {'mitdbdir/samples/2322231.xws'}
    {'mitdbdir/samples/2322247.xws'}
    {'mitdbdir/samples/2322320.xws'}
    {'mitdbdir/samples/2322334.xws'}
    {'mitdbdir/samples/2322353.xws'}
    {'mitdbdir/samples/2330011.xws'}
    {'mitdbdir/samples/2330025.xws'}
    {'mitdbdir/samples/2330218.xws'}
    {'mitdbdir/samples/2330243.xws'}
    {'mitdbdir/samples/2330339.xws'}
    {'mitdbdir/samples/2330502.xws'}
    {'mitdbdir/samples/2330943.xws'}
    {'mitdbdir/samples/2331620.xws'}
    {'mitdbdir/samples/2331802.xws'}
    {'mitdbdir/samples/2332223.xws'}
    {'mitdbdir/samples/2340016.xws'}
    {'mitdbdir/samples/2340637.xws'}
    {'mitdbdir/samples/2341401.xws'}
    {'mitdbdir/samples/2341426.xws'}
    {'mitdbdir/samples/2341702.xws'}
    {'mitdbdir/samples/2342126.xws'}
    {'mitdbdir/samples/2342317.xws'}

后缀: 无扩展名, 共 10 个文件
    {'ANNOTATORS'                }
    {'RECORDS'                   }
    {'mitdbdir/src/domit'        }
    {'mitdbdir/src/exlist'       }
    {'mitdbdir/src/extest'       }
    {'mitdbdir/src/fdlist'       }
    {'mitdbdir/src/makefile'     }
    {'mitdbdir/src/makefile-html'}
    {'x_mitdb/ANNOTATORS'        }
    {'x_mitdb/RECORDS'           }

但我发现一个问题:官方称含48 个充分标注的30分钟长度2导联ECG,但我们可以看出其有71个data文件。多了23个,具体是前缀多了个x,如:x_234.dat和234.dat,但没有资料说明有什么具体区别。

通过可视化观察一下:

Matlab 复制代码
% 脚本:compare_x234_234.m
% 比较 x_234 和 234 记录经过 readdata 处理后的心电图信号
clear; close all;

% 数据文件夹路径
dataFolder = 'D:\ECG-Tran\mit-bih-arrhythmia-database\';
% 确保文件夹以文件分隔符结尾
if ~endsWith(dataFolder, filesep)
    dataFolder = [dataFolder, filesep];
end

recordA = '234';
recordB = 'x_234';

fprintf('正在读取记录 %s ...\n', recordA);
try
    [sigA, FsA] = readdata(recordA, dataFolder);
catch ME_A
    warning('readdata 读取 %s 失败,改为直接读取原始信号。错误信息:%s', recordA, ME_A.message);
    % 回退:读取原始完整信号
    oldFolder = cd(dataFolder);
    [sigA_raw, FsA, ~] = rdsamp(recordA);
    cd(oldFolder);
    sigA = sigA_raw;  % 不进行任何预处理
end

fprintf('正在读取记录 %s ...\n', recordB);
try
    [sigB, FsB] = readdata(recordB, dataFolder);
catch ME_B
    warning('readdata 读取 %s 失败,改为直接读取原始信号。错误信息:%s', recordB, ME_B.message);
    oldFolder = cd(dataFolder);
    [sigB_raw, FsB, ~] = rdsamp(recordB);
    cd(oldFolder);
    sigB = sigB_raw;
end

% 检查采样率是否一致
if FsA ~= FsB
    error('两个记录的采样率不一致,无法直接比较。');
end

Fs = FsA;

% 让比较窗口对齐:取两者中较短的信号长度
minLen = min(size(sigA,1), size(sigB,1));
sigA = sigA(1:minLen, :);
sigB = sigB(1:minLen, :);

% 生成统一的时间轴(单位:秒)
t = (0:minLen-1) / Fs;

% 只显示前 10 秒的数据,便于观察细节
viewSec = 10;
viewSamples = min(round(viewSec * Fs), minLen);
tv = t(1:viewSamples);

% ---- 可视化 ----
figure('Name', '信号对比:234 vs x_234', 'Position', [100, 100, 1200, 800]);

% 1. 第一导联叠加对比
subplot(3,1,1);
plot(tv, sigA(1:viewSamples, 1), 'b-', 'LineWidth', 1.2); hold on;
plot(tv, sigB(1:viewSamples, 1), 'r--', 'LineWidth', 1.2);
xlabel('时间 (秒)'); ylabel('幅值 (mV)');
title(sprintf('导联 1 前 %d 秒(蓝: 234, 红: x_234)', viewSec));
legend('234', 'x_234');
grid on;

% 2. 第一导联差异信号
diff_lead1 = sigA(1:viewSamples, 1) - sigB(1:viewSamples, 1);
subplot(3,1,2);
plot(tv, diff_lead1, 'k-', 'LineWidth', 1);
xlabel('时间 (秒)'); ylabel('差异 (mV)');
title('导联 1 差异 (234 - x_234)');
grid on;
% 添加零线
hold on; plot([tv(1), tv(end)], [0 0], 'Color', [0.6 0.6 0.6]);

% 3. 第二导联叠加对比
subplot(3,1,3);
plot(tv, sigA(1:viewSamples, 2), 'b-', 'LineWidth', 1.2); hold on;
plot(tv, sigB(1:viewSamples, 2), 'r--', 'LineWidth', 1.2);
xlabel('时间 (秒)'); ylabel('幅值 (mV)');
title(sprintf('导联 2 前 %d 秒(蓝: 234, 红: x_234)', viewSec));
legend('234', 'x_234');
grid on;

sgtitle(sprintf('记录 %s 与 %s 经过 readdata 处理后的信号对比', recordA, recordB));

似乎没差别,我自己也看了其它信号并且放大范围,可以看出信号本身并无明显差异。

考虑到这里的问题主要是心律失常arrythmia,所以我们统计一下各个标签的数量:

Matlab 复制代码
%% 总结各个文件中的注释标签类型及数量(基于 ASCII 字符映射)
clear; clc;

dataFolder = 'D:\ECG-Tran\mit-bih-arrhythmia-database'; 
recordNames = {
    '100', '101', '102', '103', '104', '105', '106', '107', '108', '109', ...
    '111', '112', '113', '114', '115', '116', '117', '118', '119', ...
    '121', '122', '123', '124', ...
    '200', '201', '202', '203', '205', '207', '208', '209', '210', ...
    '212', '213', '214', '215', '217', '219', '220', '221', '222', '223', ...
    '228', '230', '231', '232', '233', '234'
};

%% ========== 1. 构建映射表:字符 -> 描述 ==========
% 心搏类型 (Beat annotations)
beatMap = containers.Map();
beatMap('N') = '正常搏动';
beatMap('L') = '左束支传导阻滞搏动';
beatMap('R') = '右束支传导阻滞搏动';
beatMap('B') = '束支传导阻滞搏动(未指定)';
beatMap('A') = '房性早搏';
beatMap('a') = '异常房性早搏';
beatMap('J') = '交界性早搏';
beatMap('S') = '室上性早搏或异位搏动(房性或交界性)';
beatMap('V') = '室性早搏';
beatMap('r') = 'R-on-T 室性早搏';
beatMap('F') = '心室融合搏动(室性与正常融合)';
beatMap('e') = '房性逸搏';
beatMap('j') = '交界性逸搏';
beatMap('n') = '室上性逸搏(房性或交界性)';
beatMap('E') = '室性逸搏';
beatMap('/') = '起搏搏动';
beatMap('f') = '起搏与正常融合搏动';
beatMap('Q') = '不可分类搏动';
beatMap('?') = '学习期间未分类搏动';

% 非心搏注释 (Non-beat annotations)
nonBeatMap = containers.Map();
nonBeatMap('[') = '室扑/室颤开始';
nonBeatMap('!') = '室扑波';
nonBeatMap(']') = '室扑/室颤结束';
nonBeatMap('x') = '未传导P波 (阻滞的房性早搏)';
nonBeatMap('(') = '波形开始';
nonBeatMap(')') = '波形结束';
nonBeatMap('p') = 'P波峰值';
nonBeatMap('t') = 'T波峰值';
nonBeatMap('u') = 'U波峰值';
nonBeatMap('`') = 'PQ 连接处';
nonBeatMap(',') = 'J点';
nonBeatMap('~') = '(未夺获)起搏器伪迹 或 信号质量变化';  % 有两种用途
nonBeatMap('|') = '孤立的 QRS 样伪迹';
nonBeatMap('+') = '节律变化';
nonBeatMap('s') = 'ST段变化';
nonBeatMap('T') = 'T波变化';
nonBeatMap('*') = '收缩期';
nonBeatMap('D') = '舒张期';
nonBeatMap('=') = '测量注释';
nonBeatMap('"') = '评论注释';
nonBeatMap('@') = '指向外部数据的链接';

% 合并总映射(优先使用 beatMap,若不存在再查 nonBeatMap)
allMaps = containers.Map();
keys = [beatMap.keys, nonBeatMap.keys];
for i = 1:length(keys)
    key = keys{i};
    if beatMap.isKey(key)
        allMaps(key) = beatMap(key);
    elseif nonBeatMap.isKey(key)
        allMaps(key) = nonBeatMap(key);
    end
end

%% ========== 2. 处理每个记录 ==========
fprintf('%-8s | 标签类型及数量\n', '记录');
fprintf('%s\n', repmat('-', 100, 1));

for i = 1:length(recordNames)
    recName = recordNames{i};
    
    oldFolder = cd(dataFolder);
    try
        [~, atype] = rdann(recName, 'atr');  % atype 为数值数组,每个数值是 ASCII 码
    catch
        fprintf('%-8s | 无法读取注释\n', recName);
        cd(oldFolder);
        continue;
    end
    cd(oldFolder);
    
    if isempty(atype)
        fprintf('%-8s | 无注释\n', recName);
        continue;
    end
    
    % 将 ASCII 码转换为字符,组成字符串数组
    chars = cellfun(@(x) char(x), num2cell(atype), 'UniformOutput', false);
    
    % 统计每个字符出现的次数
    [uniqueChars, ~, idx] = unique(chars);
    counts = accumarray(idx, 1);
    
    % 生成输出字符串
    infoParts = {};
    for j = 1:length(uniqueChars)
        ch = uniqueChars{j};
        count = counts(j);
        if allMaps.isKey(ch)
            desc = allMaps(ch);
            infoParts{end+1} = sprintf('''%s'' (%s): %d个', ch, desc, count);
        else
            % 未知字符:显示 ASCII 码和十六进制
            asciiVal = double(ch);
            infoParts{end+1} = sprintf('''%s'' (0x%02X, 未知): %d个', ch, asciiVal, count);
        end
    end
    
    fprintf('%-8s | %s\n', recName, strjoin(infoParts, '; '));
end
Matlab 复制代码
记录       | 标签类型及数量
----------------------------------------------------------------------------------------------------
100      | '+' (节律变化): 1个; 'A' (房性早搏): 33个; 'N' (正常搏动): 2239个; 'V' (室性早搏): 1个
101      | '+' (节律变化): 1个; 'A' (房性早搏): 3个; 'N' (正常搏动): 1860个; 'Q' (不可分类搏动): 2个; '|' (孤立的 QRS 样伪迹): 4个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 4个
102      | '+' (节律变化): 5个; '/' (起搏搏动): 2028个; 'N' (正常搏动): 99个; 'V' (室性早搏): 4个; 'f' (起搏与正常融合搏动): 56个
103      | '+' (节律变化): 1个; 'A' (房性早搏): 2个; 'N' (正常搏动): 2082个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 6个
104      | '+' (节律变化): 45个; '/' (起搏搏动): 1380个; 'N' (正常搏动): 163个; 'Q' (不可分类搏动): 18个; 'V' (室性早搏): 2个; 'f' (起搏与正常融合搏动): 666个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 37个
105      | '+' (节律变化): 1个; 'N' (正常搏动): 2526个; 'Q' (不可分类搏动): 5个; 'V' (室性早搏): 41个; '|' (孤立的 QRS 样伪迹): 30个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 88个
106      | '+' (节律变化): 41个; 'N' (正常搏动): 1507个; 'V' (室性早搏): 520个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 30个
107      | '+' (节律变化): 1个; '/' (起搏搏动): 2078个; 'V' (室性早搏): 59个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 2个
108      | '+' (节律变化): 1个; 'A' (房性早搏): 4个; 'F' (心室融合搏动(室性与正常融合)): 2个; 'N' (正常搏动): 1739个; 'V' (室性早搏): 17个; 'j' (交界性逸搏): 1个; 'x' (未传导P波 (阻滞的房性早搏)): 11个; '|' (孤立的 QRS 样伪迹): 8个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 41个
109      | '+' (节律变化): 1个; 'F' (心室融合搏动(室性与正常融合)): 2个; 'L' (左束支传导阻滞搏动): 2492个; 'V' (室性早搏): 38个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 2个
111      | '+' (节律变化): 1个; 'L' (左束支传导阻滞搏动): 2123个; 'V' (室性早搏): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 8个
112      | '+' (节律变化): 1个; 'A' (房性早搏): 2个; 'N' (正常搏动): 2537个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 10个
113      | '+' (节律变化): 1个; 'N' (正常搏动): 1789个; 'a' (异常房性早搏): 6个
114      | '+' (节律变化): 3个; 'A' (房性早搏): 10个; 'F' (心室融合搏动(室性与正常融合)): 4个; 'J' (交界性早搏): 2个; 'N' (正常搏动): 1820个; 'V' (室性早搏): 43个; '|' (孤立的 QRS 样伪迹): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 7个
115      | '+' (节律变化): 1个; 'N' (正常搏动): 1953个; '|' (孤立的 QRS 样伪迹): 6个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 2个
116      | '+' (节律变化): 1个; 'A' (房性早搏): 1个; 'N' (正常搏动): 2302个; 'V' (室性早搏): 109个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 8个
117      | '+' (节律变化): 1个; 'A' (房性早搏): 1个; 'N' (正常搏动): 1534个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 3个
118      | '+' (节律变化): 1个; 'A' (房性早搏): 96个; 'R' (右束支传导阻滞搏动): 2166个; 'V' (室性早搏): 16个; 'x' (未传导P波 (阻滞的房性早搏)): 10个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 12个
119      | '+' (节律变化): 103个; 'N' (正常搏动): 1543个; 'V' (室性早搏): 444个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 4个
121      | '+' (节律变化): 1个; 'A' (房性早搏): 1个; 'N' (正常搏动): 1861个; 'V' (室性早搏): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 12个
122      | '+' (节律变化): 1个; 'N' (正常搏动): 2476个; '|' (孤立的 QRS 样伪迹): 2个
123      | '+' (节律变化): 1个; 'N' (正常搏动): 1515个; 'V' (室性早搏): 3个
124      | '+' (节律变化): 13个; 'A' (房性早搏): 2个; 'F' (心室融合搏动(室性与正常融合)): 5个; 'J' (交界性早搏): 29个; 'R' (右束支传导阻滞搏动): 1531个; 'V' (室性早搏): 47个; 'j' (交界性逸搏): 5个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 2个
200      | '+' (节律变化): 148个; 'A' (房性早搏): 30个; 'F' (心室融合搏动(室性与正常融合)): 2个; 'N' (正常搏动): 1743个; 'V' (室性早搏): 826个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 43个
201      | '+' (节律变化): 35个; 'A' (房性早搏): 30个; 'F' (心室融合搏动(室性与正常融合)): 2个; 'J' (交界性早搏): 1个; 'N' (正常搏动): 1625个; 'V' (室性早搏): 198个; 'a' (异常房性早搏): 97个; 'j' (交界性逸搏): 10个; 'x' (未传导P波 (阻滞的房性早搏)): 37个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 4个
202      | '+' (节律变化): 8个; 'A' (房性早搏): 36个; 'F' (心室融合搏动(室性与正常融合)): 1个; 'N' (正常搏动): 2061个; 'V' (室性早搏): 19个; 'a' (异常房性早搏): 19个; '|' (孤立的 QRS 样伪迹): 2个
203      | '+' (节律变化): 45个; 'F' (心室融合搏动(室性与正常融合)): 1个; 'N' (正常搏动): 2529个; 'Q' (不可分类搏动): 4个; 'V' (室性早搏): 444个; 'a' (异常房性早搏): 2个; '|' (孤立的 QRS 样伪迹): 26个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 57个
205      | '+' (节律变化): 13个; 'A' (房性早搏): 3个; 'F' (心室融合搏动(室性与正常融合)): 11个; 'N' (正常搏动): 2571个; 'V' (室性早搏): 71个; '|' (孤立的 QRS 样伪迹): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 2个
207      | '!' (室扑波): 472个; '+' (节律变化): 24个; 'A' (房性早搏): 107个; 'E' (室性逸搏): 105个; 'L' (左束支传导阻滞搏动): 1457个; 'R' (右束支传导阻滞搏动): 86个; 'V' (室性早搏): 105个; '[' (室扑/室颤开始): 6个; ']' (室扑/室颤结束): 6个; '|' (孤立的 QRS 样伪迹): 2个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 15个
208      | '+' (节律变化): 53个; 'F' (心室融合搏动(室性与正常融合)): 373个; 'N' (正常搏动): 1586个; 'Q' (不可分类搏动): 2个; 'S' (室上性早搏或异位搏动(房性或交界性)): 2个; 'V' (室性早搏): 992个; '|' (孤立的 QRS 样伪迹): 8个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 24个
209      | '+' (节律变化): 21个; 'A' (房性早搏): 383个; 'N' (正常搏动): 2621个; 'V' (室性早搏): 1个; '|' (孤立的 QRS 样伪迹): 7个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 19个
210      | '+' (节律变化): 17个; 'E' (室性逸搏): 1个; 'F' (心室融合搏动(室性与正常融合)): 10个; 'N' (正常搏动): 2423个; 'V' (室性早搏): 194个; 'a' (异常房性早搏): 22个; '|' (孤立的 QRS 样伪迹): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 17个
212      | '+' (节律变化): 1个; 'N' (正常搏动): 923个; 'R' (右束支传导阻滞搏动): 1825个; '|' (孤立的 QRS 样伪迹): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 13个
213      | '+' (节律变化): 43个; 'A' (房性早搏): 25个; 'F' (心室融合搏动(室性与正常融合)): 362个; 'N' (正常搏动): 2641个; 'V' (室性早搏): 220个; 'a' (异常房性早搏): 3个
214      | '"' (评论注释): 1个; '+' (节律变化): 25个; 'F' (心室融合搏动(室性与正常融合)): 1个; 'L' (左束支传导阻滞搏动): 2003个; 'Q' (不可分类搏动): 2个; 'V' (室性早搏): 256个; '|' (孤立的 QRS 样伪迹): 5个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 4个
215      | '"' (评论注释): 2个; '+' (节律变化): 5个; 'A' (房性早搏): 3个; 'F' (心室融合搏动(室性与正常融合)): 1个; 'N' (正常搏动): 3195个; 'V' (室性早搏): 164个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 30个
217      | '+' (节律变化): 67个; '/' (起搏搏动): 1542个; 'N' (正常搏动): 244个; 'V' (室性早搏): 162个; 'f' (起搏与正常融合搏动): 260个; '|' (孤立的 QRS 样伪迹): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 4个
219      | '"' (评论注释): 4个; '+' (节律变化): 21个; 'A' (房性早搏): 7个; 'F' (心室融合搏动(室性与正常融合)): 1个; 'N' (正常搏动): 2082个; 'V' (室性早搏): 64个; 'x' (未传导P波 (阻滞的房性早搏)): 133个
220      | '+' (节律变化): 16个; 'A' (房性早搏): 94个; 'N' (正常搏动): 1954个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 4个
221      | '+' (节律变化): 23个; 'N' (正常搏动): 2031个; 'V' (室性早搏): 396个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 12个
222      | '+' (节律变化): 136个; 'A' (房性早搏): 208个; 'J' (交界性早搏): 1个; 'N' (正常搏动): 2062个; 'j' (交界性逸搏): 212个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 15个
223      | '+' (节律变化): 28个; 'A' (房性早搏): 72个; 'F' (心室融合搏动(室性与正常融合)): 14个; 'N' (正常搏动): 2029个; 'V' (室性早搏): 473个; 'a' (异常房性早搏): 1个; 'e' (房性逸搏): 16个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 10个
228      | '"' (评论注释): 3个; '+' (节律变化): 41个; 'A' (房性早搏): 3个; 'N' (正常搏动): 1688个; 'V' (室性早搏): 362个; '|' (孤立的 QRS 样伪迹): 24个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 20个
230      | '+' (节律变化): 207个; 'N' (正常搏动): 2255个; 'V' (室性早搏): 1个; '|' (孤立的 QRS 样伪迹): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 2个
231      | '"' (评论注释): 427个; '+' (节律变化): 11个; 'A' (房性早搏): 1个; 'N' (正常搏动): 314个; 'R' (右束支传导阻滞搏动): 1254个; 'V' (室性早搏): 2个; 'x' (未传导P波 (阻滞的房性早搏)): 2个
232      | '+' (节律变化): 1个; 'A' (房性早搏): 1382个; 'R' (右束支传导阻滞搏动): 397个; 'j' (交界性逸搏): 1个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 35个
233      | '+' (节律变化): 71个; 'A' (房性早搏): 7个; 'F' (心室融合搏动(室性与正常融合)): 11个; 'N' (正常搏动): 2230个; 'V' (室性早搏): 831个; '|' (孤立的 QRS 样伪迹): 2个
234      | '+' (节律变化): 3个; 'J' (交界性早搏): 50个; 'N' (正常搏动): 2700个; 'V' (室性早搏): 3个; '~' ((未夺获)起搏器伪迹 或 信号质量变化): 8个

读取数据与降采样,该数据库是360Hz,为了配合我们之前的数据,我们可以选择将其降采样(需要将注释同步考虑):

Matlab 复制代码
function [sig, Fs, tm, annot_pos, annot_sym] = readdata(recordName, dataFolder, targetFs)
% 读取心电图信号,从第5秒开始裁切,进行去趋势和带通滤波,并降采样至目标频率
% 同时读取 .atr 注释,并将注释位置同步映射到降采样后的时间轴上
% 输入:
%   recordName - 记录名称 
%   dataFolder - 数据文件夹路径
%   targetFs   - 目标采样率 (Hz),默认 128
% 输出:
%   sig       - 处理后的双导联信号 [nSamples × 2]
%   Fs        - 目标采样率 (Hz)
%   tm        - 时间向量(从0开始,对应降采样后的信号)
%   annot_pos - 降采样后的注释样本索引 (列向量,与 annot_sym 对应)
%   annot_sym - 注释符号的 cell 数组 (如 {'N','V','A',...})

    if nargin < 3
        targetFs = 128;          % 默认目标采样率 128 Hz
    end

    oldFolder = cd(dataFolder);
    cleanup = onCleanup(@() cd(oldFolder));

    % ------------------ 读取信号 ------------------
    [sig_full, origFs] = rdsamp(recordName);
    if isempty(sig_full)
        error('无法读取记录 %s 的信号', recordName);
    end

    % 第5秒对应的原始样本起始索引 (1-based)
    start_second = 5;
    startSample = round(start_second * origFs) + 1;
    if startSample > size(sig_full, 1)
        error('第%d秒(样本 %d)超出信号长度(%d)', start_second, startSample, size(sig_full,1));
    end

    % 裁切信号
    sig_cropped = sig_full(startSample:end, :);
    fprintf('记录 %s:从第%d秒(样本 %d)开始裁切,剩余 %d 个样本\n', ...
        recordName, start_second, startSample, size(sig_cropped,1));

    % 去趋势 + 带通滤波 (0.5-40 Hz)
    sig_filtered = zeros(size(sig_cropped));
    nyquist = origFs / 2;
    [b, a] = butter(4, [0.5/nyquist, 40/nyquist], 'bandpass');
    for lead = 1:2
        signal_detrend = detrend(sig_cropped(:, lead));
        sig_filtered(:, lead) = filtfilt(b, a, signal_detrend);
    end

    % 降采样至目标采样率
    if origFs ~= targetFs
        [p, q] = rat(targetFs / origFs, 1e-12);
        fprintf('重采样:从 %.1f Hz 到 %.1f Hz (因子 %.6f)\n', origFs, targetFs, p/q);
        n_out = ceil(size(sig_filtered, 1) * p / q);
        sig = zeros(n_out, 2);
        for lead = 1:2
            sig(:, lead) = resample(sig_filtered(:, lead), p, q);
        end
        Fs = targetFs;
    else
        sig = sig_filtered;
        Fs = origFs;
    end
    % 时间向量 (秒,从0开始)
    tm = (0:size(sig,1)-1) / Fs;

    % ------------------ 读取并同步注释 ------------------
    annot_pos = [];
    annot_sym = {};
    try
        % 读取 atr 注释:ann_orig 为样本位置(0-based),anntype_orig 为 ASCII 码数值
        [ann_orig, anntype_orig] = rdann(recordName, 'atr');
        if isempty(ann_orig)
            fprintf('记录 %s 无注释数据\n', recordName);
            return;
        end

        % 转换为 1-based 索引,并转换 ASCII 码为字符
        orig_idx = ann_orig + 1;          % 转为 1-based
        sym_cell = cellfun(@(x) char(x), num2cell(anntype_orig), 'UniformOutput', false);

        % 只保留裁切起始点之后的注释
        keep = (orig_idx >= startSample);
        orig_idx = orig_idx(keep);
        sym_cell = sym_cell(keep);
        if isempty(orig_idx)
            fprintf('记录 %s 在第5秒后无注释\n', recordName);
            return;
        end

        % 计算裁切后的相对样本索引 (基于原始采样率)
        cropped_idx = orig_idx - startSample + 1;   % 1-based,相对于 sig_cropped
        % 裁切后的时间 (秒)
        t_cropped = (cropped_idx - 1) / origFs;

        % 映射到降采样后的样本索引 (四舍五入)
        new_idx = round(t_cropped * targetFs) + 1;
        % 剔除越界的索引
        valid = (new_idx >= 1) & (new_idx <= size(sig,1));
        annot_pos = new_idx(valid);
        annot_sym = sym_cell(valid);

        % 可选:去除重复位置上的多余注释(保留第一个)
        [annot_pos, unique_ia] = unique(annot_pos, 'stable');
        annot_sym = annot_sym(unique_ia);

        fprintf('成功同步 %d 个注释到降采样信号 (总注释 %d)\n', length(annot_pos), sum(keep));
    catch ME
        warning('读取或同步注释时出错: %s', ME.message);
    end
end

各种标签中表示R峰位置的有:

Matlab 复制代码
beat_chars = {'N','L','R','B','A','a','J','S','V','r','F','e','j','n','E','/','f','Q','?'};

我在这里写一个输出R峰索引的脚本:

Matlab 复制代码
function [r_idx, r_times] = get_r_peaks(annot_pos, annot_sym, Fs)
% GET_R_PEAKS 从注释中筛选出指定心搏类型的R波位置
% 输入:
%   annot_pos  - 降采样后的注释样本索引 (列向量)
%   annot_sym  - 注释符号的 cell 数组 (与 annot_pos 对应)
%   Fs         - 当前信号的采样率 (Hz)
% 输出:
%   r_idx      - 筛选后的R波样本索引 (列向量)
%   r_times    - 对应的时刻 (秒,列向量)

    % 定义需要保留的心搏类型
    beat_chars = {'N','L','R','B','A','a','J','S','V','r','F','e','j','n','E','/','f','Q','?'};
    
    % 检查输入是否为空
    if isempty(annot_pos) || isempty(annot_sym)
        r_idx = [];
        r_times = [];
        fprintf('警告:没有提供任何注释,无法提取R波。\n');
        return;
    end
    
    % 确保 annot_pos 是列向量
    annot_pos = annot_pos(:);
    % 确保 annot_sym 是列 cell 数组
    if isrow(annot_sym)
        annot_sym = annot_sym';
    end
    
    % 创建逻辑掩码:每个符号是否属于目标心搏类型
    keep_mask = ismember(annot_sym, beat_chars);
    
    % 筛选出R波对应的样本索引
    r_idx = annot_pos(keep_mask);
    
    % 计算对应的时间 (秒)   tm = (idx-1)/Fs
    r_times = (r_idx - 1) / Fs;
    
end

**********END**********

相关推荐
素材积累8 小时前
博士后出站来深可申请的项目补贴等
数据库
_1_79 小时前
SQL Server 磁盘满了 收缩日志
数据库·sqlserver
basketball6169 小时前
Redis基础:1. Redis介绍
数据库·redis·缓存
李可以量化10 小时前
成交量的终极量化策略:价量共振指标完整实现(下篇)
前端·数据库·人工智能
汽车仪器仪表相关领域11 小时前
南华 NHAT-610 柴油车排放测试仪 产品详解
数据库·功能测试·汽车·压力测试·可用性测试
我滴老baby12 小时前
工业时序数据实战:基于 DolphinDB 流计算引擎的实现与调优
数据库
睡不醒男孩03082313 小时前
TiDB数据库调研
数据库·tidb
珠***格13 小时前
实操落地|防逆流装置的安装规范、调试标准与故障处置
网络·数据库·人工智能·分布式·能源·边缘计算
Omics Pro14 小时前
3种蛋白结构输入方式!已申报欧洲发明专利
数据库·人工智能·python·机器学习·plotly
itfallrain14 小时前
Spring 构造器循环依赖排查:@RequiredArgsConstructor + @Lazy 到底有没有生效
数据库·python·spring