Panel
Python library for creating interactive dashboards and applications from Python objects
Our View
Assess whether this fits the group’s research and teaching workflows. Panel makes it easy to turn your Jupyter notebooks into interactive dashboards without learning web development. You can create widgets, layouts, and deploy applications directly from Python, making your analyses interactive and shareable with collaborators.
Provisional positions are editorial proposals awaiting group review.
Teaching — Recorded in topic offerings; consult the dated event links for availability.
0 topics with recorded teaching · 0 past offerings · 0 upcoming offerings
Related themes: Scientific Computing
Related topics: Robust and Reusable Software
Common use cases
- Creating interactive data exploration dashboards
- Building parameter exploration tools
- Sharing analyses with non-programmers
- Rapid prototyping of data applications
Getting started
Panel is a Python library for creating interactive dashboards, apps, and data exploration tools. It works with many plotting libraries (Matplotlib, Bokeh, Plotly) and can turn Jupyter notebooks into standalone applications.
Why Panel?
- Framework Agnostic: Works with any Python plotting library
- Notebook Integration: Build dashboards directly in Jupyter
- Reactive: Automatically updates when inputs change
- Deploy Anywhere: Jupyter, standalone server, or static HTML
- Rich Widgets: Extensive widget library for interactivity
Basic Concepts
Simple Dashboard
import panel as pn
pn.extension()
# Create a simple dashboard
text = pn.pane.Markdown('# My Dashboard')
plot = df.hvplot.line(x='time', y='value')
dashboard = pn.Column(text, plot)
dashboard.servable()
Widgets
# Create interactive widgets
slider = pn.widgets.FloatSlider(name='Threshold', start=0, end=10, value=5)
select = pn.widgets.Select(name='Channel', options=['Ch1', 'Ch2', 'Ch3'])
button = pn.widgets.Button(name='Update', button_type='primary')
# Display widgets
pn.Column(slider, select, button)
Building Interactive Dashboards
Reactive Functions
import panel as pn
import hvplot.pandas
pn.extension()
# Create widgets
channel_select = pn.widgets.Select(
name='Channel',
options=['Ch1', 'Ch2', 'Ch3', 'Ch4']
)
# Reactive function that updates when widget changes
@pn.depends(channel_select.param.value)
def plot_channel(channel):
filtered_df = df[df['channel'] == channel]
return filtered_df.hvplot.line(x='time', y='voltage', title=f'{channel} Voltage')
# Create dashboard
dashboard = pn.Column(
'# LFP Explorer',
channel_select,
plot_channel
)
dashboard.servable()
With Parameters
import param
class DataExplorer(param.Parameterized):
channel = param.Selector(default='Ch1', objects=['Ch1', 'Ch2', 'Ch3', 'Ch4'])
threshold = param.Number(default=5.0, bounds=(0, 10))
smooth = param.Boolean(default=False)
@param.depends('channel', 'threshold', 'smooth')
def view(self):
# Filter data
data = df[df['channel'] == self.channel]
data = data[data['value'] > self.threshold]
if self.smooth:
data['value'] = data['value'].rolling(10).mean()
return data.hvplot.line(x='time', y='value')
explorer = DataExplorer()
pn.Row(explorer.param, explorer.view).servable()
Layout Options
Columns and Rows
# Vertical layout
pn.Column(widget1, plot1, plot2)
# Horizontal layout
pn.Row(widget1, plot1)
# Nested layouts
pn.Column(
'# Dashboard',
pn.Row(widget1, widget2),
pn.Row(plot1, plot2)
)
Tabs
tabs = pn.Tabs(
('Overview', overview_plot),
('Detailed Analysis', detail_plot),
('Statistics', stats_table)
)
Templates
# Use built-in templates
template = pn.template.FastListTemplate(
title='Neural Data Explorer',
sidebar=[channel_select, threshold_slider],
main=[plot1, plot2]
)
template.servable()
Practical Examples
Calcium Imaging ROI Explorer
import panel as pn
import hvplot.pandas
pn.extension()
# Widgets
roi_selector = pn.widgets.IntSlider(name='ROI', start=1, end=100, value=1)
trial_selector = pn.widgets.IntSlider(name='Trial', start=1, end=50, value=1)
@pn.depends(roi_selector.param.value, trial_selector.param.value)
def plot_trace(roi, trial):
# Filter data
trace = traces_df[
(traces_df['roi'] == roi) &
(traces_df['trial'] == trial)
]
return trace.hvplot.line(
x='time', y='dF_F',
xlabel='Time (s)',
ylabel='ΔF/F',
title=f'ROI {roi}, Trial {trial}'
)
# Summary statistics
@pn.depends(roi_selector.param.value)
def show_stats(roi):
roi_data = traces_df[traces_df['roi'] == roi]
stats = {
'Mean Response': roi_data['dF_F'].mean(),
'Peak Response': roi_data['dF_F'].max(),
'Baseline': roi_data['dF_F'].iloc[:100].mean()
}
return pn.pane.DataFrame(pd.DataFrame([stats]))
dashboard = pn.Column(
'# Calcium Imaging Explorer',
pn.Row(roi_selector, trial_selector),
plot_trace,
'## ROI Statistics',
show_stats
)
dashboard.servable()
LFP Analysis Dashboard
# Widgets for filtering
freq_range = pn.widgets.RangeSlider(
name='Frequency Range (Hz)',
start=0, end=100, value=(4, 8), step=1
)
time_range = pn.widgets.RangeSlider(
name='Time Window (s)',
start=0, end=10, value=(0, 10), step=0.1
)
@pn.depends(freq_range.param.value, time_range.param.value)
def filtered_spectrogram(freq, time):
# Filter spectrogram data
filtered = spectrogram_df[
(spectrogram_df['frequency'] >= freq[0]) &
(spectrogram_df['frequency'] <= freq[1]) &
(spectrogram_df['time'] >= time[0]) &
(spectrogram_df['time'] <= time[1])
]
return filtered.hvplot.heatmap(
x='time', y='frequency', C='power',
cmap='viridis',
xlabel='Time (s)',
ylabel='Frequency (Hz)'
)
pn.Column(
'# LFP Spectrogram',
freq_range,
time_range,
filtered_spectrogram
).servable()
Statistical Comparison Tool
# Group comparison dashboard
group1_select = pn.widgets.MultiSelect(
name='Group 1 Subjects',
options=subject_list,
size=10
)
group2_select = pn.widgets.MultiSelect(
name='Group 2 Subjects',
options=subject_list,
size=10
)
metric_select = pn.widgets.Select(
name='Metric',
options=['reaction_time', 'accuracy', 'response_variability']
)
run_button = pn.widgets.Button(name='Run Comparison', button_type='primary')
def run_comparison(event):
from scipy import stats
import pingouin as pg
# Get data for selected groups
g1_data = df[df['subject'].isin(group1_select.value)][metric_select.value]
g2_data = df[df['subject'].isin(group2_select.value)][metric_select.value]
# Run t-test
result = pg.ttest(g1_data, g2_data)
# Create comparison plot
comparison_df = pd.DataFrame({
'Group 1': g1_data,
'Group 2': g2_data
})
plot = comparison_df.hvplot.violin(
ylabel=metric_select.value,
title=f'p = {result["p-val"].values[0]:.4f}'
)
results_pane.object = result
plot_pane.object = plot
run_button.on_click(run_comparison)
results_pane = pn.pane.DataFrame()
plot_pane = pn.pane.HoloViews()
dashboard = pn.Column(
'# Statistical Comparison',
pn.Row(group1_select, group2_select),
metric_select,
run_button,
plot_pane,
results_pane
)
dashboard.servable()
Deployment
Jupyter Notebook
# Display inline in notebook
dashboard.show()
# Or use .servable() at the end of the notebook
dashboard.servable()
Standalone Server
# Save dashboard to file: dashboard.py
# Run server
panel serve dashboard.py --show
Static HTML Export
dashboard.save('dashboard.html', embed=True)
Advanced Features
Caching for Performance
@pn.cache
def expensive_computation(param):
# Expensive operation
result = process_large_dataset(param)
return result
@pn.depends(slider.param.value)
def plot(value):
data = expensive_computation(value) # Cached
return data.hvplot()
Custom JavaScript
# Add custom interactivity
js_code = """
console.log('Value changed to:', value);
"""
widget.jscallback(args={'value': widget}, value=js_code)
Authentication
# Add basic auth
pn.serve(
dashboard,
port=5006,
basic_auth={'username': 'password'}
)
Installation
pixi add panel
# or
conda install -c conda-forge panel
# or
pip install panel
For Jupyter notebook support:
jupyter labextension install @pyviz/jupyterlab_pyviz
Best Practices
- Use
.servable()at the end of notebooks for deployment - Cache expensive computations with
@pn.cache - Organize complex dashboards with tabs and layouts
- Use templates for professional-looking apps
- Test responsiveness with different screen sizes
- Document widget purposes for users
- Consider loading indicators for slow operations
- Use param.Parameterized for complex state management