importScripts("https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"); function sendPatch(patch, buffers, msg_id) { self.postMessage({ type: 'patch', patch: patch, buffers: buffers }) } async function startApplication() { console.log("Loading pyodide!"); self.postMessage({type: 'status', msg: 'Loading pyodide'}) self.pyodide = await loadPyodide(); self.pyodide.globals.set("sendPatch", sendPatch); console.log("Loaded!"); await self.pyodide.loadPackage("micropip"); const env_spec = ['https://cdn.holoviz.org/panel/wheels/bokeh-3.3.2-py3-none-any.whl', 'https://cdn.holoviz.org/panel/1.3.6/dist/wheels/panel-1.3.6-py3-none-any.whl', 'pyodide-http==0.2.1', 'holoviews', 'hvplot', 'param', 'pandas'] for (const pkg of env_spec) { let pkg_name; if (pkg.endsWith('.whl')) { pkg_name = pkg.split('/').slice(-1)[0].split('-')[0] } else { pkg_name = pkg } self.postMessage({type: 'status', msg: `Installing ${pkg_name}`}) try { await self.pyodide.runPythonAsync(` import micropip await micropip.install('${pkg}'); `); } catch(e) { console.log(e) self.postMessage({ type: 'status', msg: `Error while installing ${pkg_name}` }); } } console.log("Packages loaded!"); self.postMessage({type: 'status', msg: 'Executing code'}) const code = ` import asyncio from panel.io.pyodide import init_doc, write_doc init_doc() #!/usr/bin/env python # coding: utf-8 # # The Easiest Way to Create an Interactive Dashboard in Python # # This notebook is an updated version of the original notebook supporting the blog post # # **[The Easiest Way to Create an Interactive Dashboard in Python](https://towardsdatascience.com/the-easiest-way-to-create-an-interactive-dashboard-in-python-77440f2511d1)**. Turn Pandas pipelines into a # dashboard using [\`param.rx\`](https://param.holoviz.org/user_guide/Reactive_Expressions.html) and [panel.ReactiveExpr](https://panel.holoviz.org/reference/panes/ReactiveExpr.html). # # by *Sophia Yang* and *Marc Skov Madsen*. # # ![Data App](https://github.com/sophiamyang/hvplot_interactive/blob/main/assets/easy-dataframe-dashboards.gif?raw=true) # ## Import and configure packages # # Please note that in **Colab** you will need to \`!pip install panel hvplot\`. In VS Code notebooks you will need to install \`!pip install panel hvplot jupyter_bokeh\`. # In[ ]: # !pip install panel==1.3.6 hvplot==0.9.1 # colab # !pip install panel==1.3.6 hvplot==0.9.1 jupyter_bokeh==3.0.7 # vscode # In[ ]: import param import panel as pn pn.extension('tabulator', sizing_mode="stretch_width") # In[ ]: import hvplot.pandas import holoviews as hv hv.extension('bokeh') # ## Define Color Palette # In[ ]: PALETTE = ["#ff6f69", "#ffcc5c", "#88d8b0", ] pn.Row( pn.layout.HSpacer(height=50, styles={"background": PALETTE[0]}), pn.layout.HSpacer(height=50, styles={"background": PALETTE[1]}), pn.layout.HSpacer(height=50, styles={"background": PALETTE[2]}), ) # ## Load Data # In[ ]: from bokeh.sampledata.autompg import autompg_clean as df df.head(3) # ## Define DataFrame Pipeline # In[ ]: def get_data(df, cylinders=4, mfr=['ford','chevrolet'], yaxis="hp"): return ( df[ (df.cyl == cylinders) & (df.mfr.isin(mfr)) ] .groupby(['origin', 'mpg'])[yaxis].mean() .to_frame() .reset_index() .sort_values(by='mpg') ) get_data(df).head(3) # ## Make DataFrame Pipeline Interactive # Define [Panel widgets](https://panel.holoviz.org/reference/index.html#widgets) # In[ ]: cylinders = pn.widgets.IntSlider(name='Cylinders', start=4, end=8, step=2) mfr = pn.widgets.ToggleGroup( name='MFR', options=['ford', 'chevrolet', 'honda', 'toyota', 'audi'], value=['ford', 'chevrolet', 'honda', 'toyota', 'audi'], button_type='primary', button_style="outline") yaxis = pn.widgets.RadioButtonGroup( name='Y axis', options=['hp', 'weight'], button_type='primary', button_style="outline" ) # Combine pipeline and widgets # In[ ]: ipipeline=param.rx(get_data)(df, cylinders, mfr, yaxis) ipipeline.head() # ## Pipe to Tabulator # In[ ]: itable = pn.widgets.Tabulator(ipipeline, pagination='remote', page_size=10) itable # Check out the [Tabulator Reference Guide](https://panel.holoviz.org/reference/widgets/Tabulator.html) for more inspiration. # ## Pipe to hvPlot and HoloViews # First we will create the interactive plot with [hvPlot](https://hvplot.holoviz.org/). # In[ ]: ihvplot = ipipeline.hvplot(x='mpg', y=yaxis, by='origin', color=PALETTE, line_width=6, height=400, responsive=True) ihvplot # The we will put it in a HoloViews pane # In[ ]: iplot = pn.pane.HoloViews(ihvplot) iplot # Check out the [HoloViews Reference Guide](https://panel.holoviz.org/reference/panes/HoloViews.html) for more inspiration # ## Layout using Template # # Here we use the [FastListTemplate](https://panel.holoviz.org/reference/templates/FastListTemplate.html#templates-gallery-fastlisttemplate). # In[ ]: template = pn.template.FastListTemplate( title='The easiest way to create a dashboard', sidebar=[cylinders, 'Manufacturers', mfr, 'Y axis' , yaxis], main=[iplot, itable], accent_base_color="#88d8b0", header_background="#88d8b0", # main_layout=None, # Use this if you want a gray sidebar and white main area ) template.servable(); # Add semicolon because templates don't render well in a notebook # To *serve the notebook* run \`panel serve the_easiest_way_to_create_dashboard.ipynb\`. await write_doc() ` try { const [docs_json, render_items, root_ids] = await self.pyodide.runPythonAsync(code) self.postMessage({ type: 'render', docs_json: docs_json, render_items: render_items, root_ids: root_ids }) } catch(e) { const traceback = `${e}` const tblines = traceback.split('\n') self.postMessage({ type: 'status', msg: tblines[tblines.length-2] }); throw e } } self.onmessage = async (event) => { const msg = event.data if (msg.type === 'rendered') { self.pyodide.runPythonAsync(` from panel.io.state import state from panel.io.pyodide import _link_docs_worker _link_docs_worker(state.curdoc, sendPatch, setter='js') `) } else if (msg.type === 'patch') { self.pyodide.globals.set('patch', msg.patch) self.pyodide.runPythonAsync(` state.curdoc.apply_json_patch(patch.to_py(), setter='js') `) self.postMessage({type: 'idle'}) } else if (msg.type === 'location') { self.pyodide.globals.set('location', msg.location) self.pyodide.runPythonAsync(` import json from panel.io.state import state from panel.util import edit_readonly if state.location: loc_data = json.loads(location) with edit_readonly(state.location): state.location.param.update({ k: v for k, v in loc_data.items() if k in state.location.param }) `) } } startApplication()