|
4 | 4 | <meta charset="utf-8">
|
5 | 5 | <title>Pyscript/Panel KMeans Demo</title>
|
6 | 6 |
|
7 |
| - <link rel="icon" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/icons/favicon.ico" type=""> |
| 7 | + <link rel="icon" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/icons/favicon.ico" type=""> |
8 | 8 | <meta name="name" content="PyScript/Panel KMeans Demo">
|
9 | 9 |
|
10 | 10 | <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" type="text/css" />
|
11 |
| - <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/css/widgets.css" type="text/css" /> |
12 |
| - <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/css/markdown.css" type="text/css" /> |
13 |
| - <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/css/loading.css" type="text/css" /> |
14 |
| - <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/css/dataframe.css" type="text/css" /> |
| 11 | + <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/widgets.css" type="text/css" /> |
| 12 | + <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/markdown.css" type="text/css" /> |
| 13 | + <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/loading.css" type="text/css" /> |
| 14 | + <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/css/dataframe.css" type="text/css" /> |
15 | 15 |
|
16 | 16 | <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega@5"></script>
|
17 | 17 | <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vega-lite@5"></script>
|
|
20 | 20 | <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.js"></script>
|
21 | 21 | <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"></script>
|
22 | 22 | <script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"></script>
|
23 |
| - <script type="text/javascript" src="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/panel.min.js"></script> |
| 23 | + <script type="text/javascript" src="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/panel.min.js"></script> |
24 | 24 | <script type="text/javascript">
|
25 | 25 | Bokeh.set_log_level("info");
|
26 | 26 | </script>
|
27 | 27 |
|
28 | 28 | <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
|
29 |
| - <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/bundled/bootstraptemplate/bootstrap.css"> |
30 |
| - <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.8/dist/bundled/defaulttheme/default.css"> |
| 29 | + <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/bundled/bootstraptemplate/bootstrap.css"> |
| 30 | + <link rel="stylesheet" href="https://unpkg.com/@holoviz/panel@0.13.0-rc.10/dist/bundled/defaulttheme/default.css"> |
31 | 31 |
|
32 | 32 | <style>
|
33 | 33 | #sidebar {
|
|
83 | 83 | from io import StringIO
|
84 | 84 | from js import fetch
|
85 | 85 |
|
86 |
| -await micropip.install(['panel==0.13.0rc9', 'altair']) |
| 86 | +await micropip.install(['panel==0.13.0rc10', 'altair']) |
87 | 87 |
|
88 | 88 | import altair as alt
|
89 | 89 | import panel as pn
|
|
102 | 102 | y = pn.widgets.Select(name='y', options=cols, value='bill_length_mm')
|
103 | 103 | n_clusters = pn.widgets.IntSlider(name='n_clusters', start=1, end=5, value=3)
|
104 | 104 |
|
105 |
| -@pn.depends(x.param.value, y.param.value, n_clusters.param.value) |
106 |
| -def get_clusters(x, y, n_clusters): |
| 105 | +brush = alt.selection_interval(name='brush') # selection of type "interval" |
| 106 | + |
| 107 | +def get_clusters(n_clusters): |
107 | 108 | kmeans = KMeans(n_clusters=n_clusters)
|
108 | 109 | est = kmeans.fit(penguins[cols].values)
|
109 | 110 | df = penguins.copy()
|
110 | 111 | df['labels'] = est.labels_.astype('str')
|
| 112 | + return df |
| 113 | + |
| 114 | +def get_chart(x, y, df): |
111 | 115 | centers = df.groupby('labels').mean()
|
112 |
| - table.value = df |
113 |
| - return ( |
114 |
| - alt.Chart(df) |
115 |
| - .mark_point(size=100) |
116 |
| - .encode( |
117 |
| - x=alt.X(x, scale=alt.Scale(zero=False)), |
118 |
| - y=alt.Y(y, scale=alt.Scale(zero=False)), |
119 |
| - shape='labels', |
120 |
| - color='species' |
121 |
| - ).properties(width=800) + |
| 116 | + return (alt.Chart(df) |
| 117 | + .mark_point(size=100) |
| 118 | + .encode( |
| 119 | + x=alt.X(x, scale=alt.Scale(zero=False)), |
| 120 | + y=alt.Y(y, scale=alt.Scale(zero=False)), |
| 121 | + shape='labels', |
| 122 | + color='species' |
| 123 | + ).add_selection(brush).properties(width=800) + |
122 | 124 | alt.Chart(centers)
|
123 |
| - .mark_point(size=200, shape='cross', color='black') |
| 125 | + .mark_point(size=250, shape='cross', color='black') |
124 | 126 | .encode(x=x+':Q', y=y+':Q')
|
125 |
| -) |
| 127 | + ) |
| 128 | + |
| 129 | +chart = pn.pane.Vega() |
| 130 | +table = pn.widgets.Tabulator(pagination='remote', page_size=10) |
| 131 | + |
| 132 | +def update_table(event=None): |
| 133 | + table.value = get_clusters(n_clusters.value) |
| 134 | + |
| 135 | +n_clusters.param.watch(update_table, 'value') |
| 136 | + |
| 137 | +@pn.depends(x, y, n_clusters, watch=True) |
| 138 | +def update_chart(*events): |
| 139 | + chart.object = get_chart(x.value, y.value, table.value) |
| 140 | + chart.selection.param.watch(update_filters, 'brush') |
| 141 | + |
| 142 | +def update_filters(event=None): |
| 143 | + filters = [] |
| 144 | + for k, v in (getattr(event, 'new') or {}).items(): |
| 145 | + filters.append(dict(field=k, type='>=', value=v[0])) |
| 146 | + filters.append(dict(field=k, type='<=', value=v[1])) |
| 147 | + table.filters = filters |
126 | 148 |
|
127 |
| -table = pn.widgets.Tabulator(penguins, pagination='remote', page_size=10) |
| 149 | +update_table() |
| 150 | +update_chart() |
128 | 151 |
|
129 | 152 | intro = """
|
130 | 153 | This app provides an example of **building a simple dashboard using
|
131 | 154 | Panel**.\n\nIt demonstrates how to take the output of **k-means
|
132 |
| -clustering on the Penguins dataset** using scikit-learn, parameterizing |
133 |
| -the number of clusters and the variables to plot.\n\nThe entire |
134 |
| -clustering and plotting pipeline is expressed as a **single reactive |
135 |
| -function** that responsively returns an updated plot when one of the |
136 |
| -widgets changes.\n\n The **`x` marks the center** of the cluster. |
| 155 | +clustering on the Penguins dataset** using scikit-learn, |
| 156 | +parameterizing the number of clusters and the variables to |
| 157 | +plot.\n\nThe plot and the table are linked, i.e. selecting on the plot |
| 158 | +will filter the data in the table.\n\n The **`x` marks the center** of |
| 159 | +the cluster. |
137 | 160 | """
|
138 | 161 |
|
139 | 162 | await show(x, 'x-widget')
|
140 | 163 | await show(y, 'y-widget')
|
141 | 164 | await show(n_clusters, 'n-widget')
|
142 | 165 | await show(intro, 'intro')
|
143 |
| -await show(get_clusters, 'cluster-plot') |
| 166 | +await show(chart, 'cluster-plot') |
144 | 167 | await show(table, 'table')
|
145 | 168 | </py-script>
|
146 | 169 | <script>
|
|
0 commit comments