Tutorial 5: Interactive Visualization with Plotly#
This tutorial demonstrates how to create interactive visualizations using the braintools.visualize module with Plotly backend. We’ll cover:
Interactive spike raster plots with zoom/pan capabilities
Interactive line plots with hover information
Interactive heatmaps for connectivity analysis
Interactive 3D scatter plots for high-dimensional data
Interactive network visualizations
Building comprehensive neural activity dashboards
Export options for interactive plots
Interactive plots provide several advantages over static plots:
Zoom and Pan: Explore data at different scales
Hover Information: Get detailed information on data points
Dynamic Filtering: Show/hide data series
3D Exploration: Rotate and examine 3D data from all angles
Export Options: Save as HTML, PNG, PDF, or SVG
Let’s start by importing the necessary modules.
# Core imports
import numpy as np
# Import braintools interactive visualization functions
import braintools
# Check if plotly is available
try:
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import plotly.offline as pyo
# Configure plotly for Jupyter
pyo.init_notebook_mode(connected=True)
print("✓ Plotly is available for interactive visualizations")
except ImportError:
print("⚠️ Plotly not available. Install with: pip install plotly")
print(" Interactive features will not work without Plotly.")
# Set random seed for reproducible results
np.random.seed(42)
⚠️ Plotly not available. Install with: pip install plotly
Interactive features will not work without Plotly.
1. Generate Synthetic Neural Data#
First, let’s create realistic synthetic neural data that we’ll use throughout this tutorial.
def generate_spike_data(n_neurons=50, duration=10.0, base_rate=5.0, burst_prob=0.1):
"""
Generate realistic spike data with occasional bursts.
Parameters:
- n_neurons: Number of neurons
- duration: Recording duration in seconds
- base_rate: Base firing rate in Hz
- burst_prob: Probability of burst events
"""
spike_times = []
neuron_ids = []
for neuron_id in range(n_neurons):
# Generate base Poisson spikes
n_spikes = np.random.poisson(base_rate * duration)
times = np.sort(np.random.uniform(0, duration, n_spikes))
# Add occasional bursts
for t in times:
if np.random.random() < burst_prob:
# Add 2-5 additional spikes in a 50ms window
burst_size = np.random.randint(2, 6)
burst_times = t + np.random.exponential(0.01, burst_size)
burst_times = burst_times[burst_times < duration]
times = np.concatenate([times, burst_times])
times = np.sort(times)
spike_times.extend(times)
neuron_ids.extend([neuron_id] * len(times))
return np.array(spike_times), np.array(neuron_ids)
def generate_lfp_data(duration=10.0, sampling_rate=1000.0, n_channels=8):
"""
Generate synthetic Local Field Potential (LFP) data.
"""
n_samples = int(duration * sampling_rate)
time = np.linspace(0, duration, n_samples)
lfp_data = np.zeros((n_samples, n_channels))
for ch in range(n_channels):
# Mix different frequency components
theta = 0.5 * np.sin(2 * np.pi * 8 * time + ch * 0.3) # 8 Hz theta
alpha = 0.3 * np.sin(2 * np.pi * 12 * time + ch * 0.2) # 12 Hz alpha
beta = 0.2 * np.sin(2 * np.pi * 25 * time + ch * 0.1) # 25 Hz beta
gamma = 0.1 * np.sin(2 * np.pi * 60 * time + ch * 0.05) # 60 Hz gamma
# Add noise
noise = 0.1 * np.random.randn(n_samples)
lfp_data[:, ch] = theta + alpha + beta + gamma + noise
return time, lfp_data
def generate_connectivity_matrix(n_nodes=20, connection_prob=0.3, weight_std=1.0):
"""
Generate a random connectivity matrix with realistic properties.
"""
# Start with random connections
conn_matrix = np.random.binomial(1, connection_prob, (n_nodes, n_nodes))
# Remove self-connections
np.fill_diagonal(conn_matrix, 0)
# Add weights
weights = np.random.normal(0, weight_std, (n_nodes, n_nodes))
conn_matrix = conn_matrix * weights
return conn_matrix
# Generate data for the tutorial
print("Generating synthetic neural data...")
# Spike data
spike_times, neuron_ids = generate_spike_data(n_neurons=30, duration=5.0)
print(f"✓ Generated {len(spike_times)} spikes from {len(np.unique(neuron_ids))} neurons")
# LFP data
time_lfp, lfp_signals = generate_lfp_data(duration=5.0, n_channels=6)
print(f"✓ Generated LFP data: {lfp_signals.shape[0]} samples × {lfp_signals.shape[1]} channels")
# Connectivity data
connectivity = generate_connectivity_matrix(n_nodes=15)
print(f"✓ Generated connectivity matrix: {connectivity.shape}")
# High-dimensional data for 3D plotting
n_points = 200
high_dim_data = np.random.multivariate_normal(
mean=[0, 0, 0],
cov=[[2, 0.5, 0.2], [0.5, 1.5, 0.3], [0.2, 0.3, 1.0]],
size=n_points
)
print(f"✓ Generated 3D scatter data: {high_dim_data.shape}")
print("\nData generation complete! 🎉")
Generating synthetic neural data...
✓ Generated 1040 spikes from 30 neurons
✓ Generated LFP data: 5000 samples × 6 channels
✓ Generated connectivity matrix: (15, 15)
✓ Generated 3D scatter data: (200, 3)
Data generation complete! 🎉
2. Interactive Spike Raster Plots#
Spike raster plots show when individual neurons fire. Interactive versions allow you to:
Zoom into specific time windows
Pan across the recording
Hover over spikes to see exact timing
Color-code by neuron or time
# Basic interactive spike raster plot
print("Creating basic interactive spike raster plot...")
fig1 = braintools.visualize.interactive_spike_raster(
spike_times=spike_times,
neuron_ids=neuron_ids,
title="Interactive Spike Raster Plot - Zoom and Pan Enabled",
width=900,
height=500
)
# Show the plot
fig1.show()
print("\n📝 Interactive Features:")
print(" • Zoom: Draw a box to zoom into a region")
print(" • Pan: Click and drag to move around")
print(" • Hover: Move mouse over spikes for details")
print(" • Reset: Double-click to return to original view")
Creating basic interactive spike raster plot...
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
Cell In[3], line 3
1 # Basic interactive spike raster plot
2 print("Creating basic interactive spike raster plot...")
----> 3 fig1 = braintools.visualize.interactive_spike_raster(
4 spike_times=spike_times,
5 neuron_ids=neuron_ids,
6 title="Interactive Spike Raster Plot - Zoom and Pan Enabled",
7 width=900,
8 height=500
9 )
11 # Show the plot
12 fig1.show()
File D:\codes\projects\braintools\braintools\visualize\interactive.py:92, in interactive_spike_raster(spike_times, neuron_ids, time_range, neuron_range, color_by, title, width, height, **kwargs)
54 def interactive_spike_raster(
55 spike_times: Union[np.ndarray, List],
56 neuron_ids: Optional[Union[np.ndarray, List]] = None,
(...)
63 **kwargs
64 ):
65 """
66 Create interactive spike raster plot using Plotly.
67
(...)
90 Interactive plotly figure.
91 """
---> 92 _check_plotly()
94 # Convert to numpy arrays
95 spike_times = as_numpy(spike_times)
File D:\codes\projects\braintools\braintools\visualize\interactive.py:51, in _check_plotly()
49 """Check if plotly is available."""
50 if not PLOTLY_AVAILABLE:
---> 51 raise ImportError("Plotly is required for interactive plots. Install with: pip install plotly")
ImportError: Plotly is required for interactive plots. Install with: pip install plotly
# Spike raster with color coding by neuron
print("Creating color-coded spike raster plot...")
fig2 = braintools.visualize.interactive_spike_raster(
spike_times=spike_times,
neuron_ids=neuron_ids,
color_by='neuron',
title="Spike Raster Plot - Colored by Neuron ID",
width=900,
height=500
)
fig2.show()
print("\n🎨 Color coding helps identify:")
print(" • Individual neuron firing patterns")
print(" • Synchronized activity across neurons")
print(" • Bursting behavior of specific neurons")
Creating color-coded spike raster plot...
🎨 Color coding helps identify:
• Individual neuron firing patterns
• Synchronized activity across neurons
• Bursting behavior of specific neurons
# Spike raster with time-based coloring and filtering
print("Creating time-filtered spike raster plot...")
# Focus on a specific time window and neuron range
fig3 = braintools.visualize.interactive_spike_raster(
spike_times=spike_times,
neuron_ids=neuron_ids,
time_range=(1.0, 4.0), # Focus on 1-4 second window
neuron_range=(5, 25), # Focus on neurons 5-25
color_by='time',
title="Filtered Spike Raster - Time Range: 1-4s, Neurons: 5-25",
width=900,
height=400
)
fig3.show()
print("\n🔍 Filtering capabilities:")
print(" • Time range filtering for focused analysis")
print(" • Neuron range filtering to examine subpopulations")
print(" • Temporal color coding shows spike timing patterns")
Creating time-filtered spike raster plot...
🔍 Filtering capabilities:
• Time range filtering for focused analysis
• Neuron range filtering to examine subpopulations
• Temporal color coding shows spike timing patterns
3. Interactive Line Plots with Hover Information#
Line plots are perfect for continuous signals like LFP, membrane potentials, or population activity. Interactive versions provide detailed information on hover and allow for easy comparison of multiple signals.
# Single channel LFP with hover information
print("Creating interactive LFP line plot...")
fig4 = braintools.visualize.interactive_line_plot(
x=time_lfp,
y=lfp_signals[:, 0], # First channel only
title="Interactive LFP Signal - Channel 1",
xlabel="Time (s)",
ylabel="Amplitude (mV)",
width=900,
height=400
)
fig4.show()
print("\n📊 Hover to see exact values at each time point!")
Creating interactive LFP line plot...
📊 Hover to see exact values at each time point!
# Multi-channel LFP comparison
print("Creating multi-channel LFP comparison...")
# Select a subset of channels for clarity
selected_channels = [0, 1, 2, 3]
lfp_subset = [lfp_signals[:, ch] for ch in selected_channels]
channel_labels = [f'Channel {ch + 1}' for ch in selected_channels]
fig5 = braintools.visualize.interactive_line_plot(
x=time_lfp,
y=lfp_subset,
labels=channel_labels,
title="Multi-Channel LFP Comparison",
xlabel="Time (s)",
ylabel="Amplitude (mV)",
width=900,
height=500
)
fig5.show()
print("\n🔗 Multi-trace features:")
print(" • Click legend items to show/hide traces")
print(" • Double-click legend to isolate a single trace")
print(" • Hover shows synchronized values across all traces")
Creating multi-channel LFP comparison...
🔗 Multi-trace features:
• Click legend items to show/hide traces
• Double-click legend to isolate a single trace
• Hover shows synchronized values across all traces
# Population activity with spike count overlay
print("Creating population activity analysis...")
# Calculate population firing rate over time
bin_size = 0.05 # 50ms bins
time_bins = np.arange(0, 5.0, bin_size)
spike_counts, _ = np.histogram(spike_times, bins=time_bins)
firing_rate = spike_counts / (bin_size * len(np.unique(neuron_ids))) # Hz
bin_centers = time_bins[:-1] + bin_size / 2
# Smooth the firing rate
from scipy.ndimage import gaussian_filter1d
firing_rate_smooth = gaussian_filter1d(firing_rate, sigma=2)
fig6 = braintools.visualize.interactive_line_plot(
x=bin_centers,
y=[firing_rate, firing_rate_smooth],
labels=['Raw Population Rate', 'Smoothed Population Rate'],
title="Population Firing Rate Over Time",
xlabel="Time (s)",
ylabel="Firing Rate (Hz)",
width=900,
height=400
)
fig6.show()
print("\n📈 Population activity insights:")
print(f" • Mean firing rate: {np.mean(firing_rate):.2f} Hz")
print(f" • Max firing rate: {np.max(firing_rate):.2f} Hz")
print(f" • Rate variability: {np.std(firing_rate):.2f} Hz")
Creating population activity analysis...
📈 Population activity insights:
• Mean firing rate: 6.94 Hz
• Max firing rate: 17.33 Hz
• Rate variability: 2.74 Hz
4. Interactive Heatmaps for Connectivity Analysis#
Heatmaps are excellent for visualizing matrices like connectivity, correlation, or cross-correlation data. Interactive versions allow detailed exploration of matrix elements.
# Basic connectivity heatmap
print("Creating interactive connectivity heatmap...")
# Create labels for nodes
node_labels = [f'N{i:02d}' for i in range(connectivity.shape[0])]
fig7 = braintools.visualize.interactive_heatmap(
data=connectivity,
x_labels=node_labels,
y_labels=node_labels,
title="Neural Connectivity Matrix",
colorscale='RdBu_r', # Red-Blue colorscale, reversed
width=700,
height=600
)
fig7.show()
print("\n🔗 Connectivity analysis:")
print(f" • Matrix size: {connectivity.shape}")
print(f" • Connection density: {np.mean(connectivity != 0):.1%}")
print(f" • Weight range: [{connectivity.min():.2f}, {connectivity.max():.2f}]")
print(" • Hover over cells to see exact connection weights")
Creating interactive connectivity heatmap...
🔗 Connectivity analysis:
• Matrix size: (15, 15)
• Connection density: 25.3%
• Weight range: [-2.64, 2.83]
• Hover over cells to see exact connection weights
# Cross-correlation heatmap
print("Creating cross-correlation heatmap...")
# Calculate cross-correlation between LFP channels
n_channels = lfp_signals.shape[1]
cross_corr = np.corrcoef(lfp_signals.T)
channel_names = [f'Ch{i + 1}' for i in range(n_channels)]
fig8 = braintools.visualize.interactive_heatmap(
data=cross_corr,
x_labels=channel_names,
y_labels=channel_names,
title="LFP Channel Cross-Correlation Matrix",
colorscale='RdBu_r',
width=600,
height=500
)
# Add custom colorbar range
fig8.update_traces(zmin=-1, zmax=1)
fig8.show()
print("\n📊 Cross-correlation insights:")
print(f" • Diagonal elements are always 1.0 (self-correlation)")
print(
f" • Off-diagonal range: [{cross_corr[np.triu_indices_from(cross_corr, k=1)].min():.3f}, {cross_corr[np.triu_indices_from(cross_corr, k=1)].max():.3f}]")
print(" • Red = positive correlation, Blue = negative correlation")
Creating cross-correlation heatmap...
📊 Cross-correlation insights:
• Diagonal elements are always 1.0 (self-correlation)
• Off-diagonal range: [0.283, 0.922]
• Red = positive correlation, Blue = negative correlation
# Time-resolved correlation heatmap
print("Creating time-resolved correlation heatmap...")
# Calculate sliding window correlation
window_size = 500 # samples
step_size = 100 # samples
n_windows = (len(time_lfp) - window_size) // step_size
# Focus on first two channels for simplicity
time_corr = np.zeros(n_windows)
window_times = np.zeros(n_windows)
for i in range(n_windows):
start_idx = i * step_size
end_idx = start_idx + window_size
# Calculate correlation in this window
corr = np.corrcoef(lfp_signals[start_idx:end_idx, 0],
lfp_signals[start_idx:end_idx, 1])[0, 1]
time_corr[i] = corr
window_times[i] = time_lfp[start_idx + window_size // 2]
# Create a 2D array for heatmap (correlation vs time)
corr_2d = time_corr.reshape(1, -1)
fig9 = braintools.visualize.interactive_heatmap(
data=corr_2d,
x_labels=[f'{t:.2f}s' for t in window_times[::5]], # Every 5th time point for clarity
y_labels=['Ch1-Ch2 Correlation'],
title="Time-Resolved Cross-Correlation (Ch1 vs Ch2)",
colorscale='RdBu_r',
width=900,
height=200
)
fig9.show()
print(f"\n⏱️ Time-resolved analysis:")
print(f" • Window size: {window_size / 1000:.1f}s")
print(f" • Step size: {step_size / 1000:.1f}s")
print(f" • Correlation range: [{time_corr.min():.3f}, {time_corr.max():.3f}]")
print(" • Shows how correlation changes over time")
Creating time-resolved correlation heatmap...
⏱️ Time-resolved analysis:
• Window size: 0.5s
• Step size: 0.1s
• Correlation range: [0.915, 0.929]
• Shows how correlation changes over time
5. Interactive 3D Scatter Plots for High-Dimensional Data#
3D scatter plots are perfect for visualizing high-dimensional neural data, such as:
Principal component analysis (PCA) results
Neural state spaces
Dimensionality reduction visualizations
# Basic 3D scatter plot
print("Creating basic 3D scatter plot...")
fig10 = braintools.visualize.interactive_3d_scatter(
x=high_dim_data[:, 0],
y=high_dim_data[:, 1],
z=high_dim_data[:, 2],
title="3D Neural State Space",
width=800,
height=600
)
fig10.show()
print("\n🎮 3D Interaction controls:")
print(" • Rotate: Click and drag")
print(" • Zoom: Mouse wheel or zoom box")
print(" • Pan: Shift + click and drag")
print(" • Reset: Double-click")
Creating basic 3D scatter plot...
🎮 3D Interaction controls:
• Rotate: Click and drag
• Zoom: Mouse wheel or zoom box
• Pan: Shift + click and drag
• Reset: Double-click
# 3D scatter with color coding and size variation
print("Creating enhanced 3D scatter plot...")
# Create color values based on distance from origin
distances = np.linalg.norm(high_dim_data, axis=1)
# Create size values based on another metric
sizes = 5 + 15 * (distances - distances.min()) / (distances.max() - distances.min())
# Create labels for hover
point_labels = [f'Point {i}: d={d:.2f}' for i, d in enumerate(distances)]
fig11 = braintools.visualize.interactive_3d_scatter(
x=high_dim_data[:, 0],
y=high_dim_data[:, 1],
z=high_dim_data[:, 2],
color=distances,
size=sizes,
labels=point_labels,
title="Enhanced 3D Scatter - Color by Distance, Size by Metric",
width=800,
height=600
)
fig11.show()
print("\n🌈 Enhanced features:")
print(" • Color coding reveals data structure")
print(" • Variable point sizes add another dimension")
print(" • Hover labels provide detailed information")
print(f" • Distance range: [{distances.min():.2f}, {distances.max():.2f}]")
Creating enhanced 3D scatter plot...
🌈 Enhanced features:
• Color coding reveals data structure
• Variable point sizes add another dimension
• Hover labels provide detailed information
• Distance range: [0.10, 5.37]
# PCA visualization of neural data
print("Creating PCA visualization...")
# Perform PCA on LFP data
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
# Standardize the LFP data
scaler = StandardScaler()
lfp_standardized = scaler.fit_transform(lfp_signals)
# Apply PCA
pca = PCA(n_components=3)
lfp_pca = pca.fit_transform(lfp_standardized)
# Color by time
time_colors = time_lfp
fig12 = braintools.visualize.interactive_3d_scatter(
x=lfp_pca[:, 0],
y=lfp_pca[:, 1],
z=lfp_pca[:, 2],
color=time_colors,
title=f"PCA of LFP Data - Explained Variance: {pca.explained_variance_ratio_.sum():.1%}",
width=800,
height=600
)
fig12.show()
print("\n📊 PCA Analysis:")
print(f" • PC1 variance explained: {pca.explained_variance_ratio_[0]:.1%}")
print(f" • PC2 variance explained: {pca.explained_variance_ratio_[1]:.1%}")
print(f" • PC3 variance explained: {pca.explained_variance_ratio_[2]:.1%}")
print(f" • Total variance explained: {pca.explained_variance_ratio_.sum():.1%}")
print(" • Colors show temporal progression")
Creating PCA visualization...
📊 PCA Analysis:
• PC1 variance explained: 79.7%
• PC2 variance explained: 17.0%
• PC3 variance explained: 1.0%
• Total variance explained: 97.6%
• Colors show temporal progression
6. Interactive Network Visualizations#
Network plots are ideal for showing connectivity patterns, functional networks, and graph-based neural data.
# Basic network visualization
print("Creating interactive network visualization...")
# Use a subset of the connectivity matrix for clarity
n_nodes = 10
small_connectivity = connectivity[:n_nodes, :n_nodes]
# Generate node labels and colors
node_labels = [f'N{i:02d}' for i in range(n_nodes)]
node_colors = np.sum(np.abs(small_connectivity), axis=1) # Color by total connectivity
fig13 = braintools.visualize.interactive_network(
adjacency=small_connectivity,
node_labels=node_labels,
node_colors=node_colors,
title="Interactive Neural Network - Hover over nodes and edges",
width=700,
height=600
)
fig13.show()
print("\n🕸️ Network features:")
print(" • Nodes represent neurons or brain regions")
print(" • Edge thickness shows connection strength")
print(" • Node colors indicate connectivity degree")
print(" • Hover for detailed connection information")
Creating interactive network visualization...
🕸️ Network features:
• Nodes represent neurons or brain regions
• Edge thickness shows connection strength
• Node colors indicate connectivity degree
• Hover for detailed connection information
# Advanced network with custom positions
print("Creating network with spatial layout...")
# Create a more structured layout (e.g., representing brain regions)
# Arrange nodes in a brain-like structure
angles = np.linspace(0, 2 * np.pi, n_nodes, endpoint=False)
radii = np.random.uniform(0.5, 1.5, n_nodes) # Vary distances from center
positions = np.column_stack([
radii * np.cos(angles),
radii * np.sin(angles)
])
# Calculate node metrics
in_degree = np.sum(small_connectivity != 0, axis=0) # Number of inputs
out_degree = np.sum(small_connectivity != 0, axis=1) # Number of outputs
total_degree = in_degree + out_degree
fig14 = braintools.visualize.interactive_network(
adjacency=small_connectivity,
positions=positions,
node_labels=[f'Region {chr(65 + i)}' for i in range(n_nodes)], # A, B, C, etc.
node_colors=total_degree,
title="Brain Network - Spatial Layout with Degree Centrality",
width=700,
height=600
)
fig14.show()
print("\n🧠 Network analysis:")
print(f" • Number of nodes: {n_nodes}")
print(f" • Number of edges: {np.sum(small_connectivity != 0)}")
print(f" • Network density: {np.sum(small_connectivity != 0) / (n_nodes * (n_nodes - 1)):.2%}")
print(f" • Degree range: [{total_degree.min()}, {total_degree.max()}]")
Creating network with spatial layout...
🧠 Network analysis:
• Number of nodes: 10
• Number of edges: 28
• Network density: 31.11%
• Degree range: [3, 8]
7. Comprehensive Neural Activity Dashboard#
Dashboards combine multiple visualizations into a single, comprehensive view. This is perfect for real-time monitoring or detailed analysis sessions.
# Create comprehensive dashboard
print("Creating comprehensive neural activity dashboard...")
# Prepare data for dashboard
# Convert spike times to list format (one array per neuron)
spike_lists = []
unique_neurons = np.unique(neuron_ids)
for neuron in unique_neurons:
neuron_spikes = spike_times[neuron_ids == neuron]
spike_lists.append(neuron_spikes)
# Calculate population activity
population_activity = firing_rate_smooth
# Create the dashboard
dashboard_fig = braintools.visualize.dashboard_neural_activity(
spike_times=spike_lists,
population_activity=population_activity,
time=bin_centers,
title="Neural Activity Dashboard - Complete Analysis",
width=1200,
height=800
)
dashboard_fig.show()
print("\n📊 Dashboard components:")
print(" • Top left: Spike raster plot")
print(" • Top right: Inter-spike interval (ISI) distribution")
print(" • Middle left: Population activity over time")
print(" • Middle right: Individual neuron firing rate distribution")
print(" • Bottom left: Spike count over time (binned)")
print(" • Bottom right: Summary statistics table")
print("\n🎯 Use this dashboard for:")
print(" • Quick overview of neural activity patterns")
print(" • Identifying population synchronization")
print(" • Detecting bursting or irregular firing")
print(" • Comparing activity across different conditions")
Creating comprehensive neural activity dashboard...
📊 Dashboard components:
• Top left: Spike raster plot
• Top right: Inter-spike interval (ISI) distribution
• Middle left: Population activity over time
• Middle right: Individual neuron firing rate distribution
• Bottom left: Spike count over time (binned)
• Bottom right: Summary statistics table
🎯 Use this dashboard for:
• Quick overview of neural activity patterns
• Identifying population synchronization
• Detecting bursting or irregular firing
• Comparing activity across different conditions
8. Export Options for Interactive Plots#
Interactive plots can be saved in various formats for different purposes:
HTML: Preserves full interactivity
PNG/JPG: Static high-quality images
PDF: Vector graphics for publications
SVG: Scalable vector graphics
# Demonstration of export options
print("Demonstrating export options...")
# Create a sample plot for export
sample_fig = braintools.visualize.interactive_line_plot(
x=time_lfp[::10], # Subsample for smaller file size
y=[lfp_signals[::10, 0], lfp_signals[::10, 1]],
labels=['Channel 1', 'Channel 2'],
title="Sample Plot for Export Demonstration",
xlabel="Time (s)",
ylabel="Amplitude (mV)",
width=800,
height=400
)
# Show the plot
sample_fig.show()
print("\n💾 Export options:")
print("")
print("1. HTML (Interactive):")
print(" fig.write_html('neural_activity.html')")
print(" • Preserves all interactive features")
print(" • Can be opened in any web browser")
print(" • Perfect for sharing with collaborators")
print("")
print("2. Static Images:")
print(" fig.write_image('plot.png', width=1200, height=800)")
print(" fig.write_image('plot.pdf') # Vector graphics")
print(" fig.write_image('plot.svg') # Scalable vector")
print(" • High-quality static versions")
print(" • Perfect for publications and presentations")
print("")
print("3. Programmatic Access:")
print(" fig.to_dict() # Convert to dictionary")
print(" fig.to_json() # Convert to JSON")
print(" • For custom processing or storage")
print("")
print("Note: Install kaleido for image export: pip install kaleido")
Demonstrating export options...
💾 Export options:
1. HTML (Interactive):
fig.write_html('neural_activity.html')
• Preserves all interactive features
• Can be opened in any web browser
• Perfect for sharing with collaborators
2. Static Images:
fig.write_image('plot.png', width=1200, height=800)
fig.write_image('plot.pdf') # Vector graphics
fig.write_image('plot.svg') # Scalable vector
• High-quality static versions
• Perfect for publications and presentations
3. Programmatic Access:
fig.to_dict() # Convert to dictionary
fig.to_json() # Convert to JSON
• For custom processing or storage
Note: Install kaleido for image export: pip install kaleido
# Example: Save dashboard as HTML (uncomment to actually save)
print("Example: Saving interactive dashboard...")
# Uncomment the line below to save the dashboard
# dashboard_fig.write_html("neural_activity_dashboard.html")
print("\n📁 File saving example:")
print('dashboard_fig.write_html("neural_activity_dashboard.html")')
print("\nThis would create an HTML file that can be:")
print(" • Opened in any web browser")
print(" • Shared via email or cloud storage")
print(" • Embedded in websites or reports")
print(" • Used offline without internet connection")
# Show file size information
dashboard_size = len(dashboard_fig.to_json()) / 1024 # KB
print(f"\n📊 Dashboard data size: ~{dashboard_size:.1f} KB")
Example: Saving interactive dashboard...
📁 File saving example:
dashboard_fig.write_html("neural_activity_dashboard.html")
This would create an HTML file that can be:
• Opened in any web browser
• Shared via email or cloud storage
• Embedded in websites or reports
• Used offline without internet connection
📊 Dashboard data size: ~46.7 KB
9. Advanced Interactive Features#
Let’s explore some advanced interactive features that make data exploration even more powerful.
# Advanced: Interactive correlation matrix with statistics
print("Creating advanced interactive correlation matrix...")
fig15 = braintools.visualize.interactive_correlation_matrix(
data=lfp_signals,
labels=channel_names,
method='pearson',
title="Interactive LFP Correlation Matrix with Values",
width=700,
height=600
)
fig15.show()
print("\n🔢 Advanced correlation features:")
print(" • Values displayed directly on the heatmap")
print(" • Symmetric matrix with diagonal = 1")
print(" • Color scale from -1 (anti-correlated) to +1 (correlated)")
print(" • Interactive hover shows exact correlation values")
Creating advanced interactive correlation matrix...
🔢 Advanced correlation features:
• Values displayed directly on the heatmap
• Symmetric matrix with diagonal = 1
• Color scale from -1 (anti-correlated) to +1 (correlated)
• Interactive hover shows exact correlation values
# Advanced: Interactive surface plot
print("Creating interactive 3D surface plot...")
# Create a 2D surface representing neural activity over space and time
x_space = np.linspace(-5, 5, 30)
y_time = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x_space, y_time)
# Simulate neural activity wave propagation
Z = np.sin(np.sqrt(X ** 2) - 2 * Y) * np.exp(-X ** 2 / 10) * np.exp(-Y / 5)
fig16 = braintools.visualize.interactive_surface(
z=Z,
x=x_space,
y=y_time,
colorscale='Viridis',
title="Interactive 3D Surface - Neural Activity Wave",
width=800,
height=600
)
# Customize the layout
fig16.update_layout(
scene=dict(
xaxis_title="Space (mm)",
yaxis_title="Time (s)",
zaxis_title="Activity Level"
)
)
fig16.show()
print("\n🌊 3D Surface features:")
print(" • Rotate to view from different angles")
print(" • Zoom to examine specific regions")
print(" • Hover to see exact coordinates and values")
print(" • Perfect for spatio-temporal neural data")
Creating interactive 3D surface plot...
🌊 3D Surface features:
• Rotate to view from different angles
• Zoom to examine specific regions
• Hover to see exact coordinates and values
• Perfect for spatio-temporal neural data
# Advanced: Interactive histogram comparison
print("Creating interactive histogram comparison...")
# Calculate different metrics from spike data
isi_data = []
spike_count_data = []
for neuron in unique_neurons:
neuron_spikes = spike_times[neuron_ids == neuron]
if len(neuron_spikes) > 1:
isis = np.diff(neuron_spikes)
isi_data.extend(isis)
spike_count_data.append(len(neuron_spikes))
# Create firing rate data from spike counts
firing_rates = np.array(spike_count_data) / 5.0 # Convert to Hz (5-second recording)
fig17 = braintools.visualize.interactive_histogram(
data=[isi_data, firing_rates],
labels=['Inter-Spike Intervals', 'Firing Rates'],
bins=25,
opacity=0.7,
title="Interactive Histogram Comparison - ISI vs Firing Rates",
xlabel="Value",
ylabel="Count",
width=900,
height=500
)
# Note: This creates overlaid histograms - you can toggle them on/off
fig17.show()
print("\n📊 Histogram comparison:")
print(f" • ISI statistics: mean={np.mean(isi_data):.3f}s, std={np.std(isi_data):.3f}s")
print(f" • Firing rate statistics: mean={np.mean(firing_rates):.2f}Hz, std={np.std(firing_rates):.2f}Hz")
print(" • Click legend items to show/hide distributions")
print(" • Overlaid histograms allow direct comparison")
Creating interactive histogram comparison...
📊 Histogram comparison:
• ISI statistics: mean=0.137s, std=0.182s
• Firing rate statistics: mean=6.93Hz, std=1.41Hz
• Click legend items to show/hide distributions
• Overlaid histograms allow direct comparison