Online Panel App Compiler

Run Panel code in your browser. Build apps with sliders, dropdowns and buttons that update tables, charts and numbers.

Python
import panel as pn

from sklearn.datasets import load_iris
from sklearn.metrics import accuracy_score
from xgboost import XGBClassifier

iris_df = load_iris(as_frame=True)
trees = pn.widgets.IntSlider(start=2, end=30, name="Number of trees")

def pipeline(trees):
    model = XGBClassifier(max_depth=2, n_estimators=trees)
    model.fit(iris_df.data, iris_df.target)
    accuracy = round(accuracy_score(iris_df.target, model.predict(iris_df.data)) * 100, 1)
    return pn.indicators.Number(
        name="Training accuracy",
        value=accuracy,
        format="{value}%",
        colors=[(97.5, "red"), (99.0, "orange"), (100, "green")],
    )

pn.Column(
    "Simple example of training an XGBoost classification model on the small Iris dataset.",
    iris_df.data.head(),
    "Move the slider below to change the number of training rounds for the XGBoost classifier. The training accuracy score will adjust accordingly.",
    trees,
    pn.bind(pipeline, trees),
).servable()

Panel, from the HoloViz project, turns Python functions into web apps and dashboards. You connect widgets such as sliders, dropdowns and buttons to a function, and Panel runs the function again each time a widget changes. It can show pandas tables, Matplotlib, Bokeh and Plotly charts, and HoloViews plots. People use it for data exploration tools, model demos and internal dashboards. This page is an online Panel compiler: the code runs in your browser, so you can try it without installing anything. Run the example first, then paste any snippet below into a new cell to try it.

What the example does

load_iris(as_frame=True) loads scikit-learn's built-in Iris dataset: 150 flowers, four measurements each and three species. pn.widgets.IntSlider makes a slider from 2 to 30 that starts at 2. pipeline() trains an XGBoost classifier with n_estimators set to the slider value, scores it on the data it trained on, and returns the accuracy as a pn.indicators.Number. The colors list shows the number in red up to 97.5%, orange up to 99% and green above that. pn.bind(pipeline, trees) calls pipeline() with the slider's value and calls it again whenever the slider moves. pn.Column stacks the text, the first five rows of data, the slider and the result, and .servable() marks the column as the app, which appears below the cell when it runs.

n_estimators counts boosting rounds: with three species, XGBoost adds three trees per round. Two rounds score 97.3%, and 27 or more score 100%. A perfect score on the training data does not show how the model does on flowers it has not seen.

Filter a table with widgets

pn.bind() takes several widgets. Each widget's value goes to the matching argument, and a change to any of them runs the function again:

import pandas as pd
import panel as pn

sales = pd.DataFrame({
    "region": ["North", "South", "North", "East", "South", "East", "North"],
    "product": ["Tea", "Tea", "Coffee", "Coffee", "Juice", "Tea", "Juice"],
    "units": [120, 80, 150, 60, 90, 110, 40],
})

region = pn.widgets.Select(name="Region", options=["All", "North", "South", "East"])
min_units = pn.widgets.IntSlider(name="Minimum units", start=0, end=150, step=10)

def filter_rows(region, min_units):
    rows = sales[sales["units"] >= min_units]
    if region != "All":
        rows = rows[rows["region"] == region]
    return pn.pane.DataFrame(rows, index=False)

pn.Column(region, min_units, pn.bind(filter_rows, region, min_units)).servable()

Update a Matplotlib chart

Wrap the figure in pn.pane.Matplotlib. Build it with matplotlib.figure.Figure rather than plt.subplots(): pyplot keeps every figure it creates open, and this function runs on every slider move.

import numpy as np
import panel as pn
from matplotlib.figure import Figure

freq = pn.widgets.FloatSlider(name="Frequency", start=0.5, end=5, step=0.5, value=1)

def plot(freq):
    x = np.linspace(0, 2 * np.pi, 200)
    fig = Figure(figsize=(6, 3))
    ax = fig.subplots()
    ax.plot(x, np.sin(freq * x))
    ax.set_title(f"sin({freq}x)")
    return pn.pane.Matplotlib(fig, dpi=96, tight=True)

pn.Column(freq, pn.bind(plot, freq)).servable()

Run code when a button is clicked

on_click() registers a function that runs on every click and receives the click event. Setting a pane's object replaces what it shows:

import panel as pn

task = pn.widgets.TextInput(name="Task", placeholder="Type a task")
add = pn.widgets.Button(name="Add", button_type="primary")
tasks = pn.pane.Markdown("No tasks yet.")
items = []

def add_task(event):
    if task.value:
        items.append(task.value)
        tasks.object = "\n".join(f"- {item}" for item in items)
        task.value = ""

add.on_click(add_task)

pn.Column(pn.Row(task, add), tasks).servable()

For HoloViews plots driven by dropdowns, see HoloViews + Panel.

Good to know

  • Mark the app with .servable(). A Panel object left on the last line, the way Jupyter shows one, displays an error here instead of the app. .show() and pn.serve() start a web server, so they fail with RuntimeError: Cannot serve application in pyodide context.
  • The app runs in its own copy of Python, which it loads when it starts. It cannot use variables or imports from other cells, so keep everything the app needs in one cell.