今天拿到了一个小鼠的心电图,准备做一些实验。阅读文献时找到一个方法:质量指数谱,通过曲率来衡量其非线性。曲率越大,表示信号的非线性越强,多重分形结构越明显。
并且文章对小鼠进行注毒,发现健康小鼠的非线性最大[1]。此外该方法也可用于人类ECG[2]。
具体方法很简单,通过熵表述不确定性,曲率量化非线性复杂度,具体可参考对应文献,二者的算法一致。
Matlab
clear; close all; clc;
%% 1. 加载数据
data_path = 'D:\xiaoshu\cleandata.mat';
load(data_path);
% 自动识别信号变量(double 列向量)
vars = whos('-file', data_path);
signal = [];
for i = 1:length(vars)
if strcmp(vars(i).class, 'double') && vars(i).size(2) == 1
signal = eval(vars(i).name);
fprintf('已加载信号:%s,长度 %d 点\n', vars(i).name, length(signal));
break;
end
end
if isempty(signal)
error('未找到合适的信号变量(需为 double 列向量)');
end
% 去直流
signal = signal - mean(signal);
%% 2. 参数设置(请根据实际数据修改采样率!)
fs = 2000; % 采样率 Hz(论文中为 5000 Hz,请按你的数据调整)
gamma_max = 50; % 最大尺度因子(论文取 50)
q_list = -10:0.2:10; % q 值行向量
L_min = 4; % 最小段落长度(点数)
L_num = 12; % 段落长度数目(对数等分)
%% 3. 执行多尺度分析
fprintf('\n开始多尺度分析(修正版)...\n');
results = msmf_analysis_corrected(signal, fs, gamma_max, q_list, L_min, L_num);
%% 4. 显示结果
fprintf('\n=== 多尺度曲率结果 ===\n');
fprintf('gamma\t频率(Hz)\tK\n');
for i = 1:length(results.gamma)
fprintf('%d\t%.1f\t\t%.4f\n', results.gamma(i), results.frequency(i), results.K(i));
end
[~, max_idx] = max(abs(results.K));
fprintf('\n|K| 最大值出现在 gamma = %d,对应频率 %.1f Hz\n', results.gamma(max_idx), results.frequency(max_idx));
%% 5. 绘图
figure('Position', [100 100 900 400]);
subplot(1,2,1);
plot(results.gamma, results.K, 'bo-', 'LineWidth', 1.5);
xlabel('Scale factor \gamma');
ylabel('K_{\pi(q)}');
title('曲率 vs 尺度因子');
grid on;
subplot(1,2,2);
semilogx(results.frequency, results.K, 'ro-', 'LineWidth', 1.5);
xlabel('频率 (Hz)');
ylabel('K_{\pi(q)}');
title('曲率 vs 频率');
set(gca, 'XDir', 'reverse');
grid on;
%% 6. 保存结果
save_path = fullfile(fileparts(data_path), 'MSMF_results_corrected.mat');
save(save_path, 'results');
fprintf('\n结果已保存至:%s\n', save_path);
%% ================= 函数定义 =================
function results = msmf_analysis_corrected(signal, fs, gamma_max, q_list, L_min, L_num)
% 多尺度分析主函数
gamma_vec = (1:gamma_max)';
K_vec = zeros(gamma_max, 1);
freq_vec = fs ./ (2 * gamma_vec); % 信号频率 = fs/(2*gamma)
for g = 1:gamma_max
fprintf('Processing gamma = %d ...\n', g);
y = coarse_grain_fast(signal, g);
K = multifractal_spectrum_corrected(y, q_list, L_min, L_num);
K_vec(g) = K;
end
results.gamma = gamma_vec;
results.K = K_vec;
results.frequency = freq_vec;
end
function y = coarse_grain_fast(x, gamma)
% 向量化粗粒化:每 gamma 个连续点取平均
N = length(x);
n_new = floor(N / gamma);
if n_new == 0
y = [];
return;
end
% 截取可整除部分,reshape 后按列求平均
x_trim = x(1 : gamma * n_new);
y = mean(reshape(x_trim, gamma, n_new), 1)';
end
function K = multifractal_spectrum_corrected(y, q_list, L_min, L_num)
% 对单个粗粒化信号计算曲率 K(修正测度定义与曲率计算)
Ny = length(y);
if Ny < L_min * 4
K = NaN;
return;
end
% 生成段落长度数组(对数等分)
L_max = floor(Ny / 4);
L_vals = unique(round(logspace(log10(L_min), log10(L_max), L_num)));
L_vals(L_vals > Ny/2) = [];
if length(L_vals) < 3
K = NaN;
return;
end
% 计算配分函数
[logL, logZ] = partition_function_corrected(y, L_vals, q_list);
% 剔除无效行
valid_rows = all(~isnan(logZ), 2);
if sum(valid_rows) < 3
K = NaN;
return;
end
logL = logL(valid_rows);
logZ = logZ(valid_rows, :);
% 计算 τ(q)
tau = compute_tau(logL, logZ);
if any(isnan(tau))
K = NaN;
return;
end
% 强制 q=1 处 τ=0(与论文图1(a)拐角对应)
[~, idx_q1] = min(abs(q_list - 1));
tau(idx_q1) = 0;
% 计算曲率
K = compute_curvature_corrected(q_list, tau);
end
function [logL, logZ] = partition_function_corrected(y, L_list, q_list)
% 计算配分函数 Z(q) 及其对数(修正测度为段内均值,不加绝对值)
nL = length(L_list);
nq = length(q_list);
logL = log(L_list(:));
logZ = zeros(nL, nq);
for iL = 1:nL
L = L_list(iL);
measures = compute_measures_corrected(y, L);
total = sum(measures);
if total == 0
logZ(iL, :) = NaN;
continue;
end
P = measures / total;
for iq = 1:nq
q = q_list(iq);
if q == 1
% 香农熵的指数形式,避免 log(0)
nonzero = P > 0;
Z = exp(sum(P(nonzero) .* log(P(nonzero))));
else
Z = sum(P .^ q);
end
logZ(iL, iq) = log(Z);
end
end
end
function measures = compute_measures_corrected(y, L)
% 将信号 y 划分为长度为 L 的段落,计算每段的均值(不加绝对值,与论文一致)
Ny = length(y);
nseg = floor(Ny / L);
if nseg == 0
measures = [];
return;
end
% 向量化计算每段均值
y_trim = y(1 : L * nseg);
y_reshaped = reshape(y_trim, L, nseg);
measures = mean(y_reshaped, 1)';
% 由于 ECG 信号有正负,均值可能为负。为确保概率非负,将整体平移至最小值非负
min_m = min(measures);
if min_m < 0
measures = measures - min_m + eps;
end
end
function tau = compute_tau(logL, logZ)
% 线性拟合得到质量指数 τ(q)
nq = size(logZ, 2);
tau = zeros(1, nq);
for iq = 1:nq
valid = ~isnan(logZ(:, iq));
if sum(valid) < 2
tau(iq) = NaN;
continue;
end
p = polyfit(logL(valid), logZ(valid, iq), 1);
tau(iq) = p(1);
end
end
function K = compute_curvature_corrected(q_list, tau)
% 计算曲率 K = tan(phi)
idx_left = q_list < 1;
idx_right = q_list > 1;
if sum(idx_left) < 2 || sum(idx_right) < 2
K = NaN;
return;
end
p_left = polyfit(q_list(idx_left), tau(idx_left), 1);
p_right = polyfit(q_list(idx_right), tau(idx_right), 1);
k1 = p_left(1);
k2 = p_right(1);
K = abs((k2 - k1) / (1 + k1 * k2));
end

我数据来源是正常小鼠,和文章比较,整体趋势还是接近的:

参考文献:
1\]X. Yang, T. Sun, S. Ma and Y. Zhou, "Multiscale Detection in ECG Multifractal Structure," *2010 4th International Conference on Bioinformatics and Biomedical Engineering*, Chengdu, China, 2010, pp. 1-4, doi: 10.1109/ICBBE.2010.5515828. \[2\]杨小冬,宁新宝,何爱军,等.基于多尺度的人体ECG信号质量指数谱分析\[J\].物理学报,2008,(03):1514-1521. \*\*\*\*\*\*\*\*\*\*\*\*\*END\*\*\*\*\*\*\*\*\*\*\*\*\*