ProbeInterface is a Python package for handling probe and electrode layouts in extracellular electrophysiology. It provides a common framework for describing electrode positions, handling channel mappings, and visualizing probe geometries.
Why ProbeInterface?
- Standardized: Common format across tools
- Probe Library: Pre-defined popular probes
- Visualization: Plot electrode layouts
- Channel Mapping: Handle complex wiring
- Integration: Works with SpikeInterface, Phy, etc.
- Flexible: Support for any electrode geometry
Basic Usage
Create a Probe
from probeinterface import Probe
import numpy as np
# Create probe manually
probe = Probe(ndim=2, si_units='um')
# Define electrode positions (x, y)
positions = np.array([
[0, 0],
[0, 25],
[0, 50],
[0, 75]
])
probe.set_contacts(
positions=positions,
shapes='circle',
shape_params={'radius': 5}
)
# Set device channel indices
probe.set_device_channel_indices(np.arange(4))
print(probe)
Visualize Probe
from probeinterface.plotting import plot_probe
import matplotlib.pyplot as plt
# Plot probe layout
plot_probe(probe)
plt.show()
# With channel numbers
plot_probe(probe, with_channel_index=True)
plt.show()
Pre-Defined Probes
Neuropixels
from probeinterface import get_probe
# Neuropixels 1.0
probe = get_probe('Neuropixels-1.0')
print(f"Channels: {probe.get_contact_count()}")
# Neuropixels 2.0
probe_np2 = get_probe('Neuropixels-2.0-single-shank')
# Neuropixels Ultra
probe_ultra = get_probe('Neuropixels-Ultra')
Browse Available Probes
from probeinterface import get_probe_list
# List all available probes
probes = get_probe_list()
for probe_name in probes:
print(probe_name)
Load Specific Probe
# Neuronexus probe
probe = get_probe('Neuronexus-32')
# Cambridge NeuroTech
probe = get_probe('CambridgeNeuroTech-H2')
# Visualize
plot_probe(probe, with_channel_index=True)
Multi-Shank Probes
ProbeGroup for Multiple Shanks
from probeinterface import ProbeGroup, Probe
# Create probe group
probegroup = ProbeGroup()
# Add multiple shanks
for shank_id in range(4):
probe = Probe(ndim=2, si_units='um')
# Positions for this shank
positions = np.zeros((32, 2))
positions[:, 0] = shank_id * 200 # X offset between shanks
positions[:, 1] = np.arange(32) * 25 # Y spacing
probe.set_contacts(
positions=positions,
shapes='square',
shape_params={'width': 12}
)
probe.set_device_channel_indices(np.arange(32) + shank_id * 32)
probegroup.add_probe(probe)
# Visualize all shanks
plot_probe(probegroup)
plt.show()
Channel Mapping
Set Channel Indices
# Device channel indices (how probe is wired)
device_indices = np.array([32, 0, 33, 1, 34, 2, ...]) # Actual wiring
probe.set_device_channel_indices(device_indices)
# Contact IDs (for reference)
probe.set_contact_ids([f'elec{i}' for i in range(probe.get_contact_count())])
Reorder Channels
# Get current order
original_order = probe.device_channel_indices
# Create new mapping
new_order = np.argsort(probe.contact_positions[:, 1]) # Sort by Y position
# Apply reordering
probe.set_device_channel_indices(new_order)
Working with Recordings
Integrate with SpikeInterface
import spikeinterface as si
from probeinterface import read_prb
# Load recording
recording = si.read_spikeglx('data_folder')
# Load probe geometry
probe = read_prb('probe_layout.prb')
# Set probe to recording
recording_with_probe = recording.set_probe(probe)
# Now positions are available for analysis
positions = recording_with_probe.get_channel_locations()
Extract Probe from Recording
# Get probe info from recording
probe = recording.get_probe()
# Visualize electrode positions
plot_probe(probe)
plt.show()
# Get contact positions
positions = probe.contact_positions
print(f"Shape: {positions.shape}") # (n_channels, 2) or (n_channels, 3)
File I/O
Save/Load Probe
from probeinterface import write_probeinterface, read_probeinterface
# Save probe
write_probeinterface('my_probe.json', probe)
# Load probe
loaded_probe = read_probeinterface('my_probe.json')
Export to Other Formats
from probeinterface import write_prb, write_csv
# Export to .prb format (for Phy/KlustaKwik)
write_prb('probe.prb', probe)
# Export to CSV
write_csv('probe.csv', probe)
Load from Various Formats
from probeinterface import read_prb, read_csv, read_spikeglx
# From .prb file
probe = read_prb('probe.prb')
# From SpikeGLX meta file
probe = read_spikeglx('recording.ap.meta')
# From CSV
probe = read_csv('electrode_positions.csv')
Visualization Options
Basic Plot
from probeinterface.plotting import plot_probe
plot_probe(probe)
plt.title('Probe Layout')
plt.show()
With Annotations
# Show channel indices
plot_probe(probe, with_channel_index=True)
# Show contact IDs
plot_probe(probe, with_contact_id=True)
# Custom colors
colors = np.random.rand(probe.get_contact_count())
plot_probe(probe, contacts_colors=colors)
3D Probes
# For 3D probe geometries
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
positions = probe.contact_positions
ax.scatter(positions[:, 0], positions[:, 1], positions[:, 2])
ax.set_xlabel('X (μm)')
ax.set_ylabel('Y (μm)')
ax.set_zlabel('Z (μm)')
plt.show()
Practical Examples
Neuropixels Analysis
from probeinterface import get_probe
import matplotlib.pyplot as plt
# Load Neuropixels 1.0 probe
probe = get_probe('Neuropixels-1.0')
print(f"Total contacts: {probe.get_contact_count()}")
print(f"Shank length: {probe.contact_positions[:, 1].max()} μm")
# Visualize
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 10))
# Full probe
plot_probe(probe, ax=ax1)
ax1.set_title('Full Probe')
# Zoom to specific region
plot_probe(probe, ax=ax2)
ax2.set_xlim(-50, 50)
ax2.set_ylim(2000, 2500)
ax2.set_title('Detail View (2000-2500 μm)')
plt.tight_layout()
plt.show()
Custom Tetrode Array
from probeinterface import Probe, ProbeGroup
def create_tetrode(center_x, center_y, spacing=25):
"""Create a tetrode (4 closely-spaced electrodes)"""
probe = Probe(ndim=2, si_units='um')
positions = np.array([
[center_x - spacing/2, center_y - spacing/2],
[center_x + spacing/2, center_y - spacing/2],
[center_x - spacing/2, center_y + spacing/2],
[center_x + spacing/2, center_y + spacing/2]
])
probe.set_contacts(
positions=positions,
shapes='circle',
shape_params={'radius': 7.5}
)
return probe
# Create array of 8 tetrodes
probegroup = ProbeGroup()
for i in range(8):
tetrode = create_tetrode(
center_x=0,
center_y=i * 200, # 200 μm vertical spacing
spacing=25
)
tetrode.set_device_channel_indices(np.arange(4) + i * 4)
probegroup.add_probe(tetrode)
# Visualize
plot_probe(probegroup, with_channel_index=True)
plt.title('8-Tetrode Array')
plt.show()
Annotate Probes with Brain Regions
# Add annotations to probe
probe.annotate(manufacturer='IMEC', probe_type='Neuropixels 1.0')
# Add contact-specific annotations (e.g., brain regions)
brain_regions = ['V1'] * 100 + ['LGN'] * 284 + ['Deep'] * 616
probe.annotate_contacts(brain_area=brain_regions)
# Access annotations
print(probe.annotations)
print(probe.contact_annotations['brain_area'])
Integration with Analysis Tools
With Phy
# Export for manual curation in Phy
from probeinterface import write_prb
write_prb('probe_for_phy.prb', probe)
# Use this file in Phy for proper channel visualization
With SpikeInterface
import spikeinterface.full as si
# Load recording
recording = si.read_spikeglx('recording_folder')
# Set probe
recording = recording.set_probe(probe)
# Now spike sorting will use spatial information
sorting = si.run_sorter('kilosort3', recording)
Installation
# With pixi
pixi add probeinterface
# With pip
pip install probeinterface
# With plotting support
pixi add "probeinterface[plotting]"
Resources
- Documentation: https://probeinterface.readthedocs.io/
- GitHub: https://github.com/SpikeInterface/probeinterface
- Probe Library: https://gin.g-node.org/spikeinterface/probeinterface_library
Summary
ProbeInterface is essential for:
- Standardization: Common probe format
- Visualization: Understand electrode layouts
- Analysis: Spatial information for spike sorting
- Documentation: Record probe configurations
It provides the foundation for spatial analysis of multi-channel electrophysiology data.