Penguin Kmeans#

import altair as alt
import panel as pn
import panel_material_ui as pmui
import pandas as pd

from sklearn.cluster import KMeans

pn.extension('tabulator', 'vega')

Load data#

penguins = pn.cache(pd.read_csv)('https://datasets.holoviz.org/penguins/v1/penguins.csv').dropna()
cols = list(penguins.columns)[2:6]

Define application#

@pn.cache
def get_clusters(n_clusters):
    kmeans = KMeans(n_clusters=n_clusters, n_init='auto')
    est = kmeans.fit(penguins[cols].values)
    df = penguins.copy()
    df['labels'] = est.labels_.astype('str')
    return df

@pn.cache
def get_chart(x, y, df):
    centers = df.groupby('labels')[[x] if x == y else [x, y]].mean()
    return (
        alt.Chart(df)
            .mark_point(size=100)
            .encode(
                x=alt.X(x, scale=alt.Scale(zero=False)),
                y=alt.Y(y, scale=alt.Scale(zero=False)),
                shape='labels',
                color='species'
            ).add_params(brush) +
        alt.Chart(centers)
            .mark_point(size=250, shape='cross', color='black')
            .encode(x=x+':Q', y=y+':Q')
    ).properties(width='container', height='container')

intro = pn.pane.Markdown("""
This app provides an example of **building a simple dashboard using
Panel**.\n\nIt demonstrates how to take the output of **k-means
clustering on the Penguins dataset** using scikit-learn,
parameterizing the number of clusters and the variables to
plot.\n\nThe plot and the table are linked, i.e. selecting on the plot
will filter the data in the table.\n\n The **`x` marks the center** of
the cluster.
""", sizing_mode='stretch_width')

x = pmui.Select(label='x', options=cols, value='bill_depth_mm')
y = pmui.Select(label='y', options=cols, value='bill_length_mm')
n_clusters = pmui.IntSlider(label='n_clusters', start=1, end=5, value=3)

brush = alt.selection_interval(name='brush')  # selection of type "interval"

clusters = pn.bind(get_clusters, n_clusters)

chart = pn.pane.Vega(
    pn.bind(get_chart, x, y, clusters), min_height=400, max_height=800, sizing_mode='stretch_width'
)

table = pn.widgets.Tabulator(
    clusters,
    pagination='remote', page_size=10, height=600,
    sizing_mode='stretch_width'
)

def vega_filter(filters, df):
    filtered = df
    for field, drange in (filters or {}).items():
        filtered = filtered[filtered[field].between(*drange)]
    return filtered

table.add_filter(pn.bind(vega_filter, chart.selection.param.brush))
Traceback (most recent call last):
  File "/Users/runner/work/panel/panel/panel/io/mime_render.py", line 165, in exec_with_return
    exec(compile(init_ast, "<ast>", "exec"), global_context)
  File "<ast>", line 44, in <module>
  File "/Users/runner/work/panel/panel/panel/pane/vega.py", line 238, in __init__
    super().__init__(object, **params)
  File "/Users/runner/work/panel/panel/panel/pane/base.py", line 323, in __init__
    super().__init__(object=object, **params)
  File "/Users/runner/work/panel/panel/panel/pane/base.py", line 159, in __init__
    super().__init__(object=object, **params)
  File "/Users/runner/work/panel/panel/panel/reactive.py", line 682, in __init__
    super().__init__(**params)
  File "/Users/runner/work/panel/panel/panel/reactive.py", line 130, in __init__
    super().__init__(**params)
  File "/Users/runner/work/panel/panel/panel/viewable.py", line 739, in __init__
    super().__init__(**params)
  File "/Users/runner/work/panel/panel/panel/viewable.py", line 570, in __init__
    super().__init__(**params)
  File "/Users/runner/work/panel/panel/panel/viewable.py", line 315, in __init__
    super().__init__(**params)
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 5879, in __init__
    refs, deps = self.param._setup_params(**params)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 2316, in override_initialization
    ret = fn(self_, *args, **kw)
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 2650, in _setup_params
    ref, ref_deps, resolved, is_async = self_._resolve_ref(pobj, val)
                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 2730, in _resolve_ref
    value = resolve_value(value, recursive=pobj.nested_refs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 266, in resolve_value
    value = eval_function_with_deps(value)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 247, in eval_function_with_deps
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/depends.py", line 125, in _depends_sync
    return func(*args, **kw)
           ^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/reactive.py", line 1352, in wrapped_sync
    combined_args, combined_kwargs = combine_arguments(wargs, wkwargs)
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/reactive.py", line 1273, in combine_arguments
    arg = eval_function_with_deps(arg)  # type: ignore[arg-type]
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/parameterized.py", line 247, in eval_function_with_deps
    return function(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/depends.py", line 125, in _depends_sync
    return func(*args, **kw)
           ^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/.pixi/envs/docs/lib/python3.11/site-packages/param/reactive.py", line 1353, in wrapped_sync
    return eval_fn()(*combined_args, **combined_kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/panel/io/cache.py", line 538, in wrapped_func
    func_cache, hash_value, time = hash_func(*args, **kwargs)
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/runner/work/panel/panel/panel/io/cache.py", line 490, in hash_func
    module = sys.modules[func.__module__]
             ~~~~~~~~~~~^^^^^^^^^^^^^^^^^
KeyError: None

Layout app#

pn.Row(
    pn.Column(x, y, n_clusters),
    pn.Column(
        intro, chart, table,
    ),
    sizing_mode='stretch_both',
    min_height=1000
)
Traceback (most recent call last):
  File "/Users/runner/work/panel/panel/panel/io/mime_render.py", line 169, in exec_with_return
    out = eval(compile(_convert_expr(last_ast.body[0]), "<ast>", "eval"), global_context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<ast>", line 4, in <module>
NameError: name 'chart' is not defined

Served App#

if pn.state.served:
    pmui.Page(
        main=[intro, chart, table],
        sidebar=[x, y, n_clusters],
        title="KMeans Clustering"
    ).servable()