{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[
](https://holoviews.holoviz.org)[
](https://panel.holoviz.org)\n",
"\n",
"# Timeseries Image Analysis with HoloViews and Panel \n",
"\n",
"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`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Dependencies"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"import holoviews as hv\n",
"import hvplot.pandas\n",
"import panel as pn\n",
"\n",
"hv.extension('bokeh')\n",
"pn.extension(sizing_mode=\"stretch_width\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def make_ts_data(n_timesteps):\n",
" data = pd.DataFrame(\n",
" {\n",
" \"a\": np.random.normal(size=(n_timesteps,)),\n",
" \"b\": np.random.normal(size=(n_timesteps,)),\n",
" \"c\": np.random.normal(size=(n_timesteps,)),\n",
" },\n",
" index=pd.Index(np.arange(n_timesteps), name=\"time\", )\n",
" )\n",
" return data\n",
"\n",
"ts_data = make_ts_data(1000)\n",
"ts_data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plots"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"HEIGHT=300"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plot_a = ts_data.hvplot(y=\"a\", responsive=True, height=HEIGHT)\n",
"plot_b = ts_data.hvplot(y=\"b\", responsive=True, height=HEIGHT)\n",
"plot_c = ts_data.hvplot(y=\"c\", responsive=True, height=HEIGHT)\n",
"\n",
"plot_a + plot_b + plot_c"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_image(frame):\n",
" return hv.Image(np.random.normal(size=(100, 100))).opts(height=HEIGHT, responsive=True)\n",
"\n",
"get_image(100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_vline(frame):\n",
" return hv.VLine(frame).opts(color=\"red\")\n",
"\n",
"get_vline(0.5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## App\n",
"\n",
"### Bar"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"app_bar = pn.Row(\n",
" pn.pane.Markdown(\"## TimeSeries Image Analysis - POC\", style={\"color\": \"white\"}, width=500, sizing_mode=\"fixed\", margin=(10,5,10,15)), \n",
" pn.Spacer(),\n",
" pn.pane.PNG(\"http://holoviews.org/_static/logo.png\", height=50, sizing_mode=\"fixed\", align=\"center\"),\n",
" pn.pane.PNG(\"https://panel.holoviz.org/_static/logo_horizontal.png\", height=50, sizing_mode=\"fixed\", align=\"center\"),\n",
" background=\"black\",\n",
")\n",
"app_bar"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dynamic Plots"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"frame_slider = pn.widgets.IntSlider(name=\"Time\", value=25, start=0, end=999)\n",
"\n",
"@pn.depends(frame=frame_slider)\n",
"def image(frame):\n",
" return get_image(frame)\n",
"\n",
"@pn.depends(frame=frame_slider)\n",
"def vline(frame):\n",
" return get_vline(frame)\n",
"\n",
"vline_dmap = hv.DynamicMap(vline)\n",
"img_dmap = hv.DynamicMap(image)\n",
"\n",
"plots = ((plot_a + plot_b + plot_c) * vline_dmap).cols(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Layout"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"app = pn.Column(\n",
" app_bar,\n",
" pn.Spacer(height=10),\n",
" frame_slider,\n",
" pn.Row(\n",
" plots,\n",
" pn.Column(\n",
" pn.Spacer(height=20),\n",
" img_dmap,\n",
" ),\n",
" ),\n",
")\n",
"app"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You are now ready to serve the app to your users via `panel serve dynamic_timeseries_image_analysis.ipynb`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Notes\n",
"\n",
"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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## App\n",
"\n",
"Lets wrap it into nice template that can be served via `panel serve dynamic_timeseries_image_analysis.ipynb`"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pn.template.FastListTemplate(\n",
" site=\"Panel\", title=\"Dynamic Timeseries Image Analysis\", \n",
" main=[\n",
" \"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`.\",\n",
" *app[2:]]\n",
").servable();"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}