Visualization Tutorial#
This tutorial covers NeuRodent’s comprehensive visualization capabilities for EEG analysis results.
Overview#
NeuRodent provides two main plotting classes:
AnimalPlotter: Visualize data from a single animal
ExperimentPlotter: Compare data across multiple animals with grouping
ZeitgeberPlotter: Circadian rhythm plots using Zeitgeber Time (ZT)
Both support various plot types: time series, categorical plots, heatmaps, and more.
import sys
from pathlib import Path
import logging
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from neurodent import core, visualization, constants
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger()
# Set plotting style
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (12, 6)
/home/runner/work/neurodent/neurodent/.venv/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
1. Single Animal Visualization (AnimalPlotter)#
Setup#
# Load WAR for a single animal
war_path = Path("/path/to/war/animal_001")
war = visualization.WindowAnalysisResult.load_pickle_and_json(war_path)
# Create plotter
ap = visualization.AnimalPlotter(war)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[2], line 3
1 # Load WAR for a single animal
2 war_path = Path("/path/to/war/animal_001")
----> 3 war = visualization.WindowAnalysisResult.load_pickle_and_json(war_path)
5 # Create plotter
6 ap = visualization.AnimalPlotter(war)
File ~/work/neurodent/neurodent/src/neurodent/visualization/results.py:4043, in WindowAnalysisResult.load_pickle_and_json(cls, folder_path, pickle_name, json_name)
4041 folder_path = Path(folder_path)
4042 if not folder_path.exists():
-> 4043 raise ValueError(f"Folder path {folder_path} does not exist")
4045 if pickle_name is not None:
4046 # Handle pickle_name as either absolute path or relative to folder_path
4047 pickle_path = Path(pickle_name)
ValueError: Folder path /path/to/war/animal_001 does not exist
Time Series Plots#
Visualize features over time:
# Plot RMS amplitude over time
fig = ap.plot_feature_over_time('rms')
plt.title("RMS Amplitude Over Time")
plt.show()
# Plot log RMS
fig = ap.plot_feature_over_time('logrms')
plt.title("Log RMS Amplitude Over Time")
plt.show()
Band Power Visualization#
Display power across frequency bands:
# Plot band powers
fig = ap.plot_psdband()
plt.title("Power Spectral Density by Band")
plt.show()
# Plot band powers over time
fig = ap.plot_psdband_over_time()
plt.title("Band Power Over Time")
plt.show()
2. Multi-Animal Comparison (ExperimentPlotter)#
Setup#
# Load multiple WARs
war_paths = [
Path("/path/to/war/animal_001"),
Path("/path/to/war/animal_002"),
Path("/path/to/war/animal_003"),
]
wars = [
visualization.WindowAnalysisResult.load_pickle_and_json(path)
for path in war_paths
]
# Create experiment plotter
ep = visualization.ExperimentPlotter(
wars,
exclude=['nspike', 'lognspike'] # Exclude features
)
Categorical Plots#
Compare features across groups using box plots, violin plots, swarm plots, etc.
# Box plot grouped by genotype
g = ep.plot_catplot(
'rms',
groupby='genotype',
kind='box',
catplot_params={'showfliers': False}
)
plt.suptitle("RMS by Genotype")
plt.show()
# Violin plot with day/night comparison
g = ep.plot_catplot(
'rms',
groupby=['genotype', 'isday'],
x='genotype',
col='isday',
kind='violin'
)
plt.suptitle("RMS by Genotype and Time of Day")
plt.show()
Band Power Comparisons#
# Box plot of band powers by genotype
g = ep.plot_catplot(
'psdband',
groupby='genotype',
x='genotype',
hue='band',
kind='box',
collapse_channels=True,
catplot_params={'showfliers': False}
)
plt.suptitle("Band Power by Genotype")
plt.show()
# With day/night split
g = ep.plot_catplot(
'psdband',
groupby=['genotype', 'isday'],
x='genotype',
col='isday',
hue='band',
kind='box',
collapse_channels=True,
catplot_params={'showfliers': False}
)
plt.suptitle("Band Power by Genotype and Time")
plt.show()
Swarm and Point Plots#
Show individual data points with averages:
# Swarm plot with animal-level averaging
g = ep.plot_catplot(
'rms',
groupby=['animal', 'genotype'],
x='genotype',
hue='channel',
kind='swarm',
average_groupby=True,
collapse_channels=False,
catplot_params={'dodge': True, 'errorbar': 'ci'}
)
plt.suptitle("RMS by Genotype (Animal Averages)")
plt.show()
# Point plot with confidence intervals
g = ep.plot_catplot(
'rms',
groupby=['animal', 'genotype'],
x='genotype',
kind='point',
average_groupby=True,
collapse_channels=True,
catplot_params={'errorbar': 'ci'}
)
plt.suptitle("RMS by Genotype (Mean ± CI)")
plt.show()
3. Connectivity Visualization#
Heatmaps for Coherence and Correlation#
# Coherence heatmap by genotype
g = ep.plot_heatmap(
'cohere',
groupby='genotype'
)
plt.suptitle("Coherence by Genotype")
plt.show()
# Pearson correlation heatmap
g = ep.plot_heatmap(
'pcorr',
groupby='genotype'
)
plt.suptitle("Correlation by Genotype")
plt.show()
# With day/night comparison
g = ep.plot_heatmap(
'cohere',
groupby=['genotype', 'isday']
)
plt.suptitle("Coherence by Genotype and Time")
plt.show()
Difference Heatmaps#
Show differences relative to a baseline condition:
# Difference from wildtype
g = ep.plot_diffheatmap(
'cohere',
groupby=['genotype', 'isday'],
baseline_key='WT',
baseline_groupby='genotype',
remove_baseline=True
)
plt.suptitle("Coherence Difference from WT")
plt.show()
# By frequency band
g = ep.plot_diffheatmap(
'cohere',
groupby='genotype',
baseline_key='WT',
baseline_groupby='genotype',
col='band',
row='genotype',
remove_baseline=True
)
plt.suptitle("Band-Specific Coherence Differences")
plt.show()
4. QQ Plots for Distribution Analysis#
# QQ plot by genotype and channel
g = ep.plot_qqplot(
'rms',
groupby=['genotype'],
row='genotype',
col='channel',
height=3
)
plt.suptitle("RMS Distribution QQ Plot")
plt.show()
5. Customizing Plots#
Plot Parameters#
# Custom catplot parameters
custom_params = {
'showfliers': False,
'aspect': 2,
'height': 5,
'palette': 'Set2'
}
g = ep.plot_catplot(
'rms',
groupby='genotype',
kind='box',
catplot_params=custom_params
)
plt.show()
Channel Collapsing#
Average across channels:
# Without channel collapsing (show all channels)
g = ep.plot_catplot(
'rms',
groupby='genotype',
kind='box',
collapse_channels=False
)
plt.suptitle("RMS by Channel")
plt.show()
# With channel collapsing (average across channels)
g = ep.plot_catplot(
'rms',
groupby='genotype',
kind='box',
collapse_channels=True
)
plt.suptitle("RMS (Averaged Across Channels)")
plt.show()
6. Saving Figures#
Save high-quality figures for publications:
# Create output directory
output_folder = Path("./figures")
output_folder.mkdir(parents=True, exist_ok=True)
# Generate and save plot
g = ep.plot_catplot(
'rms',
groupby='genotype',
kind='box',
catplot_params={'showfliers': False}
)
# Save as PNG (high DPI for publications)
g.savefig(output_folder / 'rms_by_genotype.png', dpi=300, bbox_inches='tight')
# Save as PDF (vector format)
g.savefig(output_folder / 'rms_by_genotype.pdf', bbox_inches='tight')
print(f"Figures saved to {output_folder}")
7. Batch Figure Generation#
Generate multiple figures systematically:
# Generate plots for all linear features
for feature in constants.LINEAR_FEATURES:
if feature not in ep.exclude:
logger.info(f"Generating plot for {feature}")
# Box plot
g = ep.plot_catplot(
feature,
groupby='genotype',
kind='box',
collapse_channels=True,
catplot_params={'showfliers': False}
)
g.savefig(
output_folder / f'{feature}_genotype_box.png',
dpi=300,
bbox_inches='tight'
)
plt.close()
print("Batch generation complete!")
8. Advanced: Custom Plot Types#
Access underlying data for custom visualizations:
# Get aggregated data from ExperimentPlotter
df = ep.get_dataframe('rms', groupby='genotype')
# Create custom plot with matplotlib
fig, ax = plt.subplots(figsize=(10, 6))
# Plot using pandas/seaborn directly
sns.boxplot(data=df, x='genotype', y='rms', ax=ax)
ax.set_title("Custom RMS Plot")
ax.set_ylabel("RMS Amplitude")
plt.show()
9. Circadian Rhythms (ZeitgeberPlotter)#
Visualize circadian patterns using ZeitgeberPlotter. This specialized plotter handles the 48-hour duplicated time axis often used in circadian research.
# From DataFrame (workflow usage)
zp = visualization.ZeitgeberPlotter(df_zar)
# Plot single feature over 48h cycle
zp.plot_feature(
'logrms_nobase', # Baseline-subtracted feature
output_path=output_folder / 'circadian_rms.png',
figsize=(10, 6)
)
plt.show()
Summary#
This tutorial covered:
Single animal visualization with AnimalPlotter
Multi-animal comparisons with ExperimentPlotter
Categorical plots (box, violin, swarm, point)
Connectivity visualizations (heatmaps)
Difference heatmaps
Distribution analysis (QQ plots)
Plot customization
Saving figures
Batch figure generation
Custom plot types
Plot Type Reference#
AnimalPlotter Methods#
plot_feature_over_time(feature): Time series plotplot_psdband(): Bar plot of band powersplot_psdband_over_time(): Band powers over time
ExperimentPlotter Methods#
plot_catplot(feature, ...): Categorical plots (box, violin, swarm, point, strip)plot_heatmap(feature, ...): Connectivity heatmapsplot_diffheatmap(feature, ...): Difference heatmapsplot_qqplot(feature, ...): Distribution QQ plots
Next Steps#
Windowed Analysis Tutorial: Generate data for visualization
Basic Usage Tutorial: Complete workflow