mcpField
N
neurokit2

neurokit2

github.com/jaechang-hits数据与数据库38470 次安装1 周前更新
代码仓库

关于 neurokit2

neurokit2 是介绍 NeuroKit2 的技能指南;该 Python 库用于处理和分析生理信号,面向生物信号研究者,统一支持 ECG、EEG、EMG、EDA、PPG 和呼吸信号,并可生成模拟信号。

主要功能

  1. 01统一的信号处理与分析流程
  2. 02支持 ECG、EEG、EMG、EDA、PPG 和 RSP
  3. 03滤波、峰值检测与伪迹校正
  4. 04生理特征提取与信号模拟

适用场景

  1. 01从 ECG 记录提取 HRV 特征
  2. 02分离 EDA 的紧张与相位成分
  3. 03检测 EMG 中的肌肉激活时段
  4. 04生成模拟信号以验证算法

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jaechang-hits/sciagent-skills --skill neurokit2

安装后在 Claude Code 中输入/neurokit2确认已加载。

详细介绍

NeuroKit2

概述

NeuroKit2 提供用于生理信号处理的统一高级 API。每种信号类型(ECG、EEG、EMG、EDA、PPG、RSP)都遵循相同的 nk.{signal}_process() → nk.{signal}_analyze() 工作流:输入原始信号,输出清理后的信号和特征。该库会自动处理去趋势、滤波、峰值检测、伪影校正和特征提取,参数针对生物信号的特性进行了调整。结果以 pandas DataFrame 的形式返回,便于进行后续统计分析。NeuroKit2 还提供 nk.{signal}_simulate() 函数,用于生成合成测试信号。

适用场景

  • 从 ECG 记录中提取 HRV(心率变异性)特征,用于压力或自主神经系统分析
  • 检测 ECG 中的 R 波峰,并计算 RR 间期、SDNN、RMSSD、pNN50、LF/HF 比值
  • 处理 EDA/GSR 信号,将其分解为紧张性(SCL)和相位性(SCR)成分,用于心理生理学研究
  • 清理并分段 EMG 信号,以便在生物力学中检测肌肉激活的起始和结束
  • 处理可穿戴传感器采集的 PPG 信号,将其作为 ECG 的替代信号来测量心率和 SpO2
  • 生成合成生理信号,用于算法验证和单元测试
  • 处理多通道 EEG/MEG 并进行源定位时,请改用 MNE;处理原始底层 DSP 时,请使用 scipy.signal

前置条件

  • Python 软件包:neurokit2、numpy、pandas、matplotlib
  • 数据要求:生理信号的一维 NumPy 数组或 pandas Series;已知采样率(Hz)
  • 典型采样率:ECG 250–1000 Hz,EEG 256–2048 Hz,EDA 4–64 Hz,EMG 1000–2000 Hz
pip install neurokit2 numpy pandas matplotlib scipy

快速开始

import neurokit2 as nk
import matplotlib.pyplot as plt

# Generate and process synthetic ECG (10 seconds at 500 Hz)
ecg_signal = nk.ecg_simulate(duration=10, sampling_rate=500, heart_rate=70)
signals, info = nk.ecg_process(ecg_signal, sampling_rate=500)

# Plot processed ECG
nk.ecg_plot(signals, info)
plt.savefig("ecg_processed.pdf", bbox_inches="tight")
print(f"R-peaks detected: {len(info['ECG_R_Peaks'])}")
print(signals[["ECG_Clean", "ECG_Rate", "ECG_Quality"]].describe())

核心 API

模块 1:ECG 处理

从原始 ECG 到清理后的信号、R 波峰和瞬时心率的完整处理流程。

import neurokit2 as nk
import numpy as np

# Load real data (example: CSV with one ECG column at 250 Hz)
# ecg_raw = pd.read_csv("ecg_recording.csv")["ecg"].values
# Or use synthetic:
ecg_raw = nk.ecg_simulate(duration=60, sampling_rate=250, heart_rate=72, noise=0.05)

# Full processing pipeline
signals, info = nk.ecg_process(ecg_raw, sampling_rate=250)

# signals: DataFrame with columns ECG_Raw, ECG_Clean, ECG_Rate, ECG_R_Peaks, ...
# info: dict with R-peak indices, P/Q/S/T wave locations

print(f"R-peaks: {len(info['ECG_R_Peaks'])} detected")
print(f"Mean heart rate: {signals['ECG_Rate'].mean():.1f} bpm")
print(f"ECG quality (0-1): {signals['ECG_Quality'].mean():.2f}")

# Get delineated waves (P, Q, S, T)
_, waves_dict = nk.ecg_delineate(signals["ECG_Clean"], info["ECG_R_Peaks"],
                                   sampling_rate=250, method="dwt")
print(f"P-wave peaks found: {np.sum(~np.isnan(waves_dict['ECG_P_Peaks']))}")
# HRV analysis from ECG
hrv_time = nk.hrv_time(info["ECG_R_Peaks"], sampling_rate=250)
hrv_freq = nk.hrv_frequency(info["ECG_R_Peaks"], sampling_rate=250)
hrv_nonlinear = nk.hrv_nonlinear(info["ECG_R_Peaks"], sampling_rate=250)

print("Time-domain HRV:")
print(f"  SDNN  : {hrv_time['HRV_SDNN'].values[0]:.2f} ms")
print(f"  RMSSD : {hrv_time['HRV_RMSSD'].values[0]:.2f} ms")
print(f"  pNN50 : {hrv_time['HRV_pNN50'].values[0]:.2f}%")

print("\nFrequency-domain HRV:")
print(f"  LF power  : {hrv_freq['HRV_LF'].values[0]:.4f} ms²")
print(f"  HF power  : {hrv_freq['HRV_HF'].values[0]:.4f} ms²")
print(f"  LF/HF     : {hrv_freq['HRV_LFHF'].values[0]:.3f}")
模块 2:EDA / GSR 处理

将皮肤电活动(EDA)/电皮肤反应(GSR)分解为紧张性和相位性成分。

import neurokit2 as nk

# Simulate EDA signal (10 min at 4 Hz with 3 events)
eda_raw = nk.eda_simulate(duration=600, sampling_rate=4, scr_number=10, noise=0.01)

# Process: detrend, filter, decompose into SCL (tonic) + SCR (phasic)
signals, info = nk.eda_process(eda_raw, sampling_rate=4)

print(f"SCR peaks detected: {len(info['SCR_Peaks'])}")
print(f"SCR recovery times (mean): {signals['SCR_RecoveryTime'].mean():.2f} s")
print(f"SCL (tonic) mean: {signals['EDA_Tonic'].mean():.4f} μS")
print(f"SCR (phasic) amplitude mean: {signals['EDA_Phasic'].mean():.4f} μS")

# Analyze epochs around events
events = nk.events_create(event_onsets=[60, 120, 240], event_durations=3,
                           desired_length=len(eda_raw))
epoch_df = nk.epochs_create(signals, events, sampling_rate=4,
                              epochs_start=-5, epochs_end=10)
模块 3:EMG 处理

从表面 EMG 中检测肌肉激活。

import neurokit2 as nk

# Simulate EMG with 3 activations at 1000 Hz
emg_raw = nk.emg_simulate(duration=10, sampling_rate=1000, burst_number=3)

# Process: rectify, envelope, detect activation periods
signals, info = nk.emg_process(emg_raw, sampling_rate=1000)

print(f"EMG activation onsets: {len(info['EMG_Onsets'])}")
print(f"EMG activation offsets: {len(info['EMG_Offsets'])}")

# Activation periods
for onset, offset in zip(info["EMG_Onsets"], info["EMG_Offsets"]):
    duration_ms = (offset - onset) / 1000 * 1000  # samples → ms
    print(f"  Activation: {onset/1000:.2f}s – {offset/1000:.2f}s ({duration_ms:.0f} ms)")

# Plot
nk.emg_plot(signals)
import matplotlib.pyplot as plt
plt.savefig("emg_processed.pdf", bbox_inches="tight")
模块 4:EEG / 复杂度分析

适用于 EEG 和其他生物信号的信号复杂度测量。

import neurokit2 as nk
import numpy as np

# Generate EEG-like signal
eeg = nk.eeg_simulate(duration=30, sampling_rate=256, brain_information=0.7)

# Band power (delta, theta, alpha, beta, gamma)
bands = nk.eeg_power(eeg, sampling_rate=256,
                      frequency_band=["Delta", "Theta", "Alpha", "Beta", "Gamma"])
print("EEG Band Power:")
for band, power in bands.items():
    print(f"  {band}: {power:.4f}")

# Complexity measures (for any 1D biosignal)
signal = np.random.randn(1000)  # test signal

entropy_sample = nk.entropy_sample(signal, order=2, r=0.2 * np.std(signal))
entropy_fuzzy = nk.entropy_fuzzy(signal, order=2)
dfa = nk.fractal_dfa(signal)

print(f"Sample entropy: {entropy_sample[0]:.4f}")
print(f"Fuzzy entropy: {entropy_fuzzy[0]:.4f}")
print(f"DFA exponent: {dfa[0]:.4f}")
模块 5:PPG 处理

处理来自可穿戴传感器的光电容积描记(PPG)信号。

import neurokit2 as nk

# Simulate PPG at 100 Hz (common wearable sampling rate)
ppg_raw = nk.ppg_simulate(duration=30, sampling_rate=100, heart_rate=65, noise=0.01)

# Process: clean, detect peaks
signals, info = nk.ppg_process(ppg_raw, sampling_rate=100)

print(f"PPG peaks detected: {len(info['PPG_Peaks'])}")
print(f"Mean heart rate: {signals['PPG_Rate'].mean():.1f} bpm")

# Compute HRV from PPG peaks (as ECG surrogate)
hrv = nk.hrv(info["PPG_Peaks"], sampling_rate=100)
print(f"RMSSD from PPG: {hrv['HRV_RMSSD'].values[0]:.2f} ms")
模块 6:信号模拟

生成合成生理信号,用于测试和算法验证。

import neurokit2 as nk
import matplotlib.pyplot as plt

fig, axes = plt.subplots(5, 1, figsize=(12, 10))

signals_dict = {
    "ECG":  nk.ecg_simulate(duration=5, sampling_rate=500, heart_rate=70),
    "EDA":  nk.eda_simulate(duration=5, sampling_rate=64, scr_number=2),
    "EMG":  nk.emg_simulate(duration=5, sampling_rate=1000, burst_number=2),
    "PPG":  nk.ppg_simulate(duration=5, sampling_rate=100, heart_rate=70),
    "RSP":  nk.rsp_simulate(duration=5, sampling_rate=100, respiratory_rate=15),
}

for ax, (name, signal) in zip(axes, signals_dict.items()):
    sr = {"ECG": 500, "EDA": 64, "EMG": 1000, "PPG": 100, "RSP": 100}[name]
    import numpy as np
    t = np.arange(len(signal)) / sr
    ax.plot(t, signal, lw=0.8)
    ax.set_ylabel(name)

axes[-1].set_xlabel("Time (s)")
plt.tight_layout()
plt.savefig("physiological_signals.pdf", bbox_inches="tight")
print("Synthetic signals plotted")

核心概念

事件相关分析(Epochs)

NeuroKit2 使用 nk.events_create() 标记刺激开始时间,然后使用 nk.epochs_create() 将连续信号分割为围绕各个事件的固定长度窗口。这使得能够根据刺激时间锁定 ERP(事件相关电位)和 SCR(皮肤电反应)分析。

信号质量指数(SQI)

ECG 和 PPG 处理会为每个采样点计算质量指数(0–1)。低于 0.5 的质量值表示该片段存在噪声或伪影。计算 HRV 特征之前,可使用 signals["ECG_Quality"] > 0.5 屏蔽不可靠区域。

常见工作流

工作流 1:多信号生理记录分析
import neurokit2 as nk
import pandas as pd
import numpy as np

# Simulate 5-minute multi-modal recording
sr_ecg, sr_eda, sr_ppg = 250, 4, 100
duration = 300  # seconds

ecg = nk.ecg_simulate(duration=duration, sampling_rate=sr_ecg, heart_rate=72)
eda = nk.eda_simulate(duration=duration, sampling_rate=sr_eda, scr_number=15)
ppg = nk.ppg_simulate(duration=duration, sampling_rate=sr_ppg, heart_rate=72)

# Process each modality
ecg_signals, ecg_info = nk.ecg_process(ecg, sampling_rate=sr_ecg)
eda_signals, eda_info = nk.eda_process(eda, sampling_rate=sr_eda)
ppg_signals, ppg_info = nk.ppg_process(ppg, sampling_rate=sr_ppg)

# Extract HRV from ECG
hrv = nk.hrv(ecg_info["ECG_R_Peaks"], sampling_rate=sr_ecg, show=False)

# Summary table
summary = pd.Series({
    "HR_mean_bpm":       ecg_signals["ECG_Rate"].mean(),
    "HRV_RMSSD_ms":     hrv["HRV_RMSSD"].values[0],
    "HRV_LF_HF":        hrv["HRV_LFHF"].values[0],
    "SCL_mean_uS":      eda_signals["EDA_Tonic"].mean(),
    "SCR_count":        len(eda_info["SCR_Peaks"]),
    "PPG_HR_mean_bpm":  ppg_signals["PPG_Rate"].mean(),
})
print(summary.to_string())
工作流 2:压力检测特征提取
import neurokit2 as nk
import pandas as pd
import numpy as np

def extract_stress_features(ecg_array, eda_array, sr_ecg=250, sr_eda=4):
    """Extract HRV + EDA features for stress classification."""
    # ECG features
    ecg_signals, ecg_info = nk.ecg_process(ecg_array, sampling_rate=sr_ecg)
    hrv_time = nk.hrv_time(ecg_info["ECG_R_Peaks"], sampling_rate=sr_ecg)
    hrv_freq = nk.hrv_frequency(ecg_info["ECG_R_Peaks"], sampling_rate=sr_ecg)

    # EDA features
    eda_signals, eda_info = nk.eda_process(eda_array, sampling_rate=sr_eda)

    return {
        "HR_mean":    ecg_signals["ECG_Rate"].mean(),
        "RMSSD":      hrv_time["HRV_RMSSD"].values[0],
        "SDNN":       hrv_time["HRV_SDNN"].values[0],
        "LF_HF":      hrv_freq["HRV_LFHF"].values[0],
        "SCL_mean":   eda_signals["EDA_Tonic"].mean(),
        "SCR_count":  len(eda_info["SCR_Peaks"]),
        "SCR_amp_mean": eda_signals["EDA_Phasic"].mean(),
    }

# Test with simulated data
ecg = nk.ecg_simulate(duration=120, sampling_rate=250, heart_rate=85)  # elevated HR
eda = nk.eda_simulate(duration=120, sampling_rate=4, scr_number=8)

features = extract_stress_features(ecg, eda)
df = pd.DataFrame([features])
print(df.T.to_string(header=False))

关键参数

参数模块/函数默认值范围 / 选项作用
sampling_rate所有 *_process必填64–2048 Hz(取决于模态)必须与实际记录的采样率一致,才能确保计时准确
methodecg_peaks"neurokit""neurokit"、"pantompkins1985"、"hamilton2002"、"elgendi2010"R 峰检测算法;"neurokit" 最稳健
heart_rateecg_simulate7030–200 bpm模拟的 ECG 心率
noiseecg_simulate0.010–0.5添加到模拟信号中的高斯噪声幅度
scr_numbereda_simulate10–50模拟 EDA 中 SCR 事件的数量
orderentropy_sample21–5计算熵时使用的嵌入维度
rentropy_sample0.2×SD0.1–0.5×SD样本熵的容差
epochs_startepochs_create-1负浮点数(s)分段开始时间,即事件发生前的秒数
epochs_endepochs_create3正浮点数(s)分段结束时间,即事件发生后的秒数

常用方案

方案:批量处理多个 ECG 文件
import neurokit2 as nk
import pandas as pd
from pathlib import Path
import numpy as np

results = []
for ecg_file in Path("ecg_data").glob("*.csv"):
    try:
        ecg = pd.read_csv(ecg_file)["ecg"].values
        _, info = nk.ecg_process(ecg, sampling_rate=250)
        hrv = nk.hrv_time(info["ECG_R_Peaks"], sampling_rate=250)
        results.append({
            "file": ecg_file.stem,
            "RMSSD": hrv["HRV_RMSSD"].values[0],
            "SDNN":  hrv["HRV_SDNN"].values[0],
            "pNN50": hrv["HRV_pNN50"].values[0],
        })
    except Exception as e:
        print(f"FAILED {ecg_file.name}: {e}")

df = pd.DataFrame(results)
df.to_csv("hrv_results.csv", index=False)
print(df.describe())
方案:按质量指数剔除伪迹
import neurokit2 as nk
import numpy as np

ecg = nk.ecg_simulate(duration=60, sampling_rate=250, noise=0.1)
signals, info = nk.ecg_process(ecg, sampling_rate=250)

# Identify low-quality R-peaks
r_peaks = info["ECG_R_Peaks"]
quality_at_peaks = signals["ECG_Quality"].iloc[r_peaks].values

good_peaks = r_peaks[quality_at_peaks > 0.5]
print(f"R-peaks before cleaning: {len(r_peaks)}")
print(f"High-quality R-peaks  : {len(good_peaks)}")

# Recompute HRV on clean peaks only
hrv_clean = nk.hrv_time(good_peaks, sampling_rate=250)
print(f"RMSSD (cleaned): {hrv_clean['HRV_RMSSD'].values[0]:.2f} ms")

故障排除

问题原因解决方案
R 峰检测位置错误sampling_rate 不正确仔细检查记录元数据;错误的采样率会导致所有峰值时间偏移
HRV RMSSD 极高(>200 ms)漏检心搏或额外检测更换检测方法:尝试 method="pantompkins1985";检查原始信号
ecg_delineate 对 P/Q/S/T 返回的全是 NaN记录时长过短(<5 秒)或噪声过大使用时长 ≥10 s 的记录;调用 delineate 前先降低噪声
真实数据中的 EDA 未检测到 SCR 峰低通滤波器截止频率设置过高,或 EDA 采样率错误验证 EDA 采样率;在 eda_phasic() 中设置 phasic_method="cvx"
熵函数返回 NaN信号过短或为恒定值(方差为零)至少使用 200–500 个样本;确保信号不是恒定值
EMG 激活起始检测漏掉激活对低幅值信号自动设置的阈值过高传入自定义阈值:nk.emg_activation(signal, threshold=0.5)
长时间记录出现内存错误DataFrame 包含数百万行按 60 秒窗口处理;拼接 HRV 分段结果

相关技能

  • matplotlib-scientific-plotting — 绘制处理后的 neurokit2 信号和 HRV Poincaré 图
  • statsmodels-statistical-modeling — 对提取的 HRV/EDA 特征进行 ANOVA 和混合效应模型分析
  • scikit-learn-machine-learning — 使用提取的特征进行压力/情绪分类

参考资料

评论

支持 Markdown