SpikeInterface / SpikeInterface/spikeinterface

Cluster–channel mismatch in Phy after QC with SpikeInterface

Open
#4,255 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

exporters
Dominant language
Python
Stars
847
Forks
280
Avg merge
3d 9h
Merged PRs (30d)
29

Description

Hi, I’m spike-sorting 128-channel Intan recordings from a flexible probe using Kilosort4 and running quality metrics with SpikeInterface. I generated the probe layout and channel mapping using probeinterface, attached it to the recording, and exported the results to Phy.

However, in Phy I found that the cluster–channel mapping is inconsistent with original mapping(See the screenshots below, using the cluster with the highest firing rate as an example):

Before QC:
Image

After QC:
Image

I suspect this may be related to the device_channel_indices, channel ordering, or how the probe is attached, but I am not sure what the correct procedure is.

Below is the exact code used for probe construction, channel mapping, attaching the probe, waveform extraction, and exporting:

import spikeinterface.core as si
import spikeinterface.extractors as se
import probeinterface as pi
import numpy as np
from spikeinterface.qualitymetrics import (
    compute_snrs,
    compute_firing_rates,
    compute_isi_violations,
    compute_quality_metrics,
)

recording = se.read_intan('E:/Desktop/flexible electrode/LL NO 170/acute-m03-stratium/acute-m03-striatum-3_251209_152340.rhd', stream_id='0')
sorting = se.read_phy('E:/Desktop/flexible electrode/LL NO 170/acute-m03-striatum/kilosort4')

import probeinterface as pi

num_ch = 128


x = np.zeros(num_ch)
y = 776 + np.arange(num_ch) * (1500 / 128)
positions = np.column_stack((x, y))

ks_map = np.array([
    73,77,74,82,83,81,90,88,102,104,108,110,117,109,118,114,75,79,
    76,80,78,87,89,84,101,107,113,103,115,111,116,112,71,69,72,70,
    86,85,91,92,99,100,105,106,119,121,120,122,67,65,68,66,93,64,
    94,95,98,96,97,127,123,125,124,126,15,13,16,18,23,17,20,24,43,40,
    39,46,47,45,48,50,11,9,12,10,14,19,25,26,37,38,49,44,51,53,52,54,
    7,5,8,6,22,21,27,28,35,36,41,42,55,57,56,58,3,1,4,2,29,0,30,31,
    34,32,33,63,59,61,60,62
])
ks_map_str = np.array([f"A-{i:03d}" for i in ks_map])
recording = recording.select_channels(ks_map_str)

probe = pi.Probe(ndim=2, si_units="um")
probe.set_contacts(
    positions=positions[ks_map],
    shapes="circle",
    shape_params={"radius": 5}
)
probe.set_device_channel_indices(np.arange(128))

recording = recording.set_probe(probe)
print(probe)obe(probe)
print(probe)

recording_signed = si.unsigned_to_signed(recording)
recording_f = si.bandpass_filter(recording_signed, freq_min=300, freq_max=6000)
print(recording_f)
recording_cmr = si.common_reference(recording_f, reference="global", operator="median")
print(recording_cmr)

analyzer = si.create_sorting_analyzer(sorting=sorting, recording=recording_cmr, format="memory")
analyzer.compute("random_spikes", method="uniform", max_spikes_per_unit=600, seed=2205)
analyzer.compute("waveforms", ms_before=1.3, ms_after=2.6, n_jobs=2)
analyzer.compute("templates", operators=["average", "median", "std"])
analyzer.compute("noise_levels")
analyzer.compute("principal_components", n_components=3, mode="by_channel_global", whiten=True)

metrics = compute_quality_metrics(
    analyzer,
    metric_names=["snr", "isi_violation",  "firing_rate", "presence_ratio", "amplitude_cutoff", "d_prime", "isolation_distance", "l_ratio"],
    extension_params = {
        "isi_violation":{"isi_threshold_ms":2.0}
    }
)
from spikeinterface.exporters import export_to_phy
export_to_phy(sorting_analyzer=analyzer, output_folder='E:/Desktop/flexible electrode/LL NO 170/acute-m03-striatum/phy_with_metrics')

Is there anything obviously wrong in my code, especially in how I construct the probe, assign device_channel_indices, or attach the probe to the recording? I’m not sure whether the mismatch in Phy is caused by incorrect channel ordering or by the way I applied the probe mapping.

Any suggestions would be greatly appreciated!

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by inspecting the channel IDs and ordering after read_intan and select_channels, then compare them with the mapping produced by set_device_channel_indices and set_probe. Review export_to_phy's generated output and verify that the exported Phy channel and cluster mappings match the intended probe layout.

Written by the indexing model from the issue text.

Assessment

Tech stack
numpy, python
Domain
data
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.