Obsah
1. Tvorba interaktivních grafů pro webové stránky a aplikace s využitím knihovny Bokeh
2. Technologie, na nichž je knihovna Bokeh postavena
4. Zobrazení jednoduchého grafu s naměřenými hodnotami
5. Zobrazení titulku a legendy v grafu
6. Kooperace s knihovnou NumPy, porovnání s Matplotlibem
7. Vykreslení průběhu dvou funkcí do jednoho grafu
8. Alternativní způsob specifikace barev s využitím trojice hexadecimálních hodnot
9. Další styly vykreslení průběhu funkce/hodnot do grafu
11. Sloupcový graf se specifikací začátků a konce sloupců
12. Vykreslení kružnice a spirály (parametrické křivky)
13. Export grafu do rastrového obrázku ve formáty PNG
14. Nastavení stylu zobrazení legendy
15. Specifikace velikosti grafu
16. Nastavení limitu na souřadných osách
18. Logaritmické měřítko na y-ové ose
19. Repositář s demonstračními příklady
1. Tvorba interaktivních grafů pro webové stránky a aplikace s využitím knihovny Bokeh
Jak již bylo napsáno v perexu, setkali jsme se již na stránkách Roota s několika nástroji, které jsou určené pro tvorbu grafů. Připomeňme si například gnuplot, Matplotlib či grafy dostupné z knihovny SymPy. Dnes se seznámíme s knihovnou Bokeh, která je určena pro tvorbu grafů v Pythonu, přičemž výsledkem je interaktivní webová stránka s „živým“ grafem. Jedná se o knihovnu podporovanou například knihovnou PyWebIO, ovšem Bokeh lze s výhodou použít například i v Jupyter Notebooku.
Obrázek 1: Logo knihovny Bokeh.
2. Technologie, na nichž je knihovna Bokeh postavena
Bokeh je knihovna určená pro generování grafů z dat přístupných přímo v Pythonu. Vstupními daty mohou být běžné n-tice nebo seznamy, ale i vektory a matice z knihovny NumPy či dokonce datové rámce získávané a konstruované knihovnou Pandas. Výsledkem je (typicky) HTML stránka obsahující tři důležité části:
- Odkaz na frontendovou část knihovny Bokeh naprogramovanou v JavaScriptu. Čitelnou podobu frontendu lze najít na adrese https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.js, ve skutečnosti se však používá minifikovaná podoba této knihovny, která je stahována z adresy https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js.
- Vlastní data, která mají být zobrazena v grafu, včetně metadat o grafu.
- JavaScriptová funkce, která zajistí načtení frontendové části knihovny Bokeh.
Z tohoto popisu je zřejmé, že se po technologické stránce knihovna Bokeh dosti podstatným způsobem odlišuje například od Matplotlibu, se všemi přednostmi a zápory tohoto řešení. Předností je fakt, že graf je stále interaktivní a lze ho zařadit do webových aplikací, Jupyter Notebooku atd. Na druhou stranu pro tvorbu grafů určených například do článků je použití Matplotlibu více přímočaré – výsledkem je skutečný obrázek (rastrový či vektorový) a nikoli data+kód pro jeho vykreslení.
3. Instalace knihovny Bokeh
Knihovna Bokeh je nabízena přes Bokeh, takže její instalace by měla být jednoduchá a přímočará. Knihovnu nainstalujeme pro aktuálně přihlášeného uživatele následujícím způsobem:
$ pip3 install --user bokeh
Průběh instalace odhaluje i všechny závislosti této knihovny:
Collecting bokeh Downloading bokeh-2.4.3-py3-none-any.whl (18.5 MB) |████████████████████████████████| 18.5 MB 492 kB/s Requirement already satisfied: packaging>=16.8 in /usr/lib/python3/dist-packages (from bokeh) (20.3) Collecting Jinja2>=2.9 Downloading Jinja2-3.1.2-py3-none-any.whl (133 kB) |████████████████████████████████| 133 kB 755 kB/s Collecting pillow>=7.1.0 Downloading Pillow-9.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.1 MB) |████████████████████████████████| 3.1 MB 570 kB/s Collecting typing-extensions>=3.10.0 Downloading typing_extensions-4.2.0-py3-none-any.whl (24 kB) Requirement already satisfied: PyYAML>=3.10 in /usr/lib/python3/dist-packages (from bokeh) (5.3.1) Collecting tornado>=5.1 Downloading tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl (427 kB) |████████████████████████████████| 427 kB 900 kB/s Collecting numpy>=1.11.3 Downloading numpy-1.22.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (16.8 MB) |████████████████████████████████| 16.8 MB 419 kB/s Collecting MarkupSafe>=2.0 Downloading MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (25 kB) Installing collected packages: MarkupSafe, typing-extensions, tornado, pillow, numpy, Jinja2, bokeh Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.1 bokeh-2.4.3 numpy-1.22.3 pillow-9.1.1 tornado-6.1 typing-extensions-4.2.0
Po instalaci se přesvědčíme, že balíček bokeh je možné naimportovat:
$ python3 Python 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import bokeh
Popř. si můžeme zobrazit nápovědu k právě naimportovanému balíčku:
>>> help(bokeh)
S výsledkem:
Help on package bokeh: NAME bokeh DESCRIPTION Bokeh is a Python library for creating interactive visualizations for modern web browsers. Bokeh helps you build beautiful graphics, ranging from simple plots to complex dashboards with streaming datasets. With Bokeh, you can create JavaScript-powered visualizations without writing any JavaScript yourself. Most of the functionality of Bokeh is accessed through submodules such as |bokeh.plotting| and |bokeh.models|. For full documentation, please visit https://docs.bokeh.org
4. Zobrazení jednoduchého grafu s naměřenými hodnotami
Vlastnosti knihovny Bokeh si ukážeme prakticky na několika demonstračních příkladech. První příklad je velmi jednoduchý a slouží pro vykreslení základního liniového grafu. Hodnoty na x-ové ose jsou reprezentovány typem range (přesněji řečeno instancí třídy range), hodnoty na y-ové ose jsou pak vypsány v seznamu (samozřejmě je možné použít i n-tici atd. atd.):
# hodnoty na x-ové a y-ové ose x = range(10) y = [1, 2, 7, 7, 4, 5, -5, 0, 2, 1]
Graf je vykreslen přes objekt typu bokeh.plotting.figure.Figure, jenž je zkonstruován příkazem:
# plocha pro graf p = figure()
Help on function figure in module bokeh.plotting.figure: figure(**kwargs: 'TAny') -> 'Figure' Create a new :class:`~bokeh.plotting.Figure` for plotting. All other keyword arguments are passed to :class:`~bokeh.plotting.Figure`. Returns: :class:`~bokeh.plotting.Figure`
Pokud nás bude zajímat jen liniový graf, provedeme vykreslení příkazem (metodou) plot, které se předají hodnoty na x-ové ose i na ose y-ové:
# vykreslení průběhu p.line(x, y)
A konečně pro zobrazení grafu na ploše webové stránky použijeme příkaz show (nejedná se o metodu):
# vykreslení grafu do plochy webové stránky show(p)
Po vykonání posledního příkazu se vygeneruje statická HTML stránka s JavaScriptovým kódem, která se otevře v okně (resp. přesněji řečeno v tabu) webového prohlížeče. Poté se skript ukončí, kdežto webová stránka bude stále „živá“:
Obrázek 2: Webová stránka vytvořená skriptem prvního demonstračního příkladu.
Help on function show in module bokeh.io.showing: show(obj: 'LayoutDOM | Application | ModifyDoc', browser: 'str | None' = None, new: 'BrowserTarget' = 'tab', notebook_handle: 'bool' = False, notebook_url: 'str' = 'localhost:8888', **kwargs: 'Any') -> 'CommsHandle | None' Immediately display a Bokeh object or application. :func:`show` may be called multiple times in a single Jupyter notebook cell to display multiple objects. The objects are displayed in order. Args: obj (LayoutDOM or Application or callable) : A Bokeh object to display. Bokeh plots, widgets, layouts (i.e. rows and columns) may be passed to ``show`` in order to display them. If |output_file| has been called, the output will be to an HTML file, which is also opened in a new browser window or tab. If |output_notebook| has been called in a Jupyter notebook, the output will be inline in the associated notebook output cell.
Úplný zdrojový kód dnešního prvního demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/01_linear_plot.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show, save # hodnoty na x-ové a y-ové ose x = range(10) y = [1, 2, 7, 7, 4, 5, -5, 0, 2, 1] # plocha pro graf p = figure() # vykreslení průběhu p.line(x, y) # vykreslení grafu do plochy webové stránky show(p)
5. Zobrazení titulku a legendy v grafu
Graf bez popisků a titulku je většinou nepoužitelný (pokud se ovšem nejedná záměr se snahou o zmatení čtenáře). Proto do grafu přidáme jak titulek, tak i popisky obou os:
# plocha pro graf p = figure(title="Naměřené hodnoty", x_axis_label='x', y_axis_label='y')
Navíc je možné popsat i jednotlivé průběhy v grafu. Prozatím sice zobrazujeme jediný průběh, ovšem i jeho popisek lze v případě potřeby zobrazit jako legendu:
# vykreslení průběhu naměřených hodnoy p.line(x, y, legend_label="Napětí", line_width=2)
S tímto výsledkem:
Obrázek 3: Graf vykreslený druhým příkladem.
Úplný zdrojový kód dnešního druhého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/02_linear_plot.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # hodnoty na x-ové a y-ové ose x = range(10) y = [1, 2, 7, 7, 4, 5, -5, 0, 2, 1] # plocha pro graf p = figure(title="Naměřené hodnoty", x_axis_label='x', y_axis_label='y') # vykreslení průběhu naměřených hodnoy p.line(x, y, legend_label="Napětí", line_width=2) # vykreslení grafu do plochy webové stránky show(p)
6. Kooperace s knihovnou NumPy, porovnání s Matplotlibem
V úvodních kapitolách byla jako alternativa ke knihovně Bokeh zmíněna knihovna Matplotlib. Pro porovnání si tedy ukažme, jak se vykresluje graf při použití této knihovny. Vykreslíme sinusovku, která bude vypočtena s využitím knihovny NumPy (ta dokáže provést výpočty pro všechny prvky vektoru atd.):
# naimportujeme všechny funkce a konstanty z knihovny `matplotlib.pyplot` import matplotlib.pyplot as plt # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y = np.sin(x) # vykreslit průběh funkce plt.plot(x, y) # popis os plt.xlabel("x") plt.ylabel("sin(x)") # zobrazení grafu plt.show()
Výsledek by měl vypadat následovně:
Obrázek 4: Graf vykreslený knihovnou Matplotlib.
Prakticky stejným způsobem můžeme postupovat v případě, že se využívá knihovna Bokeh a nikoli knihovna Matplotlib. Opět přitom pro výpočty použijeme knihovnu NumPy:
# hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y = np.sin(x)
Namísto plt.plot ovšem zavoláme metodu p.line:
# vykreslení průběhu p.line(x, y, legend_label="sin(x)", line_width=2)
Výsledkem je webová stránka s obsahem, který lze porovnat s předchozím obrázkem:
Obrázek 5: Graf vykreslený knihovnou Bokeh.
Úplný zdrojový kód dnešního třetího demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/03_sinus_plot.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y = np.sin(x) # plocha pro graf p = figure(title="sin(x)", x_axis_label='x', y_axis_label='sin(x)') # vykreslení průběhu p.line(x, y, legend_label="sin(x)", line_width=2) # vykreslení grafu do plochy webové stránky show(p)
7. Vykreslení průběhu dvou funkcí do jednoho grafu
Počet funkcí, jejichž průběhy se mají vykreslit do jediného grafu, není prakticky omezen. Prozatím si ukážeme způsob zobrazení průběhů dvou funkcí, a to navíc za předpokladu, že funkční hodnoty spadají do stejného rozsahu a není tedy nutné přidávat další y-ovou osu s odlišným měřítkem (nebo dokonce kombinovat lineární a logaritmické měřítko). Opět přitom pro výpočet funkčních hodnot využijeme knihovnu NumPy:
# hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y1 = np.sin(x) # hodnoty na y-ové ose y2 = np.cos(x)
Po výpočtu obou vektorů s hodnotami funkce oba průběhy vykreslíme. Povšimněte si, že je možné specifikovat popisek průběhu a taktéž barvu, která se pro vykreslení průběhu použije:
p.line(x, y1, legend_label="sin(x)", line_width=2, color="blue") p.line(x, y2, legend_label="cos(x)", line_width=2, color="red")
Výsledek by měl vypadat následovně:
Obrázek 6: Průběh dvou funkcí vykreslený do jediného grafu se shodnou y-ovou osou pro oba průběhy.
Úplný zdrojový kód dnešního čtvrtého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/04_sin_cos_plot.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y1 = np.sin(x) # hodnoty na y-ové ose y2 = np.cos(x) # plocha pro graf p = figure(title="sin(x) a cos(x)", x_axis_label='x', y_axis_label='sin(x) a cos(x)') # vykreslení průběhu p.line(x, y1, legend_label="sin(x)", line_width=2, color="blue") p.line(x, y2, legend_label="cos(x)", line_width=2, color="red") # vykreslení grafu do plochy webové stránky show(p)
Všechna podporovaná jména barev lze získat takto:
import bokeh.colors.named as n for clr in dir(n): if clr.islower(): print(clr)
S těmito výsledky:
NamedColor aliceblue annotations antiquewhite aqua aquamarine azure beige bisque black blanchedalmond blue blueviolet brown burlywood cadetblue chartreuse chocolate colors coral cornflowerblue cornsilk crimson cyan darkblue darkcyan darkgoldenrod darkgray darkgreen darkgrey darkkhaki darkmagenta darkolivegreen darkorange darkorchid darkred darksalmon darkseagreen darkslateblue darkslategray darkslategrey darkturquoise darkviolet deeppink deepskyblue dimgray dimgrey dodgerblue firebrick floralwhite forestgreen fuchsia gainsboro ghostwhite gold goldenrod gray green greenyellow grey honeydew hotpink indianred indigo ivory khaki lavender lavenderblush lawngreen lemonchiffon lightblue lightcoral lightcyan lightgoldenrodyellow lightgray lightgreen lightgrey lightpink lightsalmon lightseagreen lightskyblue lightslategray lightslategrey lightsteelblue lightyellow lime limegreen linen log logging magenta maroon mediumaquamarine mediumblue mediumorchid mediumpurple mediumseagreen mediumslateblue mediumspringgreen mediumturquoise mediumvioletred midnightblue mintcream mistyrose moccasin navajowhite navy oldlace olive olivedrab orange orangered orchid palegoldenrod palegreen paleturquoise palevioletred papayawhip peachpuff peru pink plum powderblue purple rebeccapurple red rosybrown royalblue saddlebrown salmon sandybrown seagreen seashell sienna silver skyblue slateblue slategray slategrey snow springgreen steelblue tan teal thistle tomato turquoise violet wheat white whitesmoke yellow yellowgreen
8. Alternativní způsob specifikace barev s využitím trojice hexadecimálních hodnot
V předchozím demonstračním příkladu jsme barvy specifikovali jménem:
# vykreslení průběhu p.line(x, y1, legend_label="sin(x)", line_width=2, color="blue") p.line(x, y2, legend_label="cos(x)", line_width=2, color="red")
Alternativně je však možné využít celý barvový prostor RGB (224 barvových odstínů) s využitím trojice hexadecimálních hodnot, z nichž každá může mít jednu či dvě číslice:
# vykreslení průběhu p.line(x, y1, legend_label="sin(x)", line_width=2, color="#00a0a0") p.line(x, y2, legend_label="cos(x)", line_width=2, color="#a0a000")
popř. zmenšený barvový prostor pro „jen“ 212 barev:
# vykreslení průběhu p.line(x, y1, legend_label="sin(x)", line_width=2, color="#00a0a0") p.line(x, y2, legend_label="cos(x)", line_width=2, color="#a0a000")
Výsledek bude velmi podobný příkladu z předchozí kapitoly:
Obrázek 7: Průběh dvou funkcí vykreslený do jediného grafu se shodnou y-ovou osou pro oba průběhy.
Úplný zdrojový kód dnešního pátého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/05_sin_cos_plot.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y1 = np.sin(x) # hodnoty na y-ové ose y2 = np.cos(x) # plocha pro graf p = figure(title="sin(x) a cos(x)", x_axis_label='x', y_axis_label='sin(x) a cos(x)') # vykreslení průběhu p.line(x, y1, legend_label="sin(x)", line_width=2, color="#00a0a0") p.line(x, y2, legend_label="cos(x)", line_width=2, color="#a0a000") # vykreslení grafu do plochy webové stránky show(p)
9. Další styly vykreslení průběhu funkce/hodnot do grafu
Průběh funkce (nebo naměřených hodnot) nemusí být zobrazen jen s využitím lomené čáry (polyčáry), ale je možné použít i další styly vykreslování. Zatímco v knihovně Matplotlib je styl předán, resp. specifikován v pojmenovaném parametru metody plot, je tomu v knihovně Bokeh odlišně – používají se totiž samostatné metody:
# vykreslení průběhu p.square_dot(x, y1, legend_label="sin(x)", line_width=2, color="blue") p.circle(x, y2, legend_label="cos(x)", line_width=2, color="red")
S následujícím výsledkem:
Obrázek 8: Graf se dvěma průběhy zobrazenými dvěma rozdílnými styly.
Úplný zdrojový kód dnešního šestého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/06_sin_cos_circles.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y1 = np.sin(x) # hodnoty na y-ové ose y2 = np.cos(x) # plocha pro graf p = figure(title="sin(x) a cos(x)", x_axis_label='x', y_axis_label='sin(x) a cos(x)') # vykreslení průběhu p.square_dot(x, y1, legend_label="sin(x)", line_width=2, color="blue") p.circle(x, y2, legend_label="cos(x)", line_width=2, color="red") # vykreslení grafu do plochy webové stránky show(p)
10. Sloupcové grafy
Sloupcové grafy jsou v knihovně Bokeh chápány jako grafy vykreslené odlišným stylem a tudíž se (logicky) nepoužívá metoda p.line, ale metoda nazvaná p.vbar. Zkusme tedy tuto metodu zavolat bez dalších úprav:
# vykreslení průběhu naměřených hodnoy p.vbar(x, y, legend_label="Napětí", line_width=2)
Pokud se však pouze zamění p.line za p.vbar, graf ve skutečnosti nebude obsahovat žádné sloupce:
Obrázek 9: Chybějící graf s naměřenými hodnotami.
Úplný zdrojový kód dnešního sedmého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/07_bars.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # hodnoty na x-ové a y-ové ose x = range(10) y = [1, 2, 7, 7, 4, 5, -5, 0, 2, 1] # plocha pro graf p = figure(title="Naměřené hodnoty", x_axis_label='x', y_axis_label='y') # vykreslení průběhu naměřených hodnoy p.vbar(x, y, legend_label="Napětí", line_width=2) # vykreslení grafu do plochy webové stránky show(p)
Problém v předchozím příkladu je způsoben tím, že z důvodů vysvětlených v další kapitole je nutné hodnoty předat v pojmenovaném parametru top, což znamená, že volání p.vbar musí vypadat následovně:
# vykreslení průběhu naměřených hodnoy p.vbar(x, top=y, legend_label="Napětí", line_width=2)
Výsledek bude nyní vypadat takto:
Obrázek 10: Sloupcový graf s naměřenými hodnotami.
Úplný zdrojový kód dnešního osmého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/08_bars.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # hodnoty na x-ové a y-ové ose x = range(10) y = [1, 2, 7, 7, 4, 5, -5, 0, 2, 1] # plocha pro graf p = figure(title="Naměřené hodnoty", x_axis_label='x', y_axis_label='y') # vykreslení průběhu naměřených hodnoy p.vbar(x, top=y, legend_label="Napětí", line_width=2) # vykreslení grafu do plochy webové stránky show(p)
Sloupcové grafy jsou poněkud odlišné od ostatních typů grafů. Například je u nich možné specifikovat šířky sloupců:
# vykreslení průběhu naměřených hodnoy p.vbar(x, top=y, width=0.7, legend_label="Napětí", line_width=2)
Výsledek vypadá lépe, než graf na desátém obrázku:
Obrázek 11: Upravený sloupcový graf s naměřenými hodnotami.
Úplný zdrojový kód dnešního devátého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/09_bars.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # hodnoty na x-ové a y-ové ose x = range(10) y = [1, 2, 7, 7, 4, 5, -5, 0, 2, 1] # plocha pro graf p = figure(title="Naměřené hodnoty", x_axis_label='x', y_axis_label='y') # vykreslení průběhu naměřených hodnoy p.vbar(x, top=y, width=0.7, legend_label="Napětí", line_width=2) # vykreslení grafu do plochy webové stránky show(p)
11. Sloupcový graf se specifikací začátků a konce sloupců
V předchozí kapitole jsme viděli, že hodnoty pro sloupcový graf se předávají v pojmenovaném parametru top. Je tomu tak z toho důvodu, že lze specifikovat nejenom výšku sloupců, ale i výšku jejich základny (tj. vzdálenost základny od osy x). Pro tento účel se specifikuje nepovinný pojmenovaný parametr bottom:
# vykreslení průběhu p.vbar(x, top=y1, bottom=y2, width=0.01, legend_label="sin(x)", line_width=2, color="blue")
Výsledkem je poněkud uměle vytvořený graf, který vypadá následovně:
Obrázek 12: Sloupcový graf, v němž se specifikuje jak výška sloupců, tak i vzdálenost jejich základny od osy x.
Úplný zdrojový kód tohoto demonstračního příkladu:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(0, 2 * np.pi, 100) # hodnoty na y-ové ose y1 = np.sin(x) # hodnoty na y-ové ose y2 = np.cos(x) # plocha pro graf p = figure(title="sin(x) a cos(x)", x_axis_label='x', y_axis_label='sin(x) a cos(x)') # vykreslení průběhu p.vbar(x, top=y1, bottom=y2, width=0.01, legend_label="sin(x)", line_width=2, color="blue") # vykreslení grafu do plochy webové stránky show(p)
12. Vykreslení kružnice a spirály (parametrické křivky)
Vzhledem k tomu, že při vykreslení grafů předáváme jak x-ové, tak i y-ové hodnoty, lze velmi snadno vykreslit parametrické křivky. Příkladem může být běžná kružnice, která může na webové stránce vypadat následovně:
Obrázek 13: Kružnice jako parametrická křivka.
Pro vykreslení této křivky využijeme funkce numpy.sin a numpy.cos, které dokážou zpracovat celý vektor hodnot:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty parametrické křivky t = np.linspace(0, 2 * np.pi, 100) y1 = np.sin(t) y2 = np.cos(t) # plocha pro graf p = figure(title="Circle", x_axis_label='x', y_axis_label='y') # vykreslení průběhu p.line(y1, y2, line_width=2, color="blue") # vykreslení grafu do plochy webové stránky show(p)
Podobným způsobem lze vykreslit spirálu:
Obrázek 14: Spirála jako parametrická křivka.
Pro vykreslení spirály byl použit tento programový kód:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # počet bodů na spirále points = 150 # úhel v polárním grafu theta = np.linspace(0.01, 8*np.pi, points) # koeficient spirály k = 0.15 # funkce: vzdálenost od středu radius = np.exp(k*theta) y1 = radius*np.sin(theta) y2 = radius*np.cos(theta) # plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y') # vykreslení průběhu p.line(y1, y2, line_width=2, color="blue") # vykreslení grafu do plochy webové stránky show(p)
13. Export grafu do rastrového obrázku ve formáty PNG
Knihovna Bokeh umožňuje export grafu do rastrového obrázku, například ve formátu PNG atd. Problém spočívá v tom, že pro export je nutné získat obsah okna prohlížeče a pro tento účel se používá Selenium (to se nám ty moderní technologie komplikují, že?). Takže teoreticky je sice možné následující skript spustit kdekoli, ovšem pokud nebude nainstalována knihovna Selenium pro zvolený prohlížeč, dojde na konci při volání funkce export_png() k běhové chybě:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # export grafu from bokeh.io import export_png # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # počet bodů na spirále points = 150 # úhel v polárním grafu theta = np.linspace(0.01, 8*np.pi, points) # koeficient spirály k = 0.15 # funkce: vzdálenost od středu radius = np.exp(k*theta) y1 = radius*np.sin(theta) y2 = radius*np.cos(theta) # plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y') # vykreslení průběhu p.line(y1, y2, line_width=2, color="blue") # vykreslení grafu do plochy webové stránky show(p) # export grafu export_png(p, filename="plot1.png")
Výsledek pokusu o spuštění skript na počítači bez Selenia dopadne neslavně:
Traceback (most recent call last): ... ... ... ModuleNotFoundError: No module named 'selenium' The above exception was the direct cause of the following exception: Traceback (most recent call last): ... ... ... ModuleNotFoundError: No module named 'selenium' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "13_output_png.py", line 38, in <module> export_png(p, filename="plot1.png") File "/home/ptisnovs/.local/lib/python3.8/site-packages/bokeh/io/export.py", line 111, in export_png image = get_screenshot_as_png(obj, width=width, height=height, driver=webdriver, timeout=timeout) File "/home/ptisnovs/.local/lib/python3.8/site-packages/bokeh/io/export.py", line 229, in get_screenshot_as_png from .webdriver import webdriver_control File "/home/ptisnovs/.local/lib/python3.8/site-packages/bokeh/io/webdriver.py", line 24, in <module> import_required("selenium.webdriver", File "/home/ptisnovs/.local/lib/python3.8/site-packages/bokeh/util/dependencies.py", line 82, in import_required raise RuntimeError(error_msg) from e RuntimeError: To use bokeh.io image export functions you need selenium ('conda install selenium' or 'pip install selenium')
14. Nastavení stylu zobrazení legendy
Knihovna Bokeh nabízí uživatelům poměrně velké možnosti nastavení stylu zobrazení grafu. Využívá se přitom toho, že renderovací část knihovny Bokeh je postavena na webových technologiích a tím pádem i na CSS. Ukažme si nyní způsob modifikace zobrazení legendy. Ovšem na tomto místě je vhodné poznamenat, že naprosto stejný způsob je možné použít i u změny stylu zobrazení jakékoli jiné části grafu (což je mnohem jednodušší, než je tomu například v Matplotlibu):
# vykreslení průběhu p.line(y1, y2, line_width=2, color="blue", legend_label="Spira mirabilis") # nastavení stylu zobrazení legendy p.legend.location = "top_left" p.legend.border_line_width = 3 p.legend.border_line_color = "#8080a0" p.legend.border_line_alpha = 0.8 p.legend.background_fill_color = "#8080a0" p.legend.background_fill_alpha = 0.2
Výsledkem bude tento graf (zaměřte se především na to, jakým způsobem je v tomto grafu zobrazena legenda):
Obrázek 16: Graf s parametrickou funkcí i se zobrazenou legendou.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # počet bodů na spirále points = 150 # úhel v polárním grafu theta = np.linspace(0.01, 8*np.pi, points) # koeficient spirály k = 0.15 # funkce: vzdálenost od středu radius = np.exp(k*theta) y1 = radius*np.sin(theta) y2 = radius*np.cos(theta) # plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y') # vykreslení průběhu p.line(y1, y2, line_width=2, color="blue", legend_label="Spira mirabilis") # nastavení stylu zobrazení legendy p.legend.location = "top_left" p.legend.border_line_width = 3 p.legend.border_line_color = "#8080a0" p.legend.border_line_alpha = 0.8 p.legend.background_fill_color = "#8080a0" p.legend.background_fill_alpha = 0.2 # vykreslení grafu do plochy webové stránky show(p)
15. Specifikace velikosti grafu
Všechny předchozí grafy měly nastavenou výchozí velikost, ovšem knihovna Bokeh pochopitelně umožňuje velikost plochy grafu modifikovat. K tomuto účelu slouží nepovinné parametry nazvané width a height funkce/konstruktoru figure. Velikost je sice specifikována v pixelech, ovšem na tomto místě je vhodné poznamenat, že reálný graf bude nepatrně větší kvůli zobrazení os, toolbaru atd.:
# plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y', width=400, height=400)
Výsledkem bude tento graf (rozlišení screenshotu jsem schválně ponechal stejné, jako u ostatních screenshotů, takže velikost samotného grafu je možné jednoduše porovnat):
Obrázek 17: Graf zmenšený na velikost 400×400 délkových jednotek.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # počet bodů na spirále points = 150 # úhel v polárním grafu theta = np.linspace(0.01, 8*np.pi, points) # koeficient spirály k = 0.15 # funkce: vzdálenost od středu radius = np.exp(k*theta) y1 = radius*np.sin(theta) y2 = radius*np.cos(theta) # plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y', width=400, height=400) # vykreslení průběhu p.line(y1, y2, line_width=2, color="blue", legend_label="Spira mirabilis") p.legend.location = "top_right" p.legend.border_line_width = 3 p.legend.border_line_color = "#8080a0" p.legend.border_line_alpha = 0.8 p.legend.background_fill_color = "#8080a0" p.legend.background_fill_alpha = 0.2 # vykreslení grafu do plochy webové stránky show(p)
16. Nastavení limitu na souřadných osách
Víme již, jak zajistit, že graf bude zobrazen na čtvercové ose:
# plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y', width=400, height=400)
To ovšem neznamená, že například kružnice bude zobrazena skutečně jako kružnice, či že naše spirála nebude stlačena v horizontálním nebo vertikálním směru. Musíme totiž ještě buď nastavit parametr aspect na hodnotu 1.0 nebo přesně specifikovat rozsahy hodnot na obou souřadných osách. Například pro spirálu ukázanou již v předchozích příkladech to znamená, že budeme muset explicitně nastavit atributy x_range a y_range, a to následujícím způsobem:
# nastavení rozsahu na osách p.x_range = Range1d(-35, 25) p.y_range = Range1d(-30, 30)
Povšimněte si, že rozsah hodnot na x-ové ose je 25-(-35)=60 a na y-ové ose je 30-(-30)=60. Jedná se tedy o shodný rozsah, což znamená, že na grafu o velikosti 400×400 délkových jednotek (tedy o čtvercové ploše) bude měřítko na obou osách shodné. Můžeme se o tom ostatně snadno přesvědčit:
Obrázek 18: Shodné měřítko na obou osách.
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show from bokeh.models import Range1d # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # počet bodů na spirále points = 150 # úhel v polárním grafu theta = np.linspace(0.01, 8*np.pi, points) # koeficient spirály k = 0.15 # funkce: vzdálenost od středu radius = np.exp(k*theta) y1 = radius*np.sin(theta) y2 = radius*np.cos(theta) # plocha pro graf p = figure(title="Spiral", x_axis_label='x', y_axis_label='y', width=400, height=400) # nastavení rozsahu na osách p.x_range = Range1d(-35, 25) p.y_range = Range1d(-30, 30) # vykreslení průběhu p.line(y1, y2, line_width=2, color="blue", legend_label="Spira mirabilis") p.legend.location = "top_right" p.legend.border_line_width = 3 p.legend.border_line_color = "#8080a0" p.legend.border_line_alpha = 0.8 p.legend.background_fill_color = "#8080a0" p.legend.background_fill_alpha = 0.2 # vykreslení grafu do plochy webové stránky show(p)
17. Měřítka na osách
Pokusme se nyní v jediném grafu zobrazit průběh čtveřice funkcí, konkrétně lineární funkce, kvadratické funkce, kubické funkce a navíc ještě funkce exponenciální:
# hodnoty na y-ové ose y1 = x # hodnoty na y-ové ose y2 = x ** 2 # hodnoty na y-ové ose y3 = x ** 3 # hodnoty na y-ové ose y4 = 2 ** x
V případě, že se využije y-ová osa s lineárním měřítkem, bude výsledek vypadat následovně:
Obrázek 19: Čtveřice různých funkcí zobrazená v jediném grafu.
Problém spočívá v tom, že pokud zvětšíme rozsah na x-ové ose, budou ostatní funkce až na funkci exponenciální zobrazeny prakticky přímo na x-ové ose, což nemusí být kýžený výsledek. Jedno z možných řešení (existuje totiž více řešení) je ukázáno v navazující kapitole.
Předchozí graf byl zobrazen následujícím skriptem:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(-5, 5, 100) # hodnoty na y-ové ose y1 = x # hodnoty na y-ové ose y2 = x ** 2 # hodnoty na y-ové ose y3 = x ** 3 # hodnoty na y-ové ose y4 = 2 ** x # plocha pro graf p = figure(title="Functions", x_axis_label='x', y_axis_label='Functions') # vykreslení průběhu p.line(x, y1, legend_label="x", line_width=2, color="#00a0a0") p.line(x, y2, legend_label="x^2", line_width=2, color="#a0a000") p.line(x, y3, legend_label="x^3", line_width=2, color="#00a0a0") p.line(x, y4, legend_label="2^x", line_width=2, color="#a000a0") # vykreslení grafu do plochy webové stránky show(p)
18. Logaritmické měřítko na y-ové ose
Jak jsme mohli vidět na obrázku z předchozí kapitoly, není lineární měřítko na y-ové ose vhodné pro všechny typy zobrazovaných dat. Typicky se jedná o případ funkcí s exponenciálním průběhem nebo o funkce (hodnoty), které se tomuto průběhu blíží. V takovém případě je vhodné změnit měřítko na y-ové ose, tedy na ose hodnot. V knihovně Bokeh je možné měřítko na y-ové ose změnit nepovinným parametrem nazvaným y_axis_type, který se předává konstruktoru figure. Na výběr jsou hodnoty „linear“ (výchozí), „log“, „datetime“ a „mercator“.
My dnes použijeme hodnotu „log“, a to následujícím způsobem:
# plocha pro graf p = figure(title="Functions", x_axis_label='x', y_axis_label='Functions', y_axis_type="log")
Nyní se průběh všech funkcí v grafu dosti zásadním způsobem změní. Povšimněte si, jak jsou zvýrazněny hodnoty bližší nule:
Obrázek 20: Logaritmické měřítko na y-ové ose.
Úplný zdrojový kód dnešního osmnáctého demonstračního příkladu naleznete na adrese https://github.com/tisnik/most-popular-python-libs/blob/master/bokeh/18_log_scale.py:
# naimportujeme vybrané funkce z knihovny `bokeh.plotting` from bokeh.plotting import figure, show # taktéž budeme potřebovat některé funkce z knihovny `numpy` import numpy as np # vykreslení průběhu funkce sin # hodnoty na x-ové ose x = np.linspace(-5, 5, 100) # hodnoty na y-ové ose y1 = x # hodnoty na y-ové ose y2 = x ** 2 # hodnoty na y-ové ose y3 = x ** 3 # hodnoty na y-ové ose y4 = 2 ** x # plocha pro graf p = figure(title="Functions", x_axis_label='x', y_axis_label='Functions', y_axis_type="log") # vykreslení průběhu p.line(x, y1, legend_label="x", line_width=2, color="#00a0a0") p.line(x, y2, legend_label="x^2", line_width=2, color="#a0a000") p.line(x, y3, legend_label="x^3", line_width=2, color="#00a0a0") p.line(x, y4, legend_label="2^x", line_width=2, color="#a000a0") # vykreslení grafu do plochy webové stránky show(p)
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- bokeh na GitHubu
https://github.com/bokeh/bokeh - First steps 1: Creating a line chart
https://docs.bokeh.org/en/latest/docs/first_steps/first_steps1.html - Python Bokeh tutorial – Interactive Data Visualization with Bokeh
https://www.geeksforgeeks.org/python-bokeh-tutorial-interactive-data-visualization-with-bokeh/ - The R Project for Statistical Computing
https://www.r-project.org/ - An Introduction to R
https://cran.r-project.org/doc/manuals/r-release/R-intro.pdf - R (programming language)
https://en.wikipedia.org/wiki/R_(programming_language) - Graphics, ggplot2
http://r4stats.com/examples/graphics-ggplot2/ - Seriál Programovací jazyk Julia
https://www.root.cz/serialy/programovaci-jazyk-julia/ - Plotly
https://plotly.com/ - pyecharts
https://github.com/pyecharts/pyecharts/blob/master/README.en.md - Tvorba grafů v Jupyter Notebooku s využitím knihovny Matplotlib
https://www.root.cz/clanky/tvorba-grafu-v-jupyter-notebooku-s-vyuzitim-knihovny-matplotlib/