Iris Kmeans#

This app provides an example of building a simple dashboard using Panel. It demonstrates how to take the output of k-means clustering on the Iris dataset (performed using scikit-learn), parameterizing the number of clusters and the x and y variables to plot. The entire clustering and plotting pipeline is expressed as a single reactive function that returns a plot that responsively updates when one of the widgets changes.

import hvplot.pandas  # noqa: F401
import panel as pn

from bokeh.sampledata import iris
from sklearn.cluster import KMeans

pn.extension(sizing_mode='stretch_width')

Clustering#

get_clusters returns a labelled copy of the data instead of mutating it, so it can be cached on n_clusters and shared between sessions.

FLOWERS = iris.flowers
COLS = list(FLOWERS.columns)[:-1]

@pn.cache
def get_clusters(n_clusters):
    est = KMeans(n_clusters=n_clusters, n_init='auto').fit(FLOWERS[COLS].values)
    return FLOWERS.assign(labels=est.labels_.astype('str')).sort_values('labels')

def plot_clusters(x, y, n_clusters):
    flowers = get_clusters(n_clusters)
    centers = flowers.groupby('labels')[[x] if x == y else [x, y]].mean()
    return (
        flowers.hvplot.scatter(
            x, y, c='labels', size=100, height=500, responsive=True
        ) *
        centers.hvplot.scatter(
            x, y, marker='x', c='black', size=400, padding=0.1, line_width=5,
            height=500, responsive=True
        )
    )

App#

x = pn.ui.Select(label='x', options=COLS)
y = pn.ui.Select(label='y', options=COLS, value='sepal_width')
n_clusters = pn.ui.IntSlider(label='n_clusters', start=1, end=5, value=3)

widgets = pn.ui.Column(
    pn.ui.Markdown(
        "This app provides an example of **building a simple dashboard using Panel**.\n\n"
        "It demonstrates how to take the output of **k-means clustering on the Iris dataset** "
        "using scikit-learn, parameterizing the number of clusters and the variables to plot.\n\n"
        "The entire clustering and plotting pipeline is expressed as a **single reactive function** "
        "that responsively returns an updated plot when one of the widgets changes.\n\n"
        "The **`x` marks the center** of the cluster."
    ),
    x, y, n_clusters,
)

plot = pn.ui.HoloViews(pn.bind(plot_clusters, x, y, n_clusters))

page = pn.ui.Page(
    main=[plot],
    sidebar=[widgets],
    title='Iris K-Means Clustering'
)

page

Served App#

if pn.state.served:
    page.servable()