Nauč se Python > Kurzy > Datový kurz PyLadies > Interaktivní vizualizace a aplikace > Interaktivní vizualizace a aplikace I.

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.

Interaktivní vizualizace a aplikace

Při práci s daty je mnoho příležitostí, kdy se hodí interaktivita. Při vizualici se hodí zvětšování / změnšování měřítka, výběr podoblasti, ukázání vykreslených hodnot apod. Nebo při datové anlý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.

Tady si ukážeme, jak si s takovými úkoly poradit pomocí dvou nástrojů: plotly, resp. především plotly express, a streamlit.

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 nebo altair. Na "dashboarding" pak dash, panel, voila nebo justpy.

Každý z těchto nástrojů má, jako obvykle, své výhody a nevýhody. Nejrozšířenějším nástrojem je 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. 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.

Dvě největší výhody Streamlitu jsou rychlost (jednoduchost) vývoje aplikace a atraktivní výchozí vzhled.

Pár článků či přednášek, které se tématu týkají:

Pro a proti

Je potřeba ale říct, že všechny tyto 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?

  • Na malou aplikaci pro omezený počet uživatelů (dashboard pro kolegy).
  • Na rychlý vývoj prototypu.

A co když chceme budovat velkou (webovou) aplikaci?

  • 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.
  • Když takový tým nemáme, naučíme se programovat v JavaScriptu ... Ne, raději v TypeScriptu ...
  • ... raději najmeme ten vývojářský tým :-)

Instalace a import grafických knihoven

Pokud nemáte nainstalovanou knihovnu plotly, odkomentujte a spusťte příslušné řádky.

In [ ]:
# instalace plotly
# %pip install plotly

Pro plotly express se vžila zkratka px, kterou použijeme i my.

In [ ]:
import plotly.express as px

Interaktivní vizualizace dat

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í.

Rybí míry

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čí :)

In [ ]:
import pandas as pd
In [ ]:
fish_data = pd.read_csv("fish_data.csv", index_col=0)

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.

In [ ]:
px.scatter_matrix(fish_data)

To vůbec není špatné na prvotní orientaci. Už teď je vidět, že tam máme katogorickou proměnnou Species, nějaké spojité proměnné s rozměry a nepodstatné ID. Vidíme také, že některé proměnné spolu hodně korelují.

Už teď bychom mohli využít interaktivních prvků: zkuste si najet kurzorem na body v grafu nebo použít nástroje na změnu měřítka nebo výběr dat, které se zobrazí v pravém horním rohu. Ještě lepší bude ale trochu graf zlepšit: Zahodit ID a druhy si označit barvou.

In [ ]:
px.scatter_matrix(
    fish_data,
    dimensions=["Weight", "Length1", "Length2", "Length3", "Height", "Width"],
    color="Species",
)

Tady nám kromě barevnosti přibyla vpravo legenda. A dokonce legenda interaktivní! Jednoduchým kliknutím můžeme schovat / zobrazit jednotlivé kategorie, dvouklikem můžeme zobrazit jen jednu kategorii. Zkuste si to! Užitečné může být i vybírání dat - Box Select nebo Lasso Select.

Úkol: Použijte pro barvu sloupec Weight, symboly udělejte částečně průhledné pomocí argumentu opacity (rozsah 0 - 1) a v legendě, zobrazované, když se najede kurzorem na určitý bod, nechť se zobrzují všechny sloupce (pomůže argument hover_data).

Když se pak chceme třeba podívat na statistické vlastnosti jedné konkrétní proměnné (sloupce), můžeme použít některou z funkcí na zobrazení rozdělovací funkce, resp. některých jejích vlastností (momentů).

Začít můžeme poměrně častým box plotem. Bonus plotly je hlavně v interaktivním zobrazení číselných hodnot: mediánu a kvantilů, a také identifikaci (pravděpodobně) odlehlých hodnot.

In [ ]:
px.box(fish_data, x="Species", color="Species", y="Height", points="all")
In [ ]:
px.histogram(fish_data, color="Species", x="Height", opacity=0.5)

Pro zobrazení vztahu dvou proměnných může být ještě užitečné podívat se na hustotu bodů v ploše pomocí kontur. U tohoto grafu můžeme po stranách zobrazit i tzv. marginální rozdělení: nejpravděpodobnější rozdělení jedné proměnné v závislosti na druhé.

In [ ]:
px.density_contour(
    fish_data,
    color="Species",
    x="Height",
    y="Width",
    marginal_x="rug",
    marginal_y="box",
)

Úkol: Zkuste si zobrazit v grafech jiné veličiny (jiné sloupce) než jen Height a Width. Zkuste změnit typ marginálních grafů.

Co s tím dál?

Report pro šéfa a šéfovou

Máme i kolegy, kteří (ještě) nepoužívají Python a přesto by ocenili, kdyby mohli místo statického reportu dostat report s takto krásnými a interaktivními vizualizacemi. Pro tento účel se hodí export notebooku do html pomocí nbconvert.

V příkazovém řádku spustíme nbconvert pomocí příkazu jupyter nbconvert. Pro export do html pak přidáme --to html, nesmíme zapomenout zadat který notebook (tj. soubor) chceme vlastně konvertovat.

In [ ]:
# Odkomentováním se spustí příkaz v příkazové řádce (díky vykřičníku)
# Možná se seoubor u tebe jmenuje jinak než dashboardy-1, v takovém použij aktuální jméno souboru
# !jupyter nbconvert dashboardy-1.ipynb --to html

Můžeme také exportovat jen výstupy a "utajit" zdrojový kód pomocí --no-input:

In [ ]:
# !jupyter nbconvert dashboardy-1.ipynb --to html --no-input

Analýza nových dat

Všem se naše vizualizace líbily, a jelikož je potřeba analyzovat další data, dostali jsme to za úkol my. Tentokrát se nejedná o ryby, ale o tučňáky.

Úkol: Vyber si z grafů ten, který se ti nejvíc líbí, a místo rybích dat použij tučňáky.

In [ ]:
penguins = pd.read_csv("penguins_size_nona.csv")

Vytváříme aplikaci

Z naší práce v notebooku vykrystalizoval velice častý vzor: Podobné vizualizace a analýzy, v nichž se mění data a několik klíčových parametrů. Příležitost vytvořit aplikaci, která toto umožní nám a okruhu poučených uživatelů.

Pojďme si nadefinovat naší jednoduchou aplikaci:

  1. Načíst data z csv souboru.
  2. Vykreslit scatter matrix, kde budu moct zvolit dimenze, sloupec pro barvu a průhlednost.
  3. Pro vybraný sloupec zobrazit distribuci vybraného sloupce pomocí histogramu, box plotu nebo violin plotu.

Příprava v notebooku

Pojďme to nejprve načrtnout tady v notebooku. Jako první si připravíme uživatelské vstupy.

In [ ]:
# vstup 1: výběr datové sady
data_file_path = "penguins_size_nona.csv"
# vstup 2: výběr parametrů scatter matrix
dimensions = ['culmen_length_mm', 'culmen_depth_mm', 'flipper_length_mm', 'body_mass_g']
color = "sex"
opacity = 0.5
# výběr sloupce pro zobrazení rozdělení dat
interesting_column = "body_mass_g"
# výběr funkce pro zobrazení rozdělovací funkce
dist_plot = px.violin

A tohle už je pak naše aplikace: Použili jsme stejné funkce a parametry jako na začátku práce s plotly, jen jsme je parametrizovali pomocí vstupů z předchozího bloku.

In [ ]:
# načtení dat
data = pd.read_csv(data_file_path)
In [ ]:
# scatter matric plat
px.scatter_matrix(data, dimensions=dimensions, color=color, opacity=opacity)
In [ ]:
# zobrazení rozdělovací funkce
dist_plot(data, x=interesting_column, color=color)

A teď z toho pojďme udělat interaktivní webovou aplikaci! To nebudeme dělat přímo tady v notebooku, ale v "obyčejném" .py souboru s Python kódem.

Aplikaci máme připravenou v souboru app.py, tady v notebooku si soubor můžeme prohlédnout:

In [4]:
%cat ../app.py
import pandas as pd
import plotly.express as px
import streamlit as st


def app():
    # vstup 1: výběr datové sady
    data_file_path = st.file_uploader("Data file")
    data = None
    if data_file_path is not None:
        # read data if user uploads a file
        data = pd.read_csv(data_file_path)
        # seek back to position 0 after reading
        data_file_path.seek(0)
    if data is None:
        st.warning("No data loaded")
        return
    # vstup 2: výběr parametrů scatter matrix
    dimensions = st.multiselect("Scatter matrix dimensions", list(data.columns), default=list(data.columns))
    color = st.selectbox("Color", data.columns)
    opacity = st.slider("Opacity", 0.0, 1.0, 0.5)

    # scatter matrix plat
    st.write(px.scatter_matrix(data, dimensions=dimensions, color=color, opacity=opacity))

    # výběr sloupce pro zobrazení rozdělení dat
    interesting_column = st.selectbox("Interesting column", data.columns)
    # výběr funkce pro zobrazení rozdělovací funkce
    dist_plot = st.selectbox("Plot type", [px.box, px.histogram, px.violin])

    st.write(dist_plot(data, x=interesting_column, color=color))


if __name__ == "__main__":
    app()

Základem je, že uživatelské vstupy jsme předělali z podoby proměnná = hodnota do podoby proměnná = st.vhodný_widget(...). Toto je způsob vytváření streamlit aplikace:

  • Aplikaci píšeme v podstatě jako lineární skript (zdrojový kód samozřejmě můžeme strukturovat do funkcí / modulů / tříd dle vlastního uvážení, streamlit ale bude aplikaci vždy spouštět krok po kroku jako ten skript).
  • Uživatelské vstupy načítáme z návratové hodnoty funkcí st.nějaký_widget, Streamlit se postará o to, aby widget správně fungoval a návratová hodnota byla vždy ta aktuální.
  • Prvky aplikace (výstupy) zobrazíme uživateli pomocí st.write.

Widgets - pomocné "věcičky": V uživatelských grafických rozhraních (GUI) se používají widgety: nástroje na vybrání možností, hodnoty proměnné, zadání textu nebo datumu apod.

Spuštění

Streamlit ještě nejspíš nemáš nainstalovaný. Instaluje se běžným způsobem přes pip:

pip install streamlit

případně pokud používáš conda

conda install -c conda-forge streamlit

Na svém počítači si pak apikaci spustíš příkazem streamlit run s názvem souboru s aplikací. V našem případě tedy

streamlit run app.py

Pokud je vše v pořádku, zobrazí se něco takovéhoto:

  You can now view your Streamlit app in your browser.

  Network URL: http://192.168.2.103:8800
  External URL: http://85.207.123.46:8800

Dle instrukcí otevři odkaz (ten první) v prohlížeči. Je velká pravděpodobnout, že se objeví naše právě vytvořená aplikace na vizualizaci dat.

Publikujeme na internet

V principu bychom mohli spustit aplikaci u sebe na počítači tak, aby ji mohli používat i další uživatelé. Na vnitřní síti (domácí, pracovní) by to bylo snadné (i když na pracovní síti a pracovním počítači by tomu mohla bránit bezpečnostní nastavení), přístup z vnějšího internetu by už byl komplikovanější.

Naštěstí nejsme v podobné situaci sami :) Takže existují více či méně složité a sofistikované způsoby, jak aplikaci spustit na nějakém serveru (v cloudu) a zpřístupnit z internetu. My si ukážeme, jak to funguje na Heroku. Podobné služby nabízí třeba AWS (Elastic Beanstalk), Google App Engine nebo Dokku on Digital Ocean. Výhodou Heroku je jednoduchost a možnost bezplatných služeb, nevýhodou pak rychle rostoucí cena a omezené možnosti.

Budeme postupovat v podstatě podle průvodce Getting started with Python.

Registrace a klient Heroku

Než začneme, je potřeba:

  1. Založit si bezplatný účet na https://signup.heroku.com/signup/dc.
  2. Nainstalovat Git:
  3. Alespoň základní konfigurace Gitu. V příkazovém řádku (vyplňte své jméno a email):
    git config --global user.name "Moje Jméno"
    git config --global user.email "muj@email.com"
  4. Nainstalovat si Heroku klienta:

Instalaci ověříme v příkazové řádce pomocí:

  1. Git:

    git config --list

    Mělo by se objevit něco na způsob

    user.name=Moje Jméno
    user.email=muj@email.com
  2. Heroku:

    heroku --version

    Tady by mělo být výstupem zhruba

    heroku/7.39.2 darwin-x64 node-v12.13.0

Go-live

Teď budeme chvilku pracovat v příkazové řádce. Nejprve je potřeba vytvořit novou složku pro publikovanou aplikaci a nakopírovat tam soubory app.py, requirements.txt, runtime.txt a Procfile.

A teď už v příkazové řádce, kde aktuální složka musí být ta nově vytvořená, kam jsi zkopírovala soubory:

  1. Vytvoř Git repozitář:

    git init
  2. Heroku potřebuje soubor s názvem Procfile. Ten velice jednoduše říká, co že to vlastně chceme spustit. Bude tam příkaz streamlit run (tak jako jsme spouštěli aplikaci lokálně) s pár přepínači navíc. Pro Procfile musí obsahovat tento řádek:

    web: streamlit run --server.headless 1 --server.port $PORT app.py
  3. Je určitě dobré specifikovat verzi Pythonu, jinak se použije výchozí (v době psaní je to 3.6). V souboru runtime.txt na to stačí řádka python-3.8.6.

  4. Dalším souborem, který potřebujeme, je requirements.txt. O tom jste už možná slyšeli(y) - obsahuje seznam Python balíčků, které potřebuje daný Python projekt. Heroku tento soubor použije, aby před spuštěním aplikace nainstaloval vše potřebné. Pro náš dashboard musí být v requirements.txt toto:

    pandas==1.1.4
    streamlit==0.71.0
    plotly==4.13.0
  5. Soubory už máme připravené, potřebujeme je teď přidat do Git repozitáře. Na to použij tyto dva příkazy:

    git add app.py Procfile runtime.txt requirements.txt
    git commit -v -m "první verze dashboardu"
  6. A teď už můžeme publikovat. Tedy nejdříve se přihlásit pomocí

    heroku login

    (Postupujte dle pokynů - otevře se přihlašovací stránka ve webovém prohlížeči, která vás vyzve k zadání přihlašovacích údajů.)

  7. Pak ještě vytvořit Heroku aplikaci pomocí

    heroku create
  8. A nakonec už opravdu vypustit aplikaci na internet pomocí

    git push heroku master

Pokud vše půjde dobře, tento krok bude chvíli trvat. Postupně se bude vypisovat, co pro nás Heroku dělá:

Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 940.72 KiB | 2.40 MiB/s, done.
Total 4 (delta 2), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: -----> Installing python-3.8.6
remote: -----> Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2
remote: -----> Installing SQLite3
remote: -----> Installing requirements with pip
remote:        Collecting pandas==1.1.4
remote:          Downloading pandas-1.1.4-cp36-cp36m-manylinux1_x86_64.whl (9.5 MB)
remote:        Collecting streamlit==0.71.0
remote:          Downloading streamlit-0.71.0-py2.py3-none-any.whl (7.4 MB)
remote:        Collecting plotly==4.13.0
remote:          Downloading plotly-4.13.0-py2.py3-none-any.whl (13.1 MB)
...

Nakonec by se mělo objevit něco jako

remote: -----> Launching...
remote:        Released v6
remote:        https://warm-sierra-79844.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/warm-sierra-79844.git

Důležitý je odkaz pod řádkem "Released v6". To je totiž adresa, na které je náš dashboard dostupný "odkudkoli z internetu". Buď si ji zkopírujte do prohlížeče nebo použijte příkaz heroku open.

Úkol 1: Pošli odkaz na tvou běžící aplikaci :)

Úkol 2: Pomocí st.title přidej titulek (název) aplikace. Vyzkoušej u sebe lokálně, pak změnu commituj do gitu a aktualizuj aplikaci na Heroku (pomocí git push heroku master).


Toto je stránka lekce z kurzu, který probíhá nebo proběhl naživo s instruktorem.