{ "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 }