Skill Tracer
Skills › Scientific Agent Skills › neuropixels-analysis › api_reference.md

api_reference.md

237 lines · skills/neuropixels-analysis/references/api_reference.md at 5f2bc03

Open on GitHub
1# API Reference (SpikeInterface)
2
3Quick reference for the SpikeInterface functions used throughout this skill. Import with:
4
5```python
6import spikeinterface.full as si
7import spikeinterface.curation as sc
8import spikeinterface.widgets as sw
9```
11Targets SpikeInterface 0.105.0 (reviewed 2026-10-01). Real-data and sorter examples are illustrative. Set global parallelization once:
13```python
14si.set_global_job_kwargs(n_jobs=-1, chunk_duration="1s", progress_bar=True)
15```

API Reference (SpikeInterface)

Quick reference for the SpikeInterface functions used throughout this skill. Import with:

import spikeinterface.full as si
import spikeinterface.curation as sc
import spikeinterface.widgets as sw

Targets SpikeInterface 0.105.0 (reviewed 2026-10-01). Real-data and sorter examples are illustrative. Set global parallelization once:

si.set_global_job_kwargs(n_jobs=-1, chunk_duration="1s", progress_bar=True)
Raw lines 1 to 16
17## Loading
19### Inspect streams
21```python
22stream_names, stream_ids = si.get_neo_streams("spikeglx", "/path/to/run_g0/")
23# stream_names -> ['imec0.ap', 'imec0.lf', 'nidq']
24```

Inspect streams

stream_names, stream_ids = si.get_neo_streams("spikeglx", "/path/to/run_g0/")
# stream_names -> ['imec0.ap', 'imec0.lf', 'nidq']
Raw lines 19 to 25
26### Readers
28```python
29si.read_spikeglx(folder_path, stream_name="imec0.ap")
30si.read_openephys(folder_path, stream_name=None)
31si.read_nwb(file_path)
32```
34Prefer `stream_name` (a value from `get_neo_streams`) over `stream_id`.

Readers

si.read_spikeglx(folder_path, stream_name="imec0.ap")
si.read_openephys(folder_path, stream_name=None)
si.read_nwb(file_path)

Prefer stream_name (a value from get_neo_streams) over stream_id.

Raw lines 26 to 35
36### Recording introspection
38```python
39recording.get_num_channels()
40recording.get_total_duration() # seconds
41recording.get_sampling_frequency() # Hz
42recording.get_channel_locations()
43recording.get_probe()
44recording.frame_slice(start_frame, end_frame)
45```

Recording introspection

recording.get_num_channels()
recording.get_total_duration()        # seconds
recording.get_sampling_frequency()    # Hz
recording.get_channel_locations()
recording.get_probe()
recording.frame_slice(start_frame, end_frame)
Raw lines 36 to 46
47## Preprocessing
49```python
50si.highpass_filter(recording, freq_min=400.0)
51si.bandpass_filter(recording, freq_min=300.0, freq_max=6000.0)
52si.phase_shift(recording) # Requires inter_sample_shift metadata
53si.detect_bad_channels(recording) # -> (bad_channel_ids, channel_labels)
54recording.remove_channels(bad_channel_ids)
55si.common_reference(recording, operator="median", reference="global")
56si.highpass_spatial_filter(recording) # IBL-style destriping
57si.get_noise_levels(recording, return_in_uV=False)
58recording.save(folder="preprocessed/", format="binary")
59```
61> `detect_bad_channels` returns a 2-tuple; always unpack both values.

Preprocessing

si.highpass_filter(recording, freq_min=400.0)
si.bandpass_filter(recording, freq_min=300.0, freq_max=6000.0)
si.phase_shift(recording)                                  # Requires inter_sample_shift metadata
si.detect_bad_channels(recording)                          # -> (bad_channel_ids, channel_labels)
recording.remove_channels(bad_channel_ids)
si.common_reference(recording, operator="median", reference="global")
si.highpass_spatial_filter(recording)                      # IBL-style destriping
si.get_noise_levels(recording, return_in_uV=False)
recording.save(folder="preprocessed/", format="binary")
detect_bad_channels returns a 2-tuple; always unpack both values.
Raw lines 47 to 62
63## Drift detection and motion correction
65```python
66from spikeinterface.sortingcomponents.peak_detection import detect_peaks
67from spikeinterface.sortingcomponents.peak_localization import localize_peaks
69peaks = detect_peaks(rec, method='locally_exclusive', method_kwargs={'noise_levels': noise_levels, 'detect_threshold': 5, 'radius_um': 50.0})
70peak_locations = localize_peaks(rec, peaks, method="center_of_mass")
72# One-call correction with a preset
73rec_corrected = si.correct_motion(rec, preset="nonrigid_fast_and_accurate", folder="motion/")
74```
76**Presets:** `rigid_fast`, `kilosort_like`, `nonrigid_accurate`,
77`nonrigid_fast_and_accurate`, `dredge`, `dredge_fast` (upstream default), `medicine`.

Drift detection and motion correction

from spikeinterface.sortingcomponents.peak_detection import detect_peaks
from spikeinterface.sortingcomponents.peak_localization import localize_peaks

peaks = detect_peaks(rec, method='locally_exclusive', method_kwargs={'noise_levels': noise_levels, 'detect_threshold': 5, 'radius_um': 50.0})
peak_locations = localize_peaks(rec, peaks, method="center_of_mass")

# One-call correction with a preset
rec_corrected = si.correct_motion(rec, preset="nonrigid_fast_and_accurate", folder="motion/")

Presets: rigid_fast, kilosort_like, nonrigid_accurate, nonrigid_fast_and_accurate, dredge, dredge_fast (upstream default), medicine.

Raw lines 63 to 78
79## Spike sorting
81```python
82si.installed_sorters()
83si.available_sorters()
84si.get_default_sorter_params("kilosort4")
86sorting = si.run_sorter(
87 "kilosort4", # sorter name
88 recording,
89 folder="ks4_output", # NOT output_folder (deprecated)
90 verbose=True,
91 # sorter-specific kwargs, e.g. Th_universal=9, Th_learned=8, nblocks=5, batch_size=60000
94# Containerized external sorters (no local install needed)
95si.run_sorter("kilosort2_5", recording, folder="ks25/", docker_image=True)
97# Read a sorter folder back
98sorting = si.read_sorter_folder("ks4_output")
99```
101**Sorting introspection:**
103```python
104sorting.unit_ids
105sorting.count_total_num_spikes()
106sorting.get_unit_spike_train(unit_id)
107sorting.select_units(unit_ids)
108sorting.to_spike_vector()
109```

Spike sorting

si.installed_sorters()
si.available_sorters()
si.get_default_sorter_params("kilosort4")

sorting = si.run_sorter(
    "kilosort4",            # sorter name
    recording,
    folder="ks4_output",   # NOT output_folder (deprecated)
    verbose=True,
    # sorter-specific kwargs, e.g. Th_universal=9, Th_learned=8, nblocks=5, batch_size=60000
)

# Containerized external sorters (no local install needed)
si.run_sorter("kilosort2_5", recording, folder="ks25/", docker_image=True)

# Read a sorter folder back
sorting = si.read_sorter_folder("ks4_output")

Sorting introspection:

sorting.unit_ids
sorting.count_total_num_spikes()
sorting.get_unit_spike_train(unit_id)
sorting.select_units(unit_ids)
sorting.to_spike_vector()
Raw lines 79 to 110
111## Postprocessing: SortingAnalyzer
113```python
114analyzer = si.create_sorting_analyzer(
115 sorting, recording,
116 sparse=True,
117 format="binary_folder", # or "memory" / "zarr"
118 folder="analyzer/",
121# Extensions (order matters: random_spikes -> waveforms -> templates -> ...)
122analyzer.compute("random_spikes", method="uniform", max_spikes_per_unit=500)
123analyzer.compute("waveforms", ms_before=1.0, ms_after=2.0)
124analyzer.compute("templates", operators=["average", "std"])
125analyzer.compute("noise_levels")
126analyzer.compute("spike_amplitudes")
127analyzer.compute("amplitude_scalings")
128analyzer.compute("correlograms", window_ms=50.0, bin_ms=1.0)
129analyzer.compute("unit_locations", method="monopolar_triangulation")
130analyzer.compute("spike_locations", method="center_of_mass")
131analyzer.compute("template_similarity")
132analyzer.compute("principal_components", n_components=5, mode="by_channel_local")
134# Or compute several at once
135analyzer.compute(["random_spikes", "waveforms", "templates", "noise_levels"])
137# Access extension data
138analyzer.get_extension("quality_metrics").get_data()
139analyzer.get_extension("templates").get_unit_template(unit_id, operator="average")
141# Persist / reload / subset
142si.load_sorting_analyzer("analyzer/")
143analyzer.select_units(unit_ids, folder="analyzer_clean/", format="binary_folder")
144analyzer.remove_units(unit_ids)
145```

Postprocessing: SortingAnalyzer

analyzer = si.create_sorting_analyzer(
    sorting, recording,
    sparse=True,
    format="binary_folder",   # or "memory" / "zarr"
    folder="analyzer/",
)

# Extensions (order matters: random_spikes -> waveforms -> templates -> ...)
analyzer.compute("random_spikes", method="uniform", max_spikes_per_unit=500)
analyzer.compute("waveforms", ms_before=1.0, ms_after=2.0)
analyzer.compute("templates", operators=["average", "std"])
analyzer.compute("noise_levels")
analyzer.compute("spike_amplitudes")
analyzer.compute("amplitude_scalings")
analyzer.compute("correlograms", window_ms=50.0, bin_ms=1.0)
analyzer.compute("unit_locations", method="monopolar_triangulation")
analyzer.compute("spike_locations", method="center_of_mass")
analyzer.compute("template_similarity")
analyzer.compute("principal_components", n_components=5, mode="by_channel_local")

# Or compute several at once
analyzer.compute(["random_spikes", "waveforms", "templates", "noise_levels"])

# Access extension data
analyzer.get_extension("quality_metrics").get_data()
analyzer.get_extension("templates").get_unit_template(unit_id, operator="average")

# Persist / reload / subset
si.load_sorting_analyzer("analyzer/")
analyzer.select_units(unit_ids, folder="analyzer_clean/", format="binary_folder")
analyzer.remove_units(unit_ids)
Raw lines 111 to 146
147## Quality metrics
149```python
150metric_names = ["firing_rate", "presence_ratio", "snr", "isi_violation",
151 "amplitude_cutoff", "amplitude_cv", "sliding_rp_violation"]
152analyzer.compute("quality_metrics", metric_names=metric_names)
153metrics = analyzer.get_extension("quality_metrics").get_data()
155# Equivalent standalone helper
156metrics = si.compute_quality_metrics(analyzer, metric_names=metric_names)
157```
159**Common columns:** `snr`, `firing_rate`, `presence_ratio`, `amplitude_cutoff`,
160`isi_violations_ratio`, `isi_violations_count`. PCA-based metrics
161(`mahalanobis`, `d_prime`, `nearest_neighbor` metric names) require
162`analyzer.compute("principal_components")` first.

Quality metrics

metric_names = ["firing_rate", "presence_ratio", "snr", "isi_violation",
                "amplitude_cutoff", "amplitude_cv", "sliding_rp_violation"]
analyzer.compute("quality_metrics", metric_names=metric_names)
metrics = analyzer.get_extension("quality_metrics").get_data()

# Equivalent standalone helper
metrics = si.compute_quality_metrics(analyzer, metric_names=metric_names)

Common columns: snr, firing_rate, presence_ratio, amplitude_cutoff, isi_violations_ratio, isi_violations_count. PCA-based metrics (mahalanobis, d_prime, nearest_neighbor metric names) require analyzer.compute("principal_components") first.

Raw lines 147 to 163
164## Curation
166### Threshold-based
168```python
169query = "(amplitude_cutoff < 0.1) & (isi_violations_ratio < 0.5) & (presence_ratio > 0.9)"
170good_unit_ids = metrics.query(query).index.values
171clean = sorting.select_units(good_unit_ids)
172```

Threshold-based

query = "(amplitude_cutoff < 0.1) & (isi_violations_ratio < 0.5) & (presence_ratio > 0.9)"
good_unit_ids = metrics.query(query).index.values
clean = sorting.select_units(good_unit_ids)
Raw lines 166 to 173
174### Model-based (UnitRefine / Hugging Face)
176```python
177import spikeinterface.curation as sc
179labels = sc.model_based_label_units(
180 sorting_analyzer=analyzer,
181 repo_id="SpikeInterface/UnitRefine_noise_neural_classifier",
182 trust_model=True,
184# labels -> DataFrame with 'prediction' and 'probability' columns
186# Load a model object explicitly (e.g. to inspect feature_names_in_)
187model, model_info = sc.load_model(repo_id="SpikeInterface/toy_tetrode_model", trusted=["numpy.dtype"])
188```

Model-based (UnitRefine / Hugging Face)

import spikeinterface.curation as sc

labels = sc.model_based_label_units(
    sorting_analyzer=analyzer,
    repo_id="SpikeInterface/UnitRefine_noise_neural_classifier",
    trust_model=True,
)
# labels -> DataFrame with 'prediction' and 'probability' columns

# Load a model object explicitly (e.g. to inspect feature_names_in_)
model, model_info = sc.load_model(repo_id="SpikeInterface/toy_tetrode_model", trusted=["numpy.dtype"])
Raw lines 174 to 189
190### Manual edits
192```python
193from spikeinterface.curation import CurationSorting
194cur = CurationSorting(sorting)
195cur.remove_units(noise_unit_ids)
196sorting_curated = cur.sorting
197```

Manual edits

from spikeinterface.curation import CurationSorting
cur = CurationSorting(sorting)
cur.remove_units(noise_unit_ids)
sorting_curated = cur.sorting
Raw lines 190 to 198
199## Visualization (widgets)
201```python
202sw.plot_probe_map(recording, with_channel_ids=True)
203sw.plot_traces({"filtered": rec1, "cmr": rec2}, backend="matplotlib", clim=(-50, 50))
204si.plot_drift_raster_map(peaks=peaks, peak_locations=peak_locations, recording=rec, clim=(-50, 50))
205sw.plot_unit_waveforms(analyzer, unit_ids=[0])
206sw.plot_unit_templates(analyzer, unit_ids=[0, 1, 2])
207sw.plot_autocorrelograms(analyzer, unit_ids=[0])
208sw.plot_amplitudes(analyzer, unit_ids=[0], plot_histograms=True)
209sw.plot_unit_locations(analyzer)
210si.plot_sorting_summary(analyzer, backend="sortingview") # web-based viewer
211```
213See [plotting_guide.md](plotting_guide.md) for publication-quality figure recipes.

Visualization (widgets)

sw.plot_probe_map(recording, with_channel_ids=True)
sw.plot_traces({"filtered": rec1, "cmr": rec2}, backend="matplotlib", clim=(-50, 50))
si.plot_drift_raster_map(peaks=peaks, peak_locations=peak_locations, recording=rec, clim=(-50, 50))
sw.plot_unit_waveforms(analyzer, unit_ids=[0])
sw.plot_unit_templates(analyzer, unit_ids=[0, 1, 2])
sw.plot_autocorrelograms(analyzer, unit_ids=[0])
sw.plot_amplitudes(analyzer, unit_ids=[0], plot_histograms=True)
sw.plot_unit_locations(analyzer)
si.plot_sorting_summary(analyzer, backend="sortingview")  # web-based viewer

See plotting_guide.md for publication-quality figure recipes.

Raw lines 199 to 214
215## Export
217```python
218si.export_to_phy(analyzer, output_folder="phy_export/",
219 compute_pc_features=True, compute_amplitudes=True, copy_binary=True)
220si.export_report(analyzer, "report/", format="png")
222si.read_phy("phy_export/") # load Phy curation back
223```
225> Note: `export_to_phy` / `export_report` take `output_folder` — this is correct and
226> distinct from `run_sorter`/`create_sorting_analyzer`, which take `folder`.
228NWB writing is a separate NeuroConv workflow; there is no SI `export_to_nwb` API.
229See [ANALYSIS.md](ANALYSIS.md). Use `metric_params` for per-metric options;
230see [QUALITY_METRICS.md](QUALITY_METRICS.md) for metric-name/output-column mapping.
231UnitRefine examples require checking model feature/version compatibility first;
232see [AUTOMATED_CURATION.md](AUTOMATED_CURATION.md).
234SpikeInterface 0.105.0 has an observed `read_phy` bug for exported nonnumeric
235unit IDs (`np.isnan` TypeError). Phy export preserves `cluster_si_unit_ids.tsv`;
236keep that mapping and use a validated importer/fixed release for string-ID
237readback. Numeric-ID Phy export/reload was tested on synthetic data.

Export

si.export_to_phy(analyzer, output_folder="phy_export/",
                 compute_pc_features=True, compute_amplitudes=True, copy_binary=True)
si.export_report(analyzer, "report/", format="png")

si.read_phy("phy_export/")   # load Phy curation back
Note: export_to_phy / export_report take output_folder — this is correct and distinct from run_sorter/create_sorting_analyzer, which take folder.

NWB writing is a separate NeuroConv workflow; there is no SI export_to_nwb API. See ANALYSIS.md. Use metric_params for per-metric options; see QUALITY_METRICS.md for metric-name/output-column mapping. UnitRefine examples require checking model feature/version compatibility first; see AUTOMATED_CURATION.md.

SpikeInterface 0.105.0 has an observed read_phy bug for exported nonnumeric unit IDs (np.isnan TypeError). Phy export preserves cluster_si_unit_ids.tsv; keep that mapping and use a validated importer/fixed release for string-ID readback. Numeric-ID Phy export/reload was tested on synthetic data.

Raw lines 215 to 237