Dynamic timeseries image analysis

Download this notebook from GitHub (right-click to download).


Timeseries Image Analysis with HoloViews and Panel

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.

Dependencies

In [ ]:
import numpy as np
import pandas as pd
import holoviews as hv
import hvplot.pandas
import panel as pn

hv.extension('bokeh')
pn.config.sizing_mode="stretch_width"

Data

In [ ]:
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()

Plots

In [ ]:
HEIGHT=300
In [ ]:
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
In [ ]:
def get_image(frame):
    return hv.Image(np.random.normal(size=(100, 100))).opts(height=HEIGHT, responsive=True)

get_image(100)
In [ ]:
def get_vline(frame):
    return hv.VLine(frame).opts(color="red")

get_vline(0.5)

App

Bar

In [ ]:
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

Dynamic Plots

In [ ]:
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)

Layout

In [ ]:
app = pn.Column(
    app_bar,
    pn.Spacer(height=10),
    frame_slider,
    pn.Row(
        plots,
        pn.Column(
            pn.Spacer(height=20),
            img_dmap,
        ),
    ),
)
app.servable()

You are now ready to serve the app to your users via panel serve dynamic_timeseries_image_analysis.ipynb

Notes

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.


Download this notebook from GitHub (right-click to download).