SpikeInterface is a unified framework for spike sorting and analysis of extracellular electrophysiology data. It provides a common interface to multiple spike sorting algorithms and comprehensive tools for preprocessing, sorting, and quality control.
Why SpikeInterface?
- Unified Interface: Run any spike sorter with the same code
- Multiple Sorters: Kilosort, MountainSort, SpykingCircus, and more
- Quality Metrics: Automatic computation of sorting quality
- Preprocessing: Filtering, artifact removal, referencing
- Comparison: Compare outputs from different sorters
- Neo Integration: Works with standard data formats
Installation
# Basic installation
pixi add spikeinterface
# With specific sorters
pixi add kilosort # For Kilosort3/4
pixi add mountainsort5
# Or with pip
pip install spikeinterface[full]
Basic Workflow
1. Load Recording
import spikeinterface as si
import spikeinterface.extractors as se
# Load from file
recording = se.read_intan('data.rhd') # Intan
# or
recording = se.read_spikeglx('data_folder') # Neuropixels
# Check properties
print(f"Duration: {recording.get_total_duration()}s")
print(f"Sampling rate: {recording.get_sampling_frequency()}Hz")
print(f"Channels: {recording.get_num_channels()}")
2. Preprocessing
import spikeinterface.preprocessing as spre
# Bandpass filter
recording_f = spre.bandpass_filter(recording, freq_min=300, freq_max=6000)
# Common average reference
recording_cmr = spre.common_reference(recording_f, reference='global')
# Remove artifacts
recording_clean = spre.remove_artifacts(
recording_cmr,
triggers=artifact_times,
ms_before=0.5,
ms_after=2.0
)
3. Run Spike Sorting
import spikeinterface.sorters as ss
# Run Kilosort3
sorting = ss.run_sorter(
'kilosort3',
recording_clean,
output_folder='sorted_output',
verbose=True,
docker_image=True # Use Docker for isolation
)
# Check results
print(f"Found {len(sorting.get_unit_ids())} units")
4. Post-Processing
import spikeinterface.postprocessing as spost
# Extract waveforms
waveforms = spost.extract_waveforms(
recording_clean,
sorting,
folder='waveforms',
max_spikes_per_unit=500
)
# Compute templates
templates = waveforms.get_all_templates()
# Compute quality metrics
import spikeinterface.qualitymetrics as sqm
metrics = sqm.compute_quality_metrics(
waveforms,
metric_names=['snr', 'isi_violation', 'firing_rate']
)
print(metrics)
Complete Neuropixels Pipeline
import spikeinterface as si
import spikeinterface.extractors as se
import spikeinterface.preprocessing as spre
import spikeinterface.sorters as ss
import spikeinterface.postprocessing as spost
import spikeinterface.qualitymetrics as sqm
import spikeinterface.curation as sc
# 1. Load data
recording = se.read_spikeglx('neuropixels_data/')
# 2. Preprocessing pipeline
recording = spre.phase_shift(recording) # Correct for ADC shifts
recording = spre.bandpass_filter(recording, freq_min=300, freq_max=6000)
recording = spre.common_reference(recording, reference='global')
# 3. Spike sorting
sorting = ss.run_sorter(
'kilosort3',
recording,
output_folder='sorting_output',
detect_threshold=6,
projection_threshold=[10, 4],
preclust_threshold=8
)
# 4. Extract waveforms
waveforms = spost.extract_waveforms(
recording,
sorting,
folder='waveforms',
max_spikes_per_unit=500,
ms_before=1.0,
ms_after=2.0
)
# 5. Compute metrics
metrics = sqm.compute_quality_metrics(
waveforms,
metric_names=[
'snr',
'isi_violation',
'firing_rate',
'presence_ratio',
'amplitude_cutoff'
]
)
# 6. Automatic curation
sorting_curated = sc.auto_curation(
sorting,
waveforms,
min_snr=5,
max_isi_violation=0.5,
min_firing_rate=0.1
)
print(f"Original units: {len(sorting.get_unit_ids())}")
print(f"Curated units: {len(sorting_curated.get_unit_ids())}")
# 7. Export
sorting_curated.save(folder='final_sorting')
Comparing Spike Sorters
import spikeinterface.comparison as sc
# Run multiple sorters
sorting_ks3 = ss.run_sorter('kilosort3', recording, output_folder='ks3')
sorting_ms5 = ss.run_sorter('mountainsort5', recording, output_folder='ms5')
# Compare results
comparison = sc.compare_two_sorters(
sorting_ks3,
sorting_ms5,
sorting1_name='Kilosort3',
sorting2_name='MountainSort5'
)
# Get agreement matrix
agreement = comparison.get_agreement_fraction()
print(f"Agreement: {agreement:.2%}")
# Find consensus units
matches = comparison.get_matching()
print(f"Matched units: {len(matches)}")
# Visualize
import matplotlib.pyplot as plt
sc.plot_agreement_matrix(comparison)
plt.show()
Quality Metrics
Compute All Metrics
metrics = sqm.compute_quality_metrics(
waveforms,
metric_names=[
'snr', # Signal-to-noise ratio
'isi_violation', # ISI violations (%)
'firing_rate', # Mean firing rate
'presence_ratio', # Fraction of time active
'amplitude_cutoff', # Est. false negative rate
'nn_isolation', # Nearest-neighbor isolation
'nn_noise_overlap' # Overlap with noise
]
)
Filter High-Quality Units
import pandas as pd
# Set thresholds
high_quality = metrics[
(metrics['snr'] > 5) &
(metrics['isi_violation'] < 0.5) &
(metrics['firing_rate'] > 0.1) &
(metrics['presence_ratio'] > 0.9)
]
print(f"High-quality units: {len(high_quality)}")
Visualization
Plot Units
import spikeinterface.widgets as sw
# Plot unit waveforms
sw.plot_unit_waveforms(waveforms, unit_ids=[0, 1, 2])
# Plot unit templates
sw.plot_unit_templates(waveforms, unit_ids=[0, 1, 2])
# Plot amplitudes over time
sw.plot_amplitudes(waveforms, unit_ids=[0, 1, 2])
# Plot ISI distribution
sw.plot_isi_distribution(sorting, unit_ids=[0, 1, 2])
# Plot autocorrelograms
sw.plot_autocorrelograms(sorting, unit_ids=[0, 1, 2])
# Plot crosscorrelograms
sw.plot_crosscorrelograms(sorting, unit_ids=[0, 1])
Export to Phy
# Export for manual curation in Phy
spost.export_to_phy(
waveforms,
output_folder='phy_output',
compute_pc_features=True,
compute_amplitudes=True
)
# After manual curation in Phy, load back:
sorting_phy = se.read_phy('phy_output')
Working with Spike Trains
# Get spike times for a unit
spike_times = sorting.get_unit_spike_train(unit_id=0)
print(f"Unit 0: {len(spike_times)} spikes")
# Get all spike trains
for unit_id in sorting.get_unit_ids():
spike_train = sorting.get_unit_spike_train(unit_id)
rate = len(spike_train) / recording.get_total_duration()
print(f"Unit {unit_id}: {rate:.2f} Hz")
# Convert to Neo format
import neo
spike_train_neo = neo.SpikeTrain(
spike_times / recording.get_sampling_frequency(),
t_stop=recording.get_total_duration(),
units='s'
)
Preprocessing Options
Filtering
# Highpass filter
recording_hp = spre.highpass_filter(recording, freq_min=300)
# Bandpass filter
recording_bp = spre.bandpass_filter(recording, freq_min=300, freq_max=6000)
# Notch filter (remove 50/60 Hz)
recording_notch = spre.notch_filter(recording, freq=60, q=30)
Referencing
# Common average reference
recording_car = spre.common_reference(recording, reference='global')
# Common median reference
recording_cmr = spre.common_reference(recording, reference='median')
# Specific reference channels
recording_ref = spre.common_reference(recording, ref_channel_ids=[0, 1, 2])
Artifact Removal
# Remove time periods
recording_clean = spre.remove_artifacts(
recording,
list_triggers=artifact_frames,
ms_before=1.0,
ms_after=3.0
)
# Interpolate artifacts
recording_interp = spre.remove_artifacts(
recording,
list_triggers=artifact_frames,
mode='linear' # or 'cubic'
)
Available Spike Sorters
- Kilosort2/3/4: High-density probe sorting
- MountainSort5: Template matching
- SpykingCircus: Multi-electrode sorting
- Tridesclous: Template matching
- Combinato: Wavelet-based
- IronClust: Drift-resistant
- HDSort: High-density arrays
Best Practices
- Preprocessing Pipeline: Always filter and reference
- Multiple Sorters: Compare at least 2 algorithms
- Quality Metrics: Use multiple metrics for curation
- Manual Curation: Always visually inspect in Phy
- Document Parameters: Save all sorting parameters
- Version Control: Track sorter versions
- Ground Truth: Validate with hybrid recordings when possible
Performance Tips
- Use Docker images for sorters (reproducibility)
- Process recordings in chunks for memory efficiency
- Use
n_jobsparameter for parallel processing - Cache preprocessing steps
- Use
load_if_exists=Truefor waveform extraction
Resources
- Documentation: https://spikeinterface.readthedocs.io/
- Tutorials: https://spikeinterface.readthedocs.io/en/latest/tutorials.html
- GitHub: https://github.com/SpikeInterface/spikeinterface
- Paper: Buccino et al., eLife 2020
Summary
SpikeInterface is essential for:
- Unified workflow: Consistent interface across sorters
- Quality control: Automatic metrics and curation
- Comparison: Evaluate multiple algorithms
- Reproducibility: Standardized processing pipeline
It’s become the standard framework for modern spike sorting workflows.