峰值检测模拟数据是人工生成的信号,用于模拟真实世界中含有峰值的信号(如心电图ECG、脑电图EEG、质谱图等)。这些数据包含:
基础信号:周期性或趋势性成分
噪声:随机扰动,模拟测量误差
峰值:需要检测的局部极大值点
模拟数据示例:
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
# 创建一个具体的模拟信号示例
def create_simulation_example():
# 1. 时间轴
t = np.linspace(0, 10, 1000)
# 2. 基础信号(背景趋势)
base_signal = 0.5 * np.sin(2 * np.pi * 0.3 * t) # 低频波动
# 3. 添加不同类型的峰值
signal = base_signal.copy()
# 添加高斯型峰值(最常见)
def add_gaussian_peak(position, height, width):
return height * np.exp(-((t - position)**2) / (2 * width**2))
# 添加洛伦兹型峰值(质谱常见)
def add_lorentzian_peak(position, height, width):
return height * (width**2) / ((t - position)**2 + width**2)
# 添加几个不同类型的峰值
signal += add_gaussian_peak(2.0, 1.2, 0.1) # 尖锐的高斯峰
signal += add_gaussian_peak(3.5, 0.8, 0.2) # 较宽的高斯峰
signal += add_lorentzian_peak(5.0, 1.0, 0.15) # 洛伦兹峰
signal += add_gaussian_peak(6.8, 0.6, 0.08) # 小峰
signal += add_gaussian_peak(8.2, 1.5, 0.25) # 大峰
# 4. 添加噪声
noise = np.random.normal(0, 0.1, len(t)) # 高斯白噪声
noisy_signal = signal + noise
# 5. 找到真正的峰值位置(用于验证)
true_peaks, _ = find_peaks(signal, height=0.5, distance=20)
# 6. 生成标签(ground truth)
labels = np.zeros_like(t)
for peak in true_peaks:
# 在峰值中心附近标记
start = max(0, peak - 5)
end = min(len(t), peak + 6)
labels[start:end] = 1
return t, signal, noisy_signal, labels, true_peaks
# 生成数据并可视化
t, clean_signal, noisy_signal, labels, true_peaks = create_simulation_example()
# 绘制模拟信号
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
# 1. 干净的信号(无噪声)
axes[0].plot(t, clean_signal, 'b-', linewidth=1.5, label='干净信号')
axes[0].plot(t[true_peaks], clean_signal[true_peaks], 'ro',
label='真实峰值', markersize=8)
axes[0].set_title('干净的模拟信号(无噪声)')
axes[0].set_ylabel('幅值')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
# 2. 含噪声的信号(真实情况)
axes[1].plot(t, noisy_signal, 'g-', linewidth=1, alpha=0.7, label='含噪声信号')
axes[1].plot(t[true_peaks], noisy_signal[true_peaks], 'rx',
label='真实峰值位置', markersize=10, markeredgewidth=2)
axes[1].set_title('含噪声的模拟信号(真实测量情况)')
axes[1].set_ylabel('幅值')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
# 3. 标签(ground truth)
axes[2].plot(t, labels, 'r-', linewidth=2, label='峰值标签')
axes[2].fill_between(t, 0, labels, alpha=0.3, color='red')
axes[2].set_title('峰值标签(Ground Truth)')
axes[2].set_xlabel('时间')
axes[2].set_ylabel('标签 (0/1)')
axes[2].set_yticks([0, 1])
axes[2].set_yticklabels(['非峰值', '峰值'])
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 显示具体数据
print("模拟信号信息:")
print(f"信号长度:{len(t)} 个点")
print(f"时间范围:{t[0]:.1f} 到 {t[-1]:.1f}")
print(f"峰值数量:{len(true_peaks)} 个")
print(f"峰值位置(索引):{true_peaks}")
print(f"峰值位置(时间):{t[true_peaks]}")
# 打印前50个点的数据示例
print("\n前50个数据点示例:")
print("索引 | 时间 | 干净信号 | 含噪信号 | 标签")
print("-" * 60)
for i in range(50):
if i in true_peaks[:3]: # 标记前几个峰值
marker = "★"
else:
marker = " "
print(f"{i:4d} | {t[i]:5.2f} | {clean_signal[i]:7.3f} | {noisy_signal[i]:7.3f} | {labels[i]:3.0f} {marker}")
真实应用场景对应的模拟数据:
ECG信号、质谱图、EEG信号、也包括PPG信号
import numpy as np
import matplotlib.pyplot as plt
def create_ecg_like_signal():
"""生成类似心电图的信号(ECG-like)"""
t = np.linspace(0, 10, 2000)
# ECG特征波形
ecg = np.zeros_like(t)
# 模拟心跳(大约72次/分钟)
heartbeat_interval = 0.8 # 秒
num_beats = int(10 / heartbeat_interval)
for i in range(num_beats):
beat_start = i * heartbeat_interval
# P波(心房收缩)
p_wave = 0.2 * np.exp(-((t - beat_start)**2) / (2 * 0.02**2))
# QRS复合波(心室收缩)
qrs = 1.5 * np.exp(-((t - (beat_start + 0.15))**2) / (2 * 0.03**2))
# T波(心室恢复)
t_wave = 0.4 * np.exp(-((t - (beat_start + 0.3))**2) / (2 * 0.05**2))
ecg += p_wave + qrs + t_wave
# 添加基线漂移和噪声
baseline_drift = 0.1 * np.sin(2 * np.pi * 0.1 * t)
noise = np.random.normal(0, 0.05, len(t))
return t, ecg + baseline_drift + noise
def create_mass_spec_like_signal():
"""生成类似质谱图的信号"""
t = np.linspace(0, 100, 1000)
# 基线
signal = 0.1 * np.ones_like(t)
# 添加不同m/z比的峰
peak_positions = [20, 35, 50, 65, 80]
peak_heights = [1.0, 0.7, 0.9, 0.5, 0.8]
peak_widths = [0.5, 0.3, 0.4, 0.6, 0.2]
for pos, height, width in zip(peak_positions, peak_heights, peak_widths):
signal += height * np.exp(-((t - pos)**2) / (2 * width**2))
# 质谱特有的噪声(Poisson噪声)
noise = np.random.poisson(5, len(t)) / 50
return t, signal + noise
def create_eeg_like_signal():
"""生成类似脑电图的信号"""
t = np.linspace(0, 5, 1000)
# 不同频率的脑电波
delta = 0.3 * np.sin(2 * np.pi * 2 * t) # δ波 (1-4 Hz)
theta = 0.2 * np.sin(2 * np.pi * 6 * t) # θ波 (4-8 Hz)
alpha = 0.4 * np.sin(2 * np.pi * 10 * t) # α波 (8-12 Hz)
beta = 0.1 * np.sin(2 * np.pi * 20 * t) # β波 (12-30 Hz)
signal = delta + theta + alpha + beta
# 添加事件相关电位(ERP)尖峰
spike_times = [1.2, 2.4, 3.6]
for spike_time in spike_times:
spike = 0.8 * np.exp(-((t - spike_time)**2) / (2 * 0.02**2))
signal += spike
# EEG噪声
noise = np.random.normal(0, 0.1, len(t))
return t, signal + noise
# 可视化不同应用场景
fig, axes = plt.subplots(3, 1, figsize=(12, 12))
# 1. 心电图信号
t_ecg, ecg_signal = create_ecg_like_signal()
axes[0].plot(t_ecg, ecg_signal, 'b-', linewidth=1)
axes[0].set_title('心电图 (ECG) 模拟信号', fontsize=14)
axes[0].set_xlabel('时间 (秒)')
axes[0].set_ylabel('电压 (mV)')
axes[0].grid(True, alpha=0.3)
axes[0].set_xlim(0, 3) # 显示前3秒
# 标记R峰
r_peaks = np.arange(0, 3, 0.8) # 假设0.8秒一个心跳
for peak in r_peaks:
idx = np.argmin(np.abs(t_ecg - peak))
axes[0].plot(t_ecg[idx], ecg_signal[idx], 'ro', markersize=6)
# 2. 质谱信号
t_ms, ms_signal = create_mass_spec_like_signal()
axes[1].stem(t_ms, ms_signal, linefmt='b-', markerfmt=' ', basefmt=' ')
axes[1].set_title('质谱图 (Mass Spectrometry) 模拟信号', fontsize=14)
axes[1].set_xlabel('质荷比 (m/z)')
axes[1].set_ylabel('强度')
axes[1].grid(True, alpha=0.3)
# 标记峰值
ms_peaks = [20, 35, 50, 65, 80]
for peak in ms_peaks:
idx = np.argmin(np.abs(t_ms - peak))
axes[1].plot(t_ms[idx], ms_signal[idx], 'ro', markersize=6)
# 3. 脑电图信号
t_eeg, eeg_signal = create_eeg_like_signal()
axes[2].plot(t_eeg, eeg_signal, 'b-', linewidth=1)
axes[2].set_title('脑电图 (EEG) 模拟信号', fontsize=14)
axes[2].set_xlabel('时间 (秒)')
axes[2].set_ylabel('电压 (µV)')
axes[2].grid(True, alpha=0.3)
# 标记ERP尖峰
eeg_peaks = [1.2, 2.4, 3.6]
for peak in eeg_peaks:
idx = np.argmin(np.abs(t_eeg - peak))
axes[2].plot(t_eeg[idx], eeg_signal[idx], 'ro', markersize=6)
plt.tight_layout()
plt.show()
各种奇特的信号模拟:
def create_challenging_signals():
"""创建具有挑战性的信号,测试峰值检测算法"""
t = np.linspace(0, 10, 1000)
fig, axes = plt.subplots(3, 2, figsize=(15, 12))
# 1. 重叠峰
ax = axes[0, 0]
signal = np.zeros_like(t)
# 两个重叠的高斯峰
signal += np.exp(-((t - 4.0)**2) / (2 * 0.15**2))
signal += 0.8 * np.exp(-((t - 4.2)**2) / (2 * 0.15**2))
signal += np.random.normal(0, 0.05, len(t))
ax.plot(t, signal, 'b-')
ax.set_title('重叠峰(难以分离)')
ax.grid(True, alpha=0.3)
# 2. 变化基线
ax = axes[0, 1]
baseline = 0.3 * np.sin(2 * np.pi * 0.2 * t)
peaks = 0.8 * np.exp(-((t - 3.0)**2) / (2 * 0.1**2))
peaks += 0.6 * np.exp(-((t - 6.0)**2) / (2 * 0.1**2))
signal = baseline + peaks + np.random.normal(0, 0.05, len(t))
ax.plot(t, signal, 'b-')
ax.plot(t, baseline, 'r--', alpha=0.5, label='基线')
ax.set_title('变化基线下的峰')
ax.legend()
ax.grid(True, alpha=0.3)
# 3. 高噪声
ax = axes[1, 0]
signal = np.exp(-((t - 5.0)**2) / (2 * 0.1**2))
noise = np.random.normal(0, 0.3, len(t)) # 高噪声
noisy_signal = signal + noise
ax.plot(t, noisy_signal, 'b-', alpha=0.7)
ax.plot(t, signal, 'r-', alpha=0.5, label='真实信号')
ax.set_title('高噪声环境')
ax.legend()
ax.grid(True, alpha=0.3)
# 4. 小峰靠近大峰
ax = axes[1, 1]
signal = 1.0 * np.exp(-((t - 4.0)**2) / (2 * 0.2**2))
signal += 0.2 * np.exp(-((t - 4.5)**2) / (2 * 0.05**2)) # 小峰
signal += np.random.normal(0, 0.05, len(t))
ax.plot(t, signal, 'b-')
ax.set_title('小峰靠近大峰(易被掩盖)')
ax.grid(True, alpha=0.3)
# 5. 宽峰和窄峰混合
ax = axes[2, 0]
signal = 0.7 * np.exp(-((t - 3.0)**2) / (2 * 0.3**2)) # 宽峰
signal += 0.9 * np.exp(-((t - 7.0)**2) / (2 * 0.05**2)) # 窄峰
signal += np.random.normal(0, 0.05, len(t))
ax.plot(t, signal, 'b-')
ax.set_title('宽峰和窄峰混合')
ax.grid(True, alpha=0.3)
# 6. 非对称峰
ax = axes[2, 1]
# 使用非对称函数创建峰
from scipy.stats import skewnorm
signal = 1.0 * skewnorm.pdf(t, -5, loc=5, scale=0.2) # 左偏峰
signal += 0.8 * skewnorm.pdf(t, 5, loc=8, scale=0.15) # 右偏峰
signal += np.random.normal(0, 0.03, len(t))
ax.plot(t, signal, 'b-')
ax.set_title('非对称峰(非高斯形状)')
ax.grid(True, alpha=0.3)
plt.suptitle('峰值检测的各种挑战场景', fontsize=16)
plt.tight_layout()
plt.show()
create_challenging_signals()
关键要点:
-
模拟数据的目的:
-
在没有真实数据时开发和测试算法
-
控制实验条件(已知峰值位置)
-
测试算法对不同挑战的鲁棒性
-
-
常见峰值类型:
-
高斯峰(对称)
-
洛伦兹峰(质谱)
-
非对称峰(色谱)
-
尖峰(EEG/ECG)
-
-
关键参数:
-
峰高(信号强度)
-
峰宽(时间/频率范围)
-
信噪比(SNR)
-
峰密度(单位时间内的峰数)
-
-
为什么使用模拟数据训练U-Net:
-
可以生成无限量的训练数据
-
每个样本都有准确的标签(ground truth)
-
可以设计特定难度的样本
-
在应用到真实数据前验证算法有效性
-
这样的模拟数据可以帮助我们训练和测试峰值检测算法,确保它能够处理真实世界中可能遇到的各种情况。