mcpField
N
neuropixels-analysis

neuropixels-analysis

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

关于 neuropixels-analysis

这项技能指导研究人员使用 SpikeInterface 等 Python 工具分析 Neuropixels 细胞外记录。内容涵盖排序、单元质量筛选、事件锁定与群体分析,以及导出为 NWB。

主要功能

  1. 01读取 SpikeGLX、OpenEphys 和 NWB 数据
  2. 02预处理记录并运行脉冲排序
  3. 03计算单元质量指标
  4. 04分析 PSTH 和群体编码
  5. 05将排序结果导出为 NWB

适用场景

  1. 01从原始记录提取单神经元活动
  2. 02依据质量指标筛选排序后的单元
  3. 03比较同一记录上的脉冲排序器
  4. 04整理筛选结果以供数据共享

安装

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

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

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

详细介绍

Neuropixels 分析

概述

Neuropixels 探针以 30 kHz 的采样率,同时记录 384 个(NP 1.0)或 192 个(NP 2.0)通道的细胞外电压。分析遵循标准流程:原始数据 → 尖峰分选(Kilosort)→ 质量筛选 → 单元分析。SpikeInterface(Python)为 10 多种尖峰分选器提供统一 API,支持从多种格式(SpikeGLX、OpenEphys、NWB)加载数据、计算质量指标并导出分选结果。ProbeInterface 管理探针几何结构和通道映射。分选后的分析(PSTH、发放率、解码)使用标准 Python 科学计算工具栈。

适用场景

  • 对 SpikeGLX(.bin)或 OpenEphys(.dat)记录的 Neuropixels 数据进行尖峰分选,以提取单单元活动
  • 应用自动质量控制指标(ISI 违规、SNR、发放率)筛选分选后的单元
  • 计算与实验事件对齐的刺激后时间直方图(PSTH)
  • 分析群体编码:根据发放率解码刺激或行为变量
  • 将分选数据转换为 NWB(Neurodata Without Borders)格式以便共享
  • 在同一数据集上比较多种尖峰分选器,以验证方法
  • 可视化单元波形、自相关图以及探针通道上的空间分布
  • 如需使用支持 10 多种尖峰分选器并提供统一 API 和比较工具的框架,请改用 SpikeInterface

前置条件

  • Python 包:spikeinterface[full]、probeinterface、numpy、pandas、matplotlib、scipy
  • 尖峰分选器:Kilosort4(Python,pip install kilosort);或基于 MATLAB 的 Kilosort2/3(需要 MATLAB 许可证)
  • 数据要求:原始 .bin(SpikeGLX)或 .dat(OpenEphys)记录;探针通道映射文件(.prb 或来自 ProbeInterface)
  • 硬件:Kilosort 需要 NVIDIA GPU(4 GB 以上 VRAM);也可使用 CPU,但速度约慢 10 倍
pip install spikeinterface[full] probeinterface kilosort
# Optional: Phy for manual curation
pip install phy

工作流

步骤 1:加载原始记录
import spikeinterface.full as si
from pathlib import Path

# SpikeGLX recording (most common Neuropixels format)
data_dir = Path("/data/recording/session_001")
recording = si.read_spikeglx(data_dir, stream_name="imec0.ap")

print(f"Probe type:       {recording.get_probe().name}")
print(f"Channels:         {recording.get_num_channels()}")
print(f"Sampling rate:    {recording.get_sampling_frequency()} Hz")
print(f"Duration:         {recording.get_total_duration():.1f} s")
print(f"Total samples:    {recording.get_total_samples()}")
步骤 2:应用公共参考和带通滤波
import spikeinterface.preprocessing as spre

# Common median reference (removes common noise across all channels)
recording_cmr = spre.common_reference(recording, reference="global", operator="median")

# Bandpass filter: 300–6000 Hz for spikes
recording_filt = spre.bandpass_filter(recording_cmr, freq_min=300, freq_max=6000)

# Remove bad channels automatically
recording_clean, removed_ids = spre.remove_bad_channels(recording_filt)
print(f"Removed {len(removed_ids)} bad channels: {removed_ids}")
print(f"Clean recording: {recording_clean.get_num_channels()} channels")
步骤 3:运行尖峰分选
import spikeinterface.sorters as ss
from pathlib import Path

output_dir = Path("./sorting_output")

# Kilosort4 (recommended for Neuropixels; requires GPU)
sorting = ss.run_sorter(
    "kilosort4",
    recording_clean,
    output_folder=output_dir / "kilosort4",
    remove_existing_folder=True,
    verbose=True,
    # Kilosort4 parameters
    nblocks=5,          # number of drift correction blocks
    Th_learned=8,       # threshold for learned templates
    do_correction=True, # drift correction
)

print(f"Units found: {len(sorting.get_unit_ids())}")
print(f"Spike counts (first 5): {[len(sorting.get_unit_spike_train(u, segment_index=0)) for u in sorting.unit_ids[:5]]}")
步骤 4:计算波形和质量指标
import spikeinterface.full as si
import spikeinterface.qualitymetrics as sqm

# Extract waveforms (snippets around each spike)
waveforms = si.extract_waveforms(
    recording_clean,
    sorting,
    folder="./waveforms",
    ms_before=1.0,      # ms before spike peak
    ms_after=2.0,       # ms after spike peak
    max_spikes_per_unit=1000,
    overwrite=True,
    n_jobs=4,
)

print(f"Waveform shape per unit: {waveforms.get_waveforms(waveforms.unit_ids[0]).shape}")
# (n_spikes, n_samples, n_channels)

# Compute quality metrics
metrics = sqm.compute_quality_metrics(
    waveforms,
    metric_names=["snr", "isi_violation", "firing_rate", "presence_ratio",
                  "amplitude_cutoff", "nearest_neighbor"],
)
print(f"\nQuality metrics summary:")
print(metrics.describe())
步骤 5:筛选单元
import pandas as pd

# Curation thresholds (Allen Brain Institute defaults)
thresholds = {
    "snr":                (5.0, None),   # SNR ≥ 5
    "isi_violations_ratio": (None, 0.1), # ISI violation ratio ≤ 10%
    "firing_rate":        (0.1, None),   # firing rate ≥ 0.1 Hz
    "presence_ratio":     (0.9, None),   # present ≥ 90% of recording
    "amplitude_cutoff":   (None, 0.1),   # amplitude cutoff ≤ 10%
}

def apply_thresholds(metrics_df, thresholds):
    mask = pd.Series(True, index=metrics_df.index)
    for metric, (low, high) in thresholds.items():
        if metric not in metrics_df.columns:
            continue
        if low is not None:
            mask &= metrics_df[metric] >= low
        if high is not None:
            mask &= metrics_df[metric] <= high
    return mask

good_units_mask = apply_thresholds(metrics, thresholds)
good_unit_ids = metrics[good_units_mask].index.tolist()

print(f"Total units:   {len(metrics)}")
print(f"Good units:    {len(good_unit_ids)} ({100*len(good_unit_ids)/len(metrics):.0f}%)")

# Filter sorting to good units only
sorting_curated = sorting.select_units(good_unit_ids)
步骤 6:计算 PSTH 并进行可视化
import numpy as np
import matplotlib.pyplot as plt

def compute_psth(spike_times, event_times, window=(-0.5, 1.0), bin_size=0.01, fs=30000):
    """Compute peri-stimulus time histogram for one unit."""
    bins = np.arange(window[0], window[1] + bin_size, bin_size)
    spike_times_s = spike_times / fs  # samples → seconds
    counts = np.zeros(len(bins) - 1)
    for t_event in event_times:
        rel_times = spike_times_s - t_event
        in_window = rel_times[(rel_times >= window[0]) & (rel_times < window[1])]
        counts += np.histogram(in_window, bins=bins)[0]
    rate = counts / (len(event_times) * bin_size)  # convert to Hz
    return bins[:-1] + bin_size / 2, rate  # bin centers, firing rate

# Example: visual stimulus events at 1.0, 2.5, 4.0, 5.5 s
fs = 30000
event_times_s = np.array([1.0, 2.5, 4.0, 5.5, 7.0, 8.5])

# Plot PSTH for first 4 good units
fig, axes = plt.subplots(2, 2, figsize=(10, 6))
for ax, unit_id in zip(axes.flat, good_unit_ids[:4]):
    spikes = sorting_curated.get_unit_spike_train(unit_id, segment_index=0)
    times, rate = compute_psth(spikes, event_times_s, fs=fs)
    ax.bar(times, rate, width=0.01, color="steelblue", alpha=0.8)
    ax.axvline(0, color="red", lw=1.5, linestyle="--", label="Stimulus")
    ax.set_xlabel("Time from stimulus (s)")
    ax.set_ylabel("Firing rate (Hz)")
    ax.set_title(f"Unit {unit_id}")

plt.tight_layout()
plt.savefig("psth_grid.pdf", bbox_inches="tight")
print("Saved psth_grid.pdf")
步骤 7:导出为 NWB
import spikeinterface.exporters as sexp

# Export sorted data to Neurodata Without Borders format
nwb_path = "./recording_sorted.nwb"
sexp.export_to_nwb(
    sorting_curated,
    nwb_file_path=nwb_path,
    overwrite=True,
)
print(f"Exported to NWB: {nwb_path}")

# Also export waveforms for Phy manual curation
phy_dir = "./phy_export"
sexp.export_to_phy(
    waveforms,
    output_folder=phy_dir,
    compute_pc_features=True,
    copy_binary=True,
)
print(f"Phy export ready at: {phy_dir}")
print("Launch with: phy template-gui phy_export/params.py")

关键参数

参数模块/函数默认值范围 / 选项效果
freq_min / freq_maxbandpass_filter300/6000 Hz150–500 / 3000–10000尖峰频段;NP 1.0 的标准范围为 300–6000 Hz
nblocksKilosort450–10漂移校正块数;0 表示禁用漂移校正
Th_learnedKilosort486–12检测阈值(× 噪声水平);越低 = 尖峰越多,噪声也越多
ms_before / ms_afterextract_waveforms1.0/2.00.5–2.0/1.0–3.0 ms尖峰峰值附近的波形片段时间窗
max_spikes_per_unitextract_waveforms500100–5000每个单元用于提取波形的最大尖峰数
snr 阈值质量指标—5–10用于接收单元的信噪比阈值
isi_violations_ratio质量指标—0.05–0.2不应期违规率;<0.1 表示分离良好
presence_ratio质量指标—0.9单元处于活动状态的记录时长比例
bin_sizePSTH0.01 s0.001–0.05 sPSTH 时间分辨率;越小 = 细节越多、噪声越大

关键概念

尖峰分选流程

原始电压 → 预处理(公共参考、带通滤波)→ 检测(阈值交叉或学习得到的模板)→ 聚类(PCA + k-means 或高斯混合)→ 模板匹配 → 质量指标 → 筛选。Kilosort4 增加了漂移校正,这对慢性 NP 记录至关重要(数小时内探针尖端漂移 5–50 µm 会降低单元分离效果)。

质量指标
  • SNR:波形峰值幅度 / 噪声水平。SNR >5 表示该单元隔离良好
  • ISI 违例率:短于不应期(约 1.5 ms)的脉冲间隔所占比例。比率 <0.1 表示单单元隔离
  • 存在率:至少发放过一个脉冲的记录时段所占比例。<0.9 表示该单元消失或丢失
  • 幅度截断率:因检测阈值而漏检的脉冲所占比例。<0.1 可确保近乎完整的检测

常用示例

示例:比较同一段记录上的两种排序器
import spikeinterface.sorters as ss
import spikeinterface.comparison as sc

# Run two sorters
sorting_ks4 = ss.run_sorter("kilosort4", recording_clean, output_folder="./ks4")
sorting_sc   = ss.run_sorter("spykingcircus2", recording_clean, output_folder="./sc2")

# Compare outputs
comparison = sc.compare_two_sorters(sorting_ks4, sorting_sc,
                                     sorting1_name="Kilosort4",
                                     sorting2_name="SpykingCircus2")
print(comparison.get_performance())
# Shows: agreement fraction, recall, precision per matched unit pair
示例:群体发放率热图
import numpy as np
import matplotlib.pyplot as plt

fs = 30000
duration_s = sorting_curated.get_total_duration()
bin_edges = np.arange(0, duration_s, 0.05)  # 50 ms bins

# Build firing rate matrix: (n_units, n_bins)
fr_matrix = np.zeros((len(good_unit_ids), len(bin_edges)-1))
for i, uid in enumerate(good_unit_ids[:50]):
    spikes_s = sorting_curated.get_unit_spike_train(uid, segment_index=0) / fs
    counts, _ = np.histogram(spikes_s, bins=bin_edges)
    fr_matrix[i] = counts / 0.05  # Hz

# Normalize each unit
fr_norm = (fr_matrix - fr_matrix.mean(1, keepdims=True)) / (fr_matrix.std(1, keepdims=True) + 1e-6)

fig, ax = plt.subplots(figsize=(12, 5))
im = ax.imshow(fr_norm, aspect="auto", cmap="RdBu_r",
               extent=[0, duration_s, len(good_unit_ids[:50]), 0], vmin=-2, vmax=2)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Unit #")
ax.set_title("Population Activity Heatmap (z-scored FR)")
plt.colorbar(im, ax=ax, label="z-score")
plt.tight_layout()
plt.savefig("population_heatmap.pdf", bbox_inches="tight")

预期输出

输出格式典型内容
sorting/SpikeInterface 文件夹每个单元的脉冲时间;模板波形
waveforms/SpikeInterface 文件夹波形片段 (n_spikes, n_samples, n_channels)
quality_metrics.csvCSV每个单元的 SNR、ISI 违例、发放率、存在率
psth_grid.pdfPDF精选单元的 PSTH 图
recording_sorted.nwbNWB/HDF5用于共享的可移植脉冲排序数据
phy_export/Phy 格式用于手动整理 GUI 的 .npy + params.py

故障排除

问题原因解决方案
Kilosort GPU 内存错误记录时长过长,或通道数量超出 VRAM 容量使用 si.SubRecording 按时间段处理记录;使用 NP 2.0(192 ch)设置
未找到任何单元阈值过高,或预处理移除了所有信号将 Th_learned 降至 6;确认 recording_clean 中的通道数据非零
ISI 违例过多(>50%)多单元活动被合并为单个单元使用 Phy 手动整理;提高 Th_learned,使其更加保守
波形提取时内存不足脉冲数 × 通道数 × 采样数过多减少 max_spikes_per_unit;提高 n_jobs 以并行提取
漂移校正失败脉冲过少或记录时间过短设置 nblocks=1(最小校正)或 nblocks=0(禁用校正)
NWB 导出失败缺少会话元数据(受试者、日期)提供 NWBFile 元数据;或使用 sexp.export_to_nwb(..., metadata={...})
找不到 SpikeGLX 文件流名称错误列出流:si.get_neo_streams("spikeglx", data_dir)

参考资料

评论

支持 Markdown