Skills › Scientific Agent Skills › neuropixels-analysis › plotting_guide.md
plotting_guide.md
459 lines · skills/neuropixels-analysis/references/plotting_guide.md at 5f2bc03
1# Plotting Guide
3Targets SI 0.105.0 (reviewed 2026-10-01). Examples assume one continuous segment,
4calibrated amplitudes and computed extensions. Real-data figures are illustrative.
Plotting Guide
Targets SI 0.105.0 (reviewed 2026-10-01). Examples assume one continuous segment, calibrated amplitudes and computed extensions. Real-data figures are illustrative.
Raw lines 1 to 56## Setup
8```python
9import matplotlib.pyplot as plt
10import numpy as np
11import spikeinterface.full as si
12import spikeinterface.widgets as sw
14# High-quality settings
15plt.rcParams['figure.dpi'] = 150
16plt.rcParams['savefig.dpi'] = 300
17plt.rcParams['font.size'] = 10
18plt.rcParams['font.family'] = 'sans-serif'
19```
Setup
import matplotlib.pyplot as plt
import numpy as np
import spikeinterface.full as si
import spikeinterface.widgets as sw
# High-quality settings
plt.rcParams['figure.dpi'] = 150
plt.rcParams['savefig.dpi'] = 300
plt.rcParams['font.size'] = 10
plt.rcParams['font.family'] = 'sans-serif'Raw lines 6 to 20Drift and Motion Plots
Raw lines 21 to 2223### Basic Drift Map
25```python
26from spikeinterface.sortingcomponents.peak_detection import detect_peaks
27from spikeinterface.sortingcomponents.peak_localization import localize_peaks
29noise_levels = si.get_noise_levels(recording, return_in_uV=False)
30peaks = detect_peaks(recording, method='locally_exclusive', method_kwargs={'noise_levels': noise_levels, 'detect_threshold': 5, 'radius_um': 50.0})
31peak_locations = localize_peaks(recording, peaks, method='center_of_mass')
33si.plot_drift_raster_map(
34 peaks=peaks,
35 peak_locations=peak_locations,
36 recording=recording,
37 clim=(-50, 50),
38)
39plt.savefig('drift_raster.png', bbox_inches='tight')
40```
Basic Drift Map
from spikeinterface.sortingcomponents.peak_detection import detect_peaks
from spikeinterface.sortingcomponents.peak_localization import localize_peaks
noise_levels = si.get_noise_levels(recording, return_in_uV=False)
peaks = detect_peaks(recording, method='locally_exclusive', method_kwargs={'noise_levels': noise_levels, 'detect_threshold': 5, 'radius_um': 50.0})
peak_locations = localize_peaks(recording, peaks, method='center_of_mass')
si.plot_drift_raster_map(
peaks=peaks,
peak_locations=peak_locations,
recording=recording,
clim=(-50, 50),
)
plt.savefig('drift_raster.png', bbox_inches='tight')Raw lines 23 to 4142### Motion Estimate Visualization
44`correct_motion(..., output_motion_info=True)` returns `(recording, motion_info)`. The
45`motion_info` dict can be plotted directly with the built-in widget:
47```python
48rec_corrected, motion_info = si.correct_motion(
49 recording, preset='nonrigid_fast_and_accurate', output_motion_info=True, folder='motion/'
50)
52# Built-in motion visualization (drift raster + motion field)
53sw.plot_motion_info(motion_info, recording=recording)
54plt.savefig('motion_analysis.png', dpi=300, bbox_inches='tight')
56# Or inspect the Motion object directly
57motion = motion_info['motion']
58displacement = motion.displacement[0] # (n_temporal_bins, n_spatial_bins)
59temporal_bins = motion.temporal_bins_s[0]
60plt.figure(figsize=(10, 4))
61plt.plot(temporal_bins, displacement, alpha=0.5)
62plt.xlabel('Time (s)'); plt.ylabel('Displacement (um)'); plt.title('Estimated Motion')
63plt.savefig('motion_traces.png', dpi=300, bbox_inches='tight')
64```
Motion Estimate Visualization
correct_motion(..., output_motion_info=True) returns (recording, motion_info). The motion_info dict can be plotted directly with the built-in widget:
rec_corrected, motion_info = si.correct_motion(
recording, preset='nonrigid_fast_and_accurate', output_motion_info=True, folder='motion/'
)
# Built-in motion visualization (drift raster + motion field)
sw.plot_motion_info(motion_info, recording=recording)
plt.savefig('motion_analysis.png', dpi=300, bbox_inches='tight')
# Or inspect the Motion object directly
motion = motion_info['motion']
displacement = motion.displacement[0] # (n_temporal_bins, n_spatial_bins)
temporal_bins = motion.temporal_bins_s[0]
plt.figure(figsize=(10, 4))
plt.plot(temporal_bins, displacement, alpha=0.5)
plt.xlabel('Time (s)'); plt.ylabel('Displacement (um)'); plt.title('Estimated Motion')
plt.savefig('motion_traces.png', dpi=300, bbox_inches='tight')Raw lines 42 to 65Waveform Plots
Raw lines 66 to 6768### Single Unit Waveforms
70```python
71unit_id = 0
73# Basic waveforms
74sw.plot_unit_waveforms(analyzer, unit_ids=[unit_id])
75plt.savefig(f'unit_{unit_id}_waveforms.png')
77# With density map
78sw.plot_unit_waveforms_density_map(analyzer, unit_ids=[unit_id])
79plt.savefig(f'unit_{unit_id}_density.png')
80```
Single Unit Waveforms
unit_id = 0
# Basic waveforms
sw.plot_unit_waveforms(analyzer, unit_ids=[unit_id])
plt.savefig(f'unit_{unit_id}_waveforms.png')
# With density map
sw.plot_unit_waveforms_density_map(analyzer, unit_ids=[unit_id])
plt.savefig(f'unit_{unit_id}_density.png')Raw lines 68 to 8182### Template Comparison
84```python
85# Compare multiple units
86unit_ids = [0, 1, 2, 3]
87sw.plot_unit_templates(analyzer, unit_ids=unit_ids)
88plt.savefig('template_comparison.png')
89```
Template Comparison
# Compare multiple units
unit_ids = [0, 1, 2, 3]
sw.plot_unit_templates(analyzer, unit_ids=unit_ids)
plt.savefig('template_comparison.png')Raw lines 82 to 9091### Waveforms on Probe
93```python
94# Show waveforms spatially on probe
95sw.plot_unit_waveforms(
96 analyzer,
97 unit_ids=[unit_id],
98 plot_channels=True,
99)
100plt.savefig(f'unit_{unit_id}_probe.png')
101```
Waveforms on Probe
# Show waveforms spatially on probe
sw.plot_unit_waveforms(
analyzer,
unit_ids=[unit_id],
plot_channels=True,
)
plt.savefig(f'unit_{unit_id}_probe.png')Raw lines 91 to 102Quality Metrics Visualization
Raw lines 103 to 104105### Metrics Overview
107```python
108# Built-in quality-metrics widget (scatter matrix of all computed metrics)
109sw.plot_quality_metrics(analyzer)
110plt.savefig('quality_overview.png', dpi=300, bbox_inches='tight')
111```
Metrics Overview
# Built-in quality-metrics widget (scatter matrix of all computed metrics)
sw.plot_quality_metrics(analyzer)
plt.savefig('quality_overview.png', dpi=300, bbox_inches='tight')Raw lines 105 to 112113### Metrics Distribution
115```python
116fig, axes = plt.subplots(2, 3, figsize=(12, 8))
118metric_names = ['snr', 'isi_violations_ratio', 'presence_ratio',
119 'amplitude_cutoff', 'firing_rate', 'amplitude_cv_median']
121for ax, metric in zip(axes.flat, metric_names):
122 if metric in metrics.columns:
123 values = metrics[metric].dropna()
124 ax.hist(values, bins=30, edgecolor='black', alpha=0.7)
125 ax.axvline(values.median(), color='red', linestyle='--', label='median')
126 ax.set_xlabel(metric)
127 ax.set_ylabel('Count')
128 ax.legend()
130plt.tight_layout()
131plt.savefig('metrics_distribution.png', dpi=300)
132```
Metrics Distribution
fig, axes = plt.subplots(2, 3, figsize=(12, 8))
metric_names = ['snr', 'isi_violations_ratio', 'presence_ratio',
'amplitude_cutoff', 'firing_rate', 'amplitude_cv_median']
for ax, metric in zip(axes.flat, metric_names):
if metric in metrics.columns:
values = metrics[metric].dropna()
ax.hist(values, bins=30, edgecolor='black', alpha=0.7)
ax.axvline(values.median(), color='red', linestyle='--', label='median')
ax.set_xlabel(metric)
ax.set_ylabel('Count')
ax.legend()
plt.tight_layout()
plt.savefig('metrics_distribution.png', dpi=300)Raw lines 113 to 133134### Metrics Scatter Matrix
136```python
137import pandas as pd
139key_metrics = ['snr', 'isi_violations_ratio', 'presence_ratio', 'firing_rate']
140pd.plotting.scatter_matrix(
141 metrics[key_metrics],
142 figsize=(10, 10),
143 alpha=0.5,
144 diagonal='hist',
145)
146plt.savefig('metrics_scatter.png', dpi=300)
147```
Metrics Scatter Matrix
import pandas as pd
key_metrics = ['snr', 'isi_violations_ratio', 'presence_ratio', 'firing_rate']
pd.plotting.scatter_matrix(
metrics[key_metrics],
figsize=(10, 10),
alpha=0.5,
diagonal='hist',
)
plt.savefig('metrics_scatter.png', dpi=300)Raw lines 134 to 148149### Metrics vs Labels
151```python
152labels_series = pd.Series(labels)
154fig, axes = plt.subplots(1, 3, figsize=(12, 4))
156for ax, metric in zip(axes, ['snr', 'isi_violations_ratio', 'presence_ratio']):
157 for label in ['good', 'mua', 'noise']:
158 mask = labels_series == label
159 if mask.any():
160 ax.hist(metrics.loc[mask.index[mask], metric],
161 alpha=0.5, label=label, bins=20)
162 ax.set_xlabel(metric)
163 ax.legend()
165plt.tight_layout()
166plt.savefig('metrics_by_label.png', dpi=300)
167```
Metrics vs Labels
labels_series = pd.Series(labels)
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
for ax, metric in zip(axes, ['snr', 'isi_violations_ratio', 'presence_ratio']):
for label in ['good', 'mua', 'noise']:
mask = labels_series == label
if mask.any():
ax.hist(metrics.loc[mask.index[mask], metric],
alpha=0.5, label=label, bins=20)
ax.set_xlabel(metric)
ax.legend()
plt.tight_layout()
plt.savefig('metrics_by_label.png', dpi=300)Raw lines 149 to 168Correlogram Plots
Raw lines 169 to 170171### Autocorrelogram
173```python
174sw.plot_autocorrelograms(
175 analyzer,
176 unit_ids=[unit_id],
177 window_ms=50,
178 bin_ms=1,
179)
180plt.savefig(f'unit_{unit_id}_acg.png')
181```
Autocorrelogram
sw.plot_autocorrelograms(
analyzer,
unit_ids=[unit_id],
window_ms=50,
bin_ms=1,
)
plt.savefig(f'unit_{unit_id}_acg.png')Raw lines 171 to 182183### Cross-correlograms
185```python
186analyzer.compute("template_similarity")
187unit_pairs = [(0, 1), (0, 2), (1, 2)]
188sw.plot_crosscorrelograms(
189 analyzer,
190 unit_ids=sorted(set(uid for pair in unit_pairs for uid in pair)),
191 window_ms=50,
192 bin_ms=1,
193)
194plt.savefig('crosscorrelograms.png')
195```
Cross-correlograms
analyzer.compute("template_similarity")
unit_pairs = [(0, 1), (0, 2), (1, 2)]
sw.plot_crosscorrelograms(
analyzer,
unit_ids=sorted(set(uid for pair in unit_pairs for uid in pair)),
window_ms=50,
bin_ms=1,
)
plt.savefig('crosscorrelograms.png')Raw lines 183 to 196197### Correlogram Matrix
199```python
200sw.plot_autocorrelograms(
201 analyzer,
202 unit_ids=analyzer.sorting.unit_ids[:10], # First 10 units
203)
204plt.savefig('acg_matrix.png')
205```
Correlogram Matrix
sw.plot_autocorrelograms(
analyzer,
unit_ids=analyzer.sorting.unit_ids[:10], # First 10 units
)
plt.savefig('acg_matrix.png')Raw lines 197 to 206Spike Train Plots
Raw lines 207 to 208209### Raster Plot
211```python
212sw.plot_rasters(
213 sorting,
214 time_range=(0, 30), # First 30 seconds
215 unit_ids=unit_ids[:5],
216)
217plt.savefig('raster.png')
218```
Raster Plot
sw.plot_rasters(
sorting,
time_range=(0, 30), # First 30 seconds
unit_ids=unit_ids[:5],
)
plt.savefig('raster.png')Raw lines 209 to 219220### Firing Rate Over Time
222```python
223unit_id = 0
224spike_train = sorting.get_unit_spike_train(unit_id)
225fs = recording.get_sampling_frequency()
226times = spike_train / fs
228# Compute firing rate histogram
229bin_width = 1.0 # seconds
230duration = recording.get_total_duration()
231bins = np.append(np.arange(0, duration, bin_width), duration)
232hist, _ = np.histogram(times, bins=bins)
233firing_rate = hist / np.diff(bins)
235plt.figure(figsize=(12, 3))
236plt.bar(bins[:-1], firing_rate, width=np.diff(bins), align="edge", edgecolor='none')
237plt.xlabel('Time (s)')
238plt.ylabel('Firing rate (Hz)')
239plt.title(f'Unit {unit_id} firing rate')
240plt.savefig(f'unit_{unit_id}_firing_rate.png', dpi=300)
241```
Firing Rate Over Time
unit_id = 0
spike_train = sorting.get_unit_spike_train(unit_id)
fs = recording.get_sampling_frequency()
times = spike_train / fs
# Compute firing rate histogram
bin_width = 1.0 # seconds
duration = recording.get_total_duration()
bins = np.append(np.arange(0, duration, bin_width), duration)
hist, _ = np.histogram(times, bins=bins)
firing_rate = hist / np.diff(bins)
plt.figure(figsize=(12, 3))
plt.bar(bins[:-1], firing_rate, width=np.diff(bins), align="edge", edgecolor='none')
plt.xlabel('Time (s)')
plt.ylabel('Firing rate (Hz)')
plt.title(f'Unit {unit_id} firing rate')
plt.savefig(f'unit_{unit_id}_firing_rate.png', dpi=300)Raw lines 220 to 242Probe and Location Plots
Raw lines 243 to 244245### Probe Layout
247```python
248sw.plot_probe_map(recording, with_channel_ids=True)
249plt.savefig('probe_layout.png')
250```
Probe Layout
sw.plot_probe_map(recording, with_channel_ids=True)
plt.savefig('probe_layout.png')Raw lines 245 to 251252### Unit Locations on Probe
254```python
255sw.plot_unit_locations(analyzer, with_channel_ids=True)
256plt.savefig('unit_locations.png')
257```
Unit Locations on Probe
sw.plot_unit_locations(analyzer, with_channel_ids=True)
plt.savefig('unit_locations.png')Raw lines 252 to 258259### Spike Locations
261```python
262sw.plot_spike_locations(analyzer, unit_ids=[unit_id])
263plt.savefig(f'unit_{unit_id}_spike_locations.png')
264```
Spike Locations
sw.plot_spike_locations(analyzer, unit_ids=[unit_id])
plt.savefig(f'unit_{unit_id}_spike_locations.png')Raw lines 259 to 265Amplitude Plots
Raw lines 266 to 267268### Amplitudes Over Time
270```python
271sw.plot_amplitudes(
272 analyzer,
273 unit_ids=[unit_id],
274 plot_histograms=True,
275)
276plt.savefig(f'unit_{unit_id}_amplitudes.png')
277```
Amplitudes Over Time
sw.plot_amplitudes(
analyzer,
unit_ids=[unit_id],
plot_histograms=True,
)
plt.savefig(f'unit_{unit_id}_amplitudes.png')Raw lines 268 to 278279### Amplitude Distribution
281```python
282amplitudes = analyzer.get_extension('spike_amplitudes').get_data()
283spike_vector = sorting.to_spike_vector()
284unit_idx = list(sorting.unit_ids).index(unit_id)
285unit_mask = spike_vector['unit_index'] == unit_idx
286unit_amps = amplitudes[unit_mask]
288fig, ax = plt.subplots(figsize=(6, 4))
289ax.hist(unit_amps, bins=50, edgecolor='black', alpha=0.7)
290ax.axvline(np.median(unit_amps), color='red', linestyle='--', label='median')
291ax.set_xlabel('Amplitude (uV)')
292ax.set_ylabel('Count')
293ax.set_title(f'Unit {unit_id} Amplitude Distribution')
294ax.legend()
295plt.savefig(f'unit_{unit_id}_amp_dist.png', dpi=300)
296```
Amplitude Distribution
amplitudes = analyzer.get_extension('spike_amplitudes').get_data()
spike_vector = sorting.to_spike_vector()
unit_idx = list(sorting.unit_ids).index(unit_id)
unit_mask = spike_vector['unit_index'] == unit_idx
unit_amps = amplitudes[unit_mask]
fig, ax = plt.subplots(figsize=(6, 4))
ax.hist(unit_amps, bins=50, edgecolor='black', alpha=0.7)
ax.axvline(np.median(unit_amps), color='red', linestyle='--', label='median')
ax.set_xlabel('Amplitude (uV)')
ax.set_ylabel('Count')
ax.set_title(f'Unit {unit_id} Amplitude Distribution')
ax.legend()
plt.savefig(f'unit_{unit_id}_amp_dist.png', dpi=300)Raw lines 279 to 297ISI Plots
Raw lines 298 to 299300### ISI Histogram
302```python
303sw.plot_isi_distribution(
304 analyzer,
305 unit_ids=[unit_id],
306 window_ms=100,
307 bin_ms=1,
308)
309plt.savefig(f'unit_{unit_id}_isi.png')
310```
ISI Histogram
sw.plot_isi_distribution(
analyzer,
unit_ids=[unit_id],
window_ms=100,
bin_ms=1,
)
plt.savefig(f'unit_{unit_id}_isi.png')Raw lines 300 to 311312### ISI with Refractory Markers
314```python
315spike_train = sorting.get_unit_spike_train(unit_id)
316fs = recording.get_sampling_frequency()
317isis = np.diff(spike_train) / fs * 1000 # ms
319fig, ax = plt.subplots(figsize=(8, 4))
320ax.hist(isis[isis < 100], bins=100, edgecolor='black', alpha=0.7)
321ax.axvline(1.5, color='red', linestyle='--', label='1.5ms refractory')
322ax.axvline(3.0, color='orange', linestyle='--', label='3ms threshold')
323ax.set_xlabel('ISI (ms)')
324ax.set_ylabel('Count')
325ax.set_title(f'Unit {unit_id} ISI Distribution')
326ax.legend()
327plt.savefig(f'unit_{unit_id}_isi_detailed.png', dpi=300)
328```
ISI with Refractory Markers
spike_train = sorting.get_unit_spike_train(unit_id)
fs = recording.get_sampling_frequency()
isis = np.diff(spike_train) / fs * 1000 # ms
fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(isis[isis < 100], bins=100, edgecolor='black', alpha=0.7)
ax.axvline(1.5, color='red', linestyle='--', label='1.5ms refractory')
ax.axvline(3.0, color='orange', linestyle='--', label='3ms threshold')
ax.set_xlabel('ISI (ms)')
ax.set_ylabel('Count')
ax.set_title(f'Unit {unit_id} ISI Distribution')
ax.legend()
plt.savefig(f'unit_{unit_id}_isi_detailed.png', dpi=300)Raw lines 312 to 329Summary Plots
Raw lines 330 to 331332### Unit Summary Panel
334```python
335# Built-in one-call summary (waveform, template, ACG, amplitudes, location)
336sw.plot_unit_summary(analyzer, unit_id=unit_id)
337plt.savefig(f'unit_{unit_id}_summary.png', dpi=300, bbox_inches='tight')
338```
Unit Summary Panel
# Built-in one-call summary (waveform, template, ACG, amplitudes, location)
sw.plot_unit_summary(analyzer, unit_id=unit_id)
plt.savefig(f'unit_{unit_id}_summary.png', dpi=300, bbox_inches='tight')Raw lines 332 to 339340### Manual Multi-Panel Summary
342```python
343fs = sorting.get_sampling_frequency()
344fig = plt.figure(figsize=(16, 12))
346# Waveforms
347ax1 = fig.add_subplot(2, 3, 1)
348wfs = analyzer.get_extension('waveforms').get_waveforms_one_unit(unit_id)
349for i in range(min(50, wfs.shape[0])):
350 ax1.plot(wfs[i, :, 0], 'k', alpha=0.1, linewidth=0.5)
351template = wfs.mean(axis=0)[:, 0]
352ax1.plot(template, 'b', linewidth=2)
353ax1.set_title('Waveforms')
355# Template: choose the unit's main channel, not an arbitrary probe channel.
356ax2 = fig.add_subplot(2, 3, 2)
357templates_ext = analyzer.get_extension('templates')
358template = templates_ext.get_unit_template(unit_id, operator='average')
359template_std = templates_ext.get_unit_template(unit_id, operator='std')
360main_channel = int(np.argmax(np.ptp(template, axis=0)))
361x = np.arange(template.shape[0]) / fs * 1000
362ax2.plot(x, template[:, main_channel], 'b', linewidth=2)
363ax2.fill_between(x, template[:, main_channel] - template_std[:, main_channel],
364 template[:, main_channel] + template_std[:, main_channel], alpha=0.3)
365ax2.set_xlabel('Time in waveform window (ms)')
366ax2.set_title('Template')
368# Autocorrelogram
369ax3 = fig.add_subplot(2, 3, 3)
370correlograms = analyzer.get_extension('correlograms')
371ccg, bins = correlograms.get_data()
372unit_idx = list(sorting.unit_ids).index(unit_id)
373ax3.bar(bins[:-1], ccg[unit_idx, unit_idx, :], width=bins[1]-bins[0], color='gray')
374ax3.axvline(0, color='r', linestyle='--', alpha=0.5)
375ax3.set_title('Autocorrelogram')
377# Amplitudes
378ax4 = fig.add_subplot(2, 3, 4)
379amps_ext = analyzer.get_extension('spike_amplitudes')
380amps = amps_ext.get_data()
381spike_vector = sorting.to_spike_vector()
382unit_mask = spike_vector['unit_index'] == unit_idx
383unit_times = spike_vector['sample_index'][unit_mask] / fs
384unit_amps = amps[unit_mask]
385ax4.scatter(unit_times, unit_amps, s=1, alpha=0.3)
386ax4.set_xlabel('Time (s)')
387ax4.set_ylabel('Amplitude')
388ax4.set_title('Amplitudes')
390# ISI
391ax5 = fig.add_subplot(2, 3, 5)
392isis = np.diff(sorting.get_unit_spike_train(unit_id)) / fs * 1000
393ax5.hist(isis[isis < 100], bins=50, color='gray', edgecolor='black')
394ax5.axvline(1.5, color='r', linestyle='--')
395ax5.set_xlabel('ISI (ms)')
396ax5.set_title('ISI Distribution')
398# Metrics
399ax6 = fig.add_subplot(2, 3, 6)
400unit_metrics = metrics.loc[unit_id]
401text_lines = [f"{k}: {v:.4f}" for k, v in unit_metrics.items() if not np.isnan(v)]
402ax6.text(0.1, 0.9, '\n'.join(text_lines[:8]), transform=ax6.transAxes,
403 verticalalignment='top', fontsize=10, family='monospace')
404ax6.axis('off')
405ax6.set_title('Metrics')
407plt.tight_layout()
408plt.savefig(f'unit_{unit_id}_full_summary.png', dpi=300)
409```
Manual Multi-Panel Summary
fs = sorting.get_sampling_frequency()
fig = plt.figure(figsize=(16, 12))
# Waveforms
ax1 = fig.add_subplot(2, 3, 1)
wfs = analyzer.get_extension('waveforms').get_waveforms_one_unit(unit_id)
for i in range(min(50, wfs.shape[0])):
ax1.plot(wfs[i, :, 0], 'k', alpha=0.1, linewidth=0.5)
template = wfs.mean(axis=0)[:, 0]
ax1.plot(template, 'b', linewidth=2)
ax1.set_title('Waveforms')
# Template: choose the unit's main channel, not an arbitrary probe channel.
ax2 = fig.add_subplot(2, 3, 2)
templates_ext = analyzer.get_extension('templates')
template = templates_ext.get_unit_template(unit_id, operator='average')
template_std = templates_ext.get_unit_template(unit_id, operator='std')
main_channel = int(np.argmax(np.ptp(template, axis=0)))
x = np.arange(template.shape[0]) / fs * 1000
ax2.plot(x, template[:, main_channel], 'b', linewidth=2)
ax2.fill_between(x, template[:, main_channel] - template_std[:, main_channel],
template[:, main_channel] + template_std[:, main_channel], alpha=0.3)
ax2.set_xlabel('Time in waveform window (ms)')
ax2.set_title('Template')
# Autocorrelogram
ax3 = fig.add_subplot(2, 3, 3)
correlograms = analyzer.get_extension('correlograms')
ccg, bins = correlograms.get_data()
unit_idx = list(sorting.unit_ids).index(unit_id)
ax3.bar(bins[:-1], ccg[unit_idx, unit_idx, :], width=bins[1]-bins[0], color='gray')
ax3.axvline(0, color='r', linestyle='--', alpha=0.5)
ax3.set_title('Autocorrelogram')
# Amplitudes
ax4 = fig.add_subplot(2, 3, 4)
amps_ext = analyzer.get_extension('spike_amplitudes')
amps = amps_ext.get_data()
spike_vector = sorting.to_spike_vector()
unit_mask = spike_vector['unit_index'] == unit_idx
unit_times = spike_vector['sample_index'][unit_mask] / fs
unit_amps = amps[unit_mask]
ax4.scatter(unit_times, unit_amps, s=1, alpha=0.3)
ax4.set_xlabel('Time (s)')
ax4.set_ylabel('Amplitude')
ax4.set_title('Amplitudes')
# ISI
ax5 = fig.add_subplot(2, 3, 5)
isis = np.diff(sorting.get_unit_spike_train(unit_id)) / fs * 1000
ax5.hist(isis[isis < 100], bins=50, color='gray', edgecolor='black')
ax5.axvline(1.5, color='r', linestyle='--')
ax5.set_xlabel('ISI (ms)')
ax5.set_title('ISI Distribution')
# Metrics
ax6 = fig.add_subplot(2, 3, 6)
unit_metrics = metrics.loc[unit_id]
text_lines = [f"{k}: {v:.4f}" for k, v in unit_metrics.items() if not np.isnan(v)]
ax6.text(0.1, 0.9, '\n'.join(text_lines[:8]), transform=ax6.transAxes,
verticalalignment='top', fontsize=10, family='monospace')
ax6.axis('off')
ax6.set_title('Metrics')
plt.tight_layout()
plt.savefig(f'unit_{unit_id}_full_summary.png', dpi=300)Raw lines 340 to 410Publication-Quality Settings
Raw lines 411 to 412413### Figure Sizes
415```python
416# Single column (3.5 inches)
417fig, ax = plt.subplots(figsize=(3.5, 3))
419# Double column (7 inches)
420fig, ax = plt.subplots(figsize=(7, 4))
422# Full page
423fig, ax = plt.subplots(figsize=(7, 9))
424```
Figure Sizes
# Single column (3.5 inches)
fig, ax = plt.subplots(figsize=(3.5, 3))
# Double column (7 inches)
fig, ax = plt.subplots(figsize=(7, 4))
# Full page
fig, ax = plt.subplots(figsize=(7, 9))Raw lines 413 to 425426### Font Settings
428```python
429plt.rcParams.update({
430 'font.size': 8,
431 'axes.titlesize': 9,
432 'axes.labelsize': 8,
433 'xtick.labelsize': 7,
434 'ytick.labelsize': 7,
435 'legend.fontsize': 7,
436 'font.family': 'Arial',
437})
438```
Font Settings
plt.rcParams.update({
'font.size': 8,
'axes.titlesize': 9,
'axes.labelsize': 8,
'xtick.labelsize': 7,
'ytick.labelsize': 7,
'legend.fontsize': 7,
'font.family': 'Arial',
})Raw lines 426 to 439440### Export Settings
442```python
443# For publications
444plt.savefig('figure.pdf', format='pdf', bbox_inches='tight')
445plt.savefig('figure.svg', format='svg', bbox_inches='tight')
447# High-res PNG
448plt.savefig('figure.png', dpi=600, bbox_inches='tight', facecolor='white')
449```
Export Settings
# For publications
plt.savefig('figure.pdf', format='pdf', bbox_inches='tight')
plt.savefig('figure.svg', format='svg', bbox_inches='tight')
# High-res PNG
plt.savefig('figure.png', dpi=600, bbox_inches='tight', facecolor='white')Raw lines 440 to 450451### Color Palettes
453```python
454# Colorblind-friendly
455colors = ['#0072B2', '#E69F00', '#009E73', '#CC79A7', '#F0E442']
457# For good/mua/noise
458label_colors = {'good': '#2ecc71', 'mua': '#f39c12', 'noise': '#e74c3c'}
459```
Color Palettes
# Colorblind-friendly
colors = ['#0072B2', '#E69F00', '#009E73', '#CC79A7', '#F0E442']
# For good/mua/noise
label_colors = {'good': '#2ecc71', 'mua': '#f39c12', 'noise': '#e74c3c'}Raw lines 451 to 459