{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"*Na naucse.python.cz bohužel nefunguje zobrazování interaktivních plotly grafů. Pro zobrazení včetně grafů můžeš použít nbviewer: https://nbviewer.jupyter.org/github/coobas/pydataladies-dashboard/blob/main/notebooks/dashboardy-1.ipynb nebo si notebook pustit lokálně. Všechny soubory pohromadě najdeš v repozitáři https://github.com/coobas/pydataladies-dashboard.*\n",
"\n",
"\n",
"# Interaktivní vizualizace a aplikace\n",
"\n",
"Při práci s daty je mnoho příležitostí, kdy se hodí interaktivita. Při vizualici se hodí zvětšování / zmenšování měřítka, výběr podoblasti, ukázání vykreslených hodnot apod. Nebo při datové analýze obecně se může hodit interaktivně v notebooku měnit nějaký parametr (třeba hyperparametr pro strojové učení). Anebo chceme dát výsledky naší skvělé analýzy k dispozici \"netechnickým\" kolegům nebo kamarádům, kteří (zatím) nedokáží Jupyter notebook spustit.\n",
"\n",
"Tady si ukážeme, jak si s takovými úkoly poradit pomocí dvou nástrojů: [plotly](https://plotly.com/python/), resp. především [plotly express](https://plotly.com/python/plotly-express/), a [streamlit](https://www.streamlit.io/).\n",
"\n",
"Existují i další nástroje, které poskytují podobné možnosti. Podrobný přehled najdete na https://pyviz.org/tools.html. Na interaktivní vizualizace jsou to především [holoviews](http://github.com/pyviz/holoviews) nebo [altair](http://github.com/altair-viz/altair). Na \"dashboarding\" pak [dash](http://github.com/plotly/dash), [panel](https://panel.holoviz.org/), [voila](http://github.com/QuantStack/voila), [vizro](https://github.com/mckinsey/vizro) nebo [justpy](https://justpy.io).\n",
"\n",
"Velmi atraktivní novinkami jsou [JupyterLite](https://github.com/jupyterlite/jupyterlite) a [PyScript](https://pyscript.net). Oba projekty těží z možnosti spustit Python kód přímo ve webovém prohlížeči, konkrétně v jeho virtuálním stroji, díky technologie Web Assembly. \n",
"* JupyteLite umožňuje spustit Jupyter notebooky v prohlížeči, bez nutnosti instalace Pythonu, a to včetně knihoven na zpracováni dat jako jsou NumPy, Pandas apod. Více se můžeš dozvědět také z přednášky na pražském PyData meetup: [Jeremy Tuloup - JupyterLite: Jupyter ❤️ WebAssembly ❤️ Python (slides, video)](https://pydata.cz/#jeremy-tuloup---jupyterlite-jupyter-%EF%B8%8F-webassembly-%EF%B8%8F-python-slides-video).\n",
"* PyScript umožňuje vložit Python kód přímo do HTML kódu a tímto způsobem vytvářet interaktivní webové aplikace přímo V Pythonu. PyScript tak vlastně \"nahrazuje\" JavaScript.\n",
"\n",
"Každý z těchto nástrojů má, jako obvykle, své výhody a nevýhody. Nejrozšířenějším nástrojem je [Dash](http://github.com/plotly/dash) ze stejné dílny jako plotly, který poskytuje i enterprise řešení pro provoz aplikací. Dash je určitě dobrou volbou, jak se můžete dozvědět i na [přednášce z pražského PyData Meetupu](https://www.youtube.com/watch?v=dewrzMPPLDU). Panel (a také Voila) se od Dash liší tím, že je lze použít i v Jupyter notebooku a pak notebook použít přímo jako aplikaci. Největší přednost `voila` je jednoduchý způsob, jak udělat dashboard přímo z notebooku: viz [dokumentace](https://voila.readthedocs.io/en/stable/using.html).\n",
"\n",
"Dvě největší výhody Streamlitu jsou rychlost (jednoduchost) vývoje aplikace a atraktivní výchozí vzhled. Streamlit také (nedávno) přidal možnost vzájemné interakce mezi widgety pomocí callback funkcí. Pro Streamlit lze také poměrně snadno vytvářet nové komponenty, důkazem toho budiž [galerie komponent](https://streamlit.io/components).\n",
"\n",
"Pár článků či přednášek, které se tématu týkají:\n",
"* [Dash, Voila, Panel, & Streamlit—Our Thoughts on the Big Four Dashboarding Tools](https://quansight.com/post/dash-voila-panel-streamlit-our-thoughts-on-the-big-four-dashboarding-tools)\n",
"* [Pure Python Web Development](https://metaperl.github.io/pure-python-web-development/intro.html) - velký přehled knihoven pro webový vývoj v Pythonu.\n",
"* [Streamlit - The fastest way to build and share data science apps](https://www.youtube.com/watch?v=sdgTYy3BJiM)\n",
"* [How to Build a Reporting Dashboard using Dash and Plotly](https://towardsdatascience.com/how-to-build-a-complex-reporting-dashboard-using-dash-and-plotl-4f4257c18a7f)\n",
"* [Turn any Notebook into a Deployable Dashboard | SciPy 2019 | James Bednar](https://www.youtube.com/watch?v=L91rd1D6XTA)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Pro a proti\n",
"\n",
"Je potřeba ale říct, že všechny zmíněné přístupy mají své výrazné nevýhody a limity a nehodí se pro velké a složité aplikace. Možnosti interakcí v aplikaci jsou omezené a také mohou být pomalé. Robustní škálování pro mnoho uživatelů (velký provoz) je obecně složitější. Kdy tedy především použít, co si tady ukážeme?\n",
"* Na malou aplikaci pro omezený počet uživatelů (dashboard pro kolegy).\n",
"* Na rychlý vývoj prototypu.\n",
"\n",
"A co když chceme budovat velkou (webovou) aplikaci?\n",
"* Zadáme vývojářskému týmu, aby v moderních JavaScript nástrojích typu React nebo Vue.js pro nás vytvořil krásný a rychlý \"front-end\", zatímco my vytvoříme v Pythonu \"back-end\", který s front-endem bude komunikovat např. pomocí JSON API. To uvidíme i v naší lekci o API.\n",
"* Když takový tým nemáme, naučíme se programovat v JavaScriptu ... Ne, raději v TypeScriptu ... Zkusíme PyScript ...\n",
"* ... nakonec najmeme front-end vývojářský tým pokud je to možné :-)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Instalace a import grafických knihoven\n",
"\n",
"Pokud nemáte nainstalovanou knihovnu plotly, odkomentujte a spusťte příslušné řádky."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# instalace plotly\n",
"# %pip install plotly\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pro plotly express se vžila zkratka `px`, kterou použijeme i my. "
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import plotly.express as px\n"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import plotly.io as pio\n",
"\n",
"# from https://github.com/microsoft/vscode-jupyter/issues/6999\n",
"# This ensures Plotly output works in multiple places:\n",
"# plotly_mimetype: VS Code notebook UI\n",
"# notebook: \"Jupyter: Export to HTML\" command in VS Code\n",
"# See https://plotly.com/python/renderers/#multiple-renderers\n",
"pio.renderers.default = \"plotly_mimetype+notebook\"\n",
"# notebook-only does not work in Jupyter Lab\n",
"# pio.renderers.default = \"notebook\"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Interaktivní vizualizace dat\n",
"\n",
"Pojďme si zkusit trochu více prohlédnout data, se kterými jsme pracovali v předchozích lekcích na strojové učení."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Rybí míry\n",
"\n",
"Začněme rozměry ryb, na kterých jsme ukazovali regresi a klasifikaci. Určitě stojí za to si data nejprve trochu prohlédnout. (Jen si asi nenakreslíme přímo vzhled ryb, na to nám data nestačí :)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"fish_data = pd.read_csv(\"fish_data.csv\", index_col=0)\n"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"species_cs = {\n",
" \"Bream\": \"Cejn\",\n",
" \"Roach\": \"Plotice\",\n",
" \"Whitefish\": \"Bílá ryba\",\n",
" \"Parkki\": \"Karas\",\n",
" \"Perch\": \"Okoun\",\n",
" \"Pike\": \"Štika\",\n",
" \"Smelt\": \"Koruška\",\n",
"}\n",
"fish_data = fish_data.assign(Species=fish_data[\"Species\"].map(species_cs))\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A místo klasického zobrazování čísel si zkusíme rovnou data vykreslit do grafu. Víme (tušíme), že v datech je spousta sloupců. Můžeme si je nechat vykreslit všechny pomocí `scatter_matrix`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
" \n",
" "
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.plotly.v1+json": {
"config": {
"plotlyServerURL": "https://plot.ly"
},
"data": [
{
"dimensions": [
{
"axis": {
"matches": true
},
"label": "Species",
"values": [
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Cejn",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Plotice",
"Bílá ryba",
"Bílá ryba",
"Bílá ryba",
"Bílá ryba",
"Karas",
"Karas",
"Karas",
"Karas",
"Karas",
"Karas",
"Karas",
"Karas",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Okoun",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Štika",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška",
"Koruška"
]
},
{
"axis": {
"matches": true
},
"label": "Weight",
"values": [
242,
290,
340,
363,
430,
450,
390,
450,
500,
475,
500,
500,
340,
600,
600,
700,
700,
610,
575,
685,
725,
720,
714,
850,
1000,
955,
925,
975,
950,
40,
78,
87,
110,
120,
145,
160,
140,
200,
180,
290,
390,
270,
270,
540,
800,
55,
60,
120,
170,
145,
200,
273,
300,
40,
51.5,
70,
78,
80,
85,
85,
110,
125,
130,
120,
120,
130,
135,
130,
150,
145,
150,
225,
145,
197,
218,
300,
260,
265,
250,
514,
556,
840,
685,
700,
690,
900,
650,
820,
850,
900,
1015,
820,
1100,
1000,
1100,
200,
300,
300,
300,
430,
345,
456,
510,
540,
500,
567,
770,
1250,
1600,
1550,
6.7,
7.5,
7,
9.7,
9.8,
8.7,
10,
9.9,
9.8,
12.2,
13.4,
12.2,
19.9
]
},
{
"axis": {
"matches": true
},
"label": "Length1",
"values": [
23.2,
24,
23.9,
26.3,
26.5,
26.8,
27.6,
27.6,
28.5,
28.4,
28.7,
29.1,
29.5,
29.4,
29.4,
30.4,
30.4,
30.9,
31.3,
31.4,
31.8,
32,
32.7,
32.8,
33.5,
35,
36.2,
37.4,
38,
12.9,
17.5,
18.2,
19.1,
19.4,
20.5,
20.5,
21,
22.1,
23.6,
24,
29.5,
23.6,
24.1,
28.5,
33.7,
13.5,
14.3,
17.5,
19,
19.8,
21.2,
23,
24,
13.8,
15,
15.7,
16.8,
17.2,
17.8,
18.2,
19,
19,
19.3,
20,
20,
20,
20,
20.5,
20.5,
20.7,
21,
22,
22,
23.5,
25,
25.2,
25.4,
25.4,
25.9,
30.5,
32,
32.5,
34,
34,
34.6,
36.5,
36.5,
36.6,
36.9,
37,
37,
37.1,
39,
39.8,
40.1,
30,
31.7,
32.7,
34.8,
35.5,
36,
40,
40,
40.1,
42,
43.2,
44.8,
52,
56,
56,
9.3,
10,
10.1,
10.4,
10.7,
10.8,
11.3,
11.3,
11.4,
11.5,
11.7,
12.1,
13.8
]
},
{
"axis": {
"matches": true
},
"label": "Length2",
"values": [
25.4,
26.3,
26.5,
29,
29,
29.7,
30,
30,
30.7,
31,
31,
31.5,
32,
32,
32,
33,
33,
33.5,
34,
34,
35,
35,
36,
36,
37,
38.5,
39.5,
41,
41,
14.1,
18.8,
19.8,
20.8,
21,
22,
22.5,
22.5,
23.5,
25.2,
26,
31.7,
26,
26.5,
31,
36.4,
14.7,
15.5,
19,
20.7,
21.5,
23,
25,
26,
15,
16.2,
17.4,
18.7,
19,
19.6,
20,
21,
21,
21.3,
22,
22,
22,
22,
22.5,
22.5,
22.7,
23,
24,
24,
25.6,
26.5,
27.3,
27.5,
27.5,
28,
32.8,
34.5,
35,
36.5,
36,
37,
39,
39,
39,
40,
40,
40,
40,
42,
43,
43,
32.3,
34,
35,
37.3,
38,
38.5,
42.5,
42.5,
43,
45,
46,
48,
56,
60,
60,
9.8,
10.5,
10.6,
11,
11.2,
11.3,
11.8,
11.8,
12,
12.2,
12.4,
13,
15
]
},
{
"axis": {
"matches": true
},
"label": "Length3",
"values": [
30,
31.2,
31.1,
33.5,
34,
34.7,
35,
35.1,
36.2,
36.2,
36.2,
36.4,
37.3,
37.2,
37.2,
38.3,
38.5,
38.6,
39.5,
39.2,
40.9,
40.6,
41.5,
41.6,
42.6,
44,
45.3,
45.9,
46.5,
16.2,
21.2,
22.2,
23.1,
23.7,
24.3,
25.3,
25,
26.8,
27.9,
29.2,
35,
28.7,
29.3,
34,
39.6,
16.5,
17.4,
21.3,
23.2,
24.1,
25.8,
28,
29,
16,
17.2,
18.5,
19.4,
20.2,
20.8,
21,
22.5,
22.5,
22.8,
23.5,
23.5,
23.5,
23.5,
24,
24,
24.2,
24.5,
25.5,
25.5,
27,
28,
28.7,
28.9,
28.9,
29.4,
34,
36.5,
37.3,
39,
38.3,
39.3,
41.4,
41.4,
41.3,
42.3,
42.5,
42.4,
42.5,
44.6,
45.2,
45.5,
34.8,
37.8,
38.8,
39.8,
40.5,
41,
45.5,
45.5,
45.8,
48,
48.7,
51.2,
59.7,
64,
64,
10.8,
11.6,
11.6,
12,
12.4,
12.6,
13.1,
13.1,
13.2,
13.4,
13.5,
13.8,
16.2
]
},
{
"axis": {
"matches": true
},
"label": "Height",
"values": [
11.52,
12.48,
12.3778,
12.73,
12.444,
13.6024,
12.67,
14.0049,
14.2266,
14.2628,
14.3714,
13.7592,
13.9129,
14.9544,
15.438,
14.8604,
14.938,
15.633,
15.1285,
15.9936,
16.36,
16.3618,
16.517,
16.8896,
18.957,
18.084,
18.7542,
18.6354,
17.6235,
4.1472,
5.5756,
5.6166,
6.1677,
6.1146,
6.6339,
7.0334,
6.55,
7.3968,
7.0866,
8.8768,
9.485,
8.3804,
8.1454,
10.744000000000002,
11.7612,
6.8475,
6.5772,
8.3922,
9.396,
9.7364,
10.3458,
11.088,
11.368,
3.824,
4.5924,
4.588,
5.1992,
5.6358,
5.1376,
5.082,
5.6925,
5.6925,
6.384,
6.11,
5.64,
6.11,
5.875,
5.856,
6.792000000000001,
5.9532,
5.2185,
7.292999999999999,
6.375,
6.561,
7.167999999999999,
8.323,
7.1672,
7.0516,
7.8204,
10.03,
10.2565,
11.4884,
10.880999999999998,
10.6091,
10.5717,
11.1366,
11.1366,
12.4313,
11.9286,
11.73,
12.3808,
11.135,
12.8002,
11.9328,
12.5125,
5.568,
5.7078,
5.9364,
6.2884,
7.29,
6.396,
7.28,
6.825,
7.786,
6.96,
7.792000000000001,
7.68,
10.6863,
9.6,
9.6,
1.7388,
1.972,
1.7284,
2.196,
2.0832,
1.9782,
2.2139,
2.2139,
2.2044,
2.0904,
2.43,
2.277,
2.9322
]
},
{
"axis": {
"matches": true
},
"label": "Width",
"values": [
4.02,
4.3056,
4.6961,
4.4555,
5.134,
4.9274,
4.69,
4.8438,
4.9594,
5.1042,
4.8146,
4.368,
5.0728,
5.1708,
5.58,
5.2854,
5.1975,
5.1338,
5.5695,
5.3704,
6.0532,
6.09,
5.8515,
6.1984,
6.603,
6.292000000000001,
6.7497,
6.7473,
6.3705,
2.268,
2.9044,
3.1746,
3.3957,
3.2943,
3.5478,
3.8203,
3.325,
4.1272,
3.906,
4.4968,
5.355,
4.2476,
4.2485,
6.562,
6.5736,
2.3265,
2.3142,
2.9181,
3.4104,
3.1571,
3.6636,
4.144,
4.234,
2.432,
2.6316,
2.9415,
3.1234,
3.0502,
3.0368,
2.772,
3.555,
3.6675,
3.534,
3.4075,
3.525,
3.525,
3.525,
3.624,
3.624,
3.63,
3.626,
3.723,
3.825,
4.239,
4.144,
5.1373,
4.335,
4.335,
4.2042,
6.018,
6.3875,
7.7957,
6.864,
6.7408,
6.3666,
7.4934,
6.002999999999999,
7.3514,
7.1064,
7.225,
7.4624,
6.63,
6.8684,
7.2772,
7.4165,
3.3756,
4.158,
4.3844,
4.0198,
4.5765,
3.977,
4.3225,
4.459,
5.1296,
4.896,
4.87,
5.376,
6.9849,
6.144,
6.144,
1.0476,
1.16,
1.1484,
1.38,
1.2772,
1.2852,
1.2838,
1.1659,
1.1484,
1.3936,
1.269,
1.2558,
1.8792
]
},
{
"axis": {
"matches": true
},
"label": "ID",
"values": [
0,
1,
2,
3,
4,
5,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
20,
21,
25,
26,
27,
28,
29,
31,
32,
33,
34,
35,
37,
38,
41,
42,
44,
45,
46,
50,
51,
52,
54,
55,
56,
58,
59,
61,
62,
64,
67,
68,
69,
70,
71,
74,
75,
76,
78,
79,
80,
81,
82,
84,
85,
86,
87,
88,
89,
91,
92,
93,
94,
96,
97,
100,
101,
102,
103,
104,
106,
109,
110,
111,
112,
113,
115,
116,
117,
118,
119,
120,
121,
122,
123,
124,
125,
128,
129,
130,
131,
132,
133,
134,
135,
136,
137,
138,
139,
141,
142,
143,
145,
146,
147,
148,
149,
150,
151,
152,
153,
154,
155,
156,
158
]
}
],
"hovertemplate": "%{xaxis.title.text}=%{x}
%{yaxis.title.text}=%{y}