The purpose of this notebook is to illustrate how you can make an interactive and responsive tool for timeseries analysis of images by combining a Panel IntSlider
, some @pn.depends
annotated plotting functions and a few HoloViews DynamicMap
.
import numpy as np
import pandas as pd
import holoviews as hv
import hvplot.pandas
import panel as pn
hv.extension('bokeh')
pn.extension(sizing_mode="stretch_width")
def make_ts_data(n_timesteps):
data = pd.DataFrame(
{
"a": np.random.normal(size=(n_timesteps,)),
"b": np.random.normal(size=(n_timesteps,)),
"c": np.random.normal(size=(n_timesteps,)),
},
index=pd.Index(np.arange(n_timesteps), name="time", )
)
return data
ts_data = make_ts_data(1000)
ts_data.head()
HEIGHT=300
plot_a = ts_data.hvplot(y="a", responsive=True, height=HEIGHT)
plot_b = ts_data.hvplot(y="b", responsive=True, height=HEIGHT)
plot_c = ts_data.hvplot(y="c", responsive=True, height=HEIGHT)
plot_a + plot_b + plot_c
def get_image(frame):
return hv.Image(np.random.normal(size=(100, 100))).opts(height=HEIGHT, responsive=True)
get_image(100)
def get_vline(frame):
return hv.VLine(frame).opts(color="red")
get_vline(0.5)
app_bar = pn.Row(
pn.pane.Markdown("## TimeSeries Image Analysis - POC", style={"color": "white"}, width=500, sizing_mode="fixed", margin=(10,5,10,15)),
pn.Spacer(),
pn.pane.PNG("http://holoviews.org/_static/logo.png", height=50, sizing_mode="fixed", align="center"),
pn.pane.PNG("https://panel.holoviz.org/_static/logo_horizontal.png", height=50, sizing_mode="fixed", align="center"),
background="black",
)
app_bar
frame_slider = pn.widgets.IntSlider(name="Time", value=25, start=0, end=999)
@pn.depends(frame=frame_slider)
def image(frame):
return get_image(frame)
@pn.depends(frame=frame_slider)
def vline(frame):
return get_vline(frame)
vline_dmap = hv.DynamicMap(vline)
img_dmap = hv.DynamicMap(image)
plots = ((plot_a + plot_b + plot_c) * vline_dmap).cols(1)
app = pn.Column(
app_bar,
pn.Spacer(height=10),
frame_slider,
pn.Row(
plots,
pn.Column(
pn.Spacer(height=20),
img_dmap,
),
),
)
app
You are now ready to serve the app to your users via panel serve dynamic_timeseries_image_analysis.ipynb
The plots = ((plot_a + plot_b + plot_c) * vline_dmap).cols(1)
line is essential for making the app fast and responsive. Initially the plot_a + plot_b + plot_c
where regenerated together with the vline
everytime the frame_slider.value
was changed. That made the app slower because it had to recalculate and retransfer much more data.
Lets wrap it into nice template that can be served via panel serve dynamic_timeseries_image_analysis.ipynb
pn.template.FastListTemplate(
site="Panel", title="Dynamic Timeseries Image Analysis",
main=[
"The purpose of this app is to illustrate how you can **make an interactive and responsive tool for timeseries analysis of images** by combining a Panel `IntSlider`, some `@pn.depends` annotated plotting functions and a few HoloViews `DynamicMap`.",
*app[2:]]
).servable();