07: Vizualizace a manipulace s grafikou
Tento notebook je výukovým materiálem v předmětu BI-JUL.21 vyučovaném v zimním semestru akademického roku 2024/2025 Tomášem Kalvodou. Tvorba těchto materiálů byla podpořena NVS FIT.
Hlavní stránkou předmětu, kde jsou i další notebooky a zajímavé informace, je jeho Course Pages stránka.
versioninfo()Julia Version 1.12.0 Commit b907bd0600f (2025-10-07 15:42 UTC) Build Info: Official https://julialang.org release Platform Info: OS: Linux (x86_64-linux-gnu) CPU: 8 × Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz WORD_SIZE: 64 LLVM: libLLVM-18.1.7 (ORCJIT, skylake) GC: Built with stock GC Threads: 1 default, 1 interactive, 1 GC (on 8 virtual cores)
1. Vizualizace
Nejen v Julia existuje celá řada nástrojů/knihoven pro vytváření různých grafů, diagramů a vizualizací. V Python ekosystému je asi nejpoúžívanější Matplotlib, který je k dispozici i v Julia (viz PyPlot.jl níže).
Všechny tyto nástroje se snaží dosáhnout podobných cílů a nabízejí různě kvalitní implementace. Dost často bývá otázkou vkusu, který z nástrojů použít. V tomto notebooku ukážeme tři možnosti:
Paleta možností je ale širší. Zmiňme alespoň následující (pro zájemce k prozkoumání):
Plots: nadstavba nad několika grafickými balíčky.Gadfly: atraktivní a interaktivní výstup.Winston: malý a rychlý, terminologií vychází s MATLABu.UnicodePlots: zajímavý nástroj vytvářející grafy pomocí unicode symbolů a fungující i v terminálu.
Možností je opravdu mnoho a tento notebook je spíše experimentální.
Při používání tohoto notebooku doporučuji vždy mezi sekcemi (různými balíčky) restartovat jádro. Moduly mají podobné zaměření a mohlo by dojít ke kolizím pojmenování metod.
1.1 GR.jl
Balíček GR.jl poskytuje Julia rozhranní k GR frameworku.
GR byl vyvinut skupinou Scientific IT-Systems z Peter Grünberg Institutu na Forschungszentrum Jülich.
- Mezi jeho přednosti patří rychlost.
- Mezi nevýhody bych zařadil nekvalitní dokumentaci a nekonzistenci API. (Chyba může být ale na straně Julia balíčku...)
Tento balíček pravděpodobně nemáte nainstalovaný, takže nejprve tento musíme nainstalovat (případně viz pokyny k instalaci systémových závislostí):
(@v1.11) pkg> add GRZde v notebooku projdeme jen ty nejužitečnější části balíčku. Také tento balíček použijeme jako jakéhosi průvodci po možnostech, u ostatních se totiž dost opakují. Zvídavého čtenáře odkazujeme na dokumentaci s ukázkami dalších typů grafů.
using GRTvorbu jednotlivých grafů můžeme oddělit zavoláním figure, případně subplot. K dispozici také je savefig.
plot a oplot
Pomocí plot nepřekvapivě vykreslíme 2D graf funkce. Poziční argumenty mohou mít následující význam:
- stejně dlouhé vektory
xayreprezentující souřadnice bodů, - vektor
xa funkce pro výpočet -nové hodnoty, - pouze vektor
y, nezávisle proměnná pak odpovídá indexům.
Dále plot přijímá několik keyword argumentů, nejzajímavější asi jsou:
title: titulek grafu (alternativně metodatitle),xlabel: titulek grafu (alternativně metodaxlabel),ylabel: titulek grafu (alternativně metodaylabel),xlim,ylim: rozsah jednotlivých os (alternativně metodyxlimaylim),
a metoda
legend: popisky jednotlivých grafů.
Následuje několik jednoduchých ukázek:
xs = LinRange(-10, 10, 100)
ys = sin.(xs)
figure()
plot(xs, ys, title="\$y=\\sin(x)\$", xlabel="\$x\$", ylabel="\$y\$", ylim=(-1.2, 1.2))Poznámka: Všimněte si, že vykreslení prvního grafu je (nejen zde, takřka vždy) výrazně pomalejší, než těch následujících. To je způsobeno probíhající JIT kompilací. Tento efekt je v Julia světě označován zkratkou TTFP (Time To First Plot). Tomuto efektu se lze v některých situacích částečně vyhnout pomocí PackageCompiler.jl.
Pokud chceme vykreslit více dat do jednoho obrázku, použijeme k tomu oplot, nastavení popisků atp. se bohužel bere z posledního volání oplus (lze to obejít nastavením těchto parametrů pomocí metod, ne pomocí keyword hodnot).
legend("\$\\sin\$", "\$\\cos\$")
figure()
plot(xs, ys, title="\$y=\\sin(x)\$", xlabel="\$x\$", ylabel="\$y\$", ylim=(-1.2, 1.2))
oplot(xs, cos, title="Trigonometrické funkce", xlabel="\$x\$", ylabel="\$y\$", ylim=(-1.2, 1.2))Styl čáry lze kontrolovat pomocí klíčů:
setlinetype: druh čáry (vizGR.LINETYPE_...),setlinewidth: šířka čáry,setlinecolorind: barva čáry (???).
figure()
setlinetype(GR.LINETYPE_TRIPLE_DOT)
plot(LinRange(-1, 1, 50), exp, linewidth=5)figure()
setlinetype(GR.LINETYPE_TRIPLE_DOT)
plot(LinRange(-1, 1, 3), exp, linewidth=5)Zde jsem se vzdal a proto:
Cvičení: ???
- Pokuste
*se změnit barvu čáry například na červenou.
* tj. úspěch není nutný.
figure()
@which plot(LinRange(-1, 1, 50), exp)scatter
Scatter plot vykresluje pouze jednotlivé body a neinterpoluje (lineárně) mezi nimi:
figure()
title("\$a_n = (-1)^n\$")
legend("\$a_n\$")
xlabel("\$n\$")
ylabel("\$a_n\$")
plot([(-1)^n for n=0:9])?legendsearch: legend seekend Signed begin prepend! signed setenv Regex end
Set the legend of the plot.
The plot legend is drawn using the extended text function GR.textext. You can use a subset of LaTeX math syntax, but will need to escape certain characters, e.g. parentheses. For more information see the documentation of GR.textext.
:param args: The legend strings
Usage examples:
.. code-block:: julia
julia> # Set the legends to "a" and "b"
julia> legend("a", "b")
figure()
title("\$a_n = (-1)^n\$")
legend("\$a_n\$")
xlabel("\$n\$")
ylabel("\$a_n\$")
x = Float64[j for j=0:9]
y = Float64[(-1)^n for n=0:9]
scatter(x, y, xlim=(-0.2, 9.2), ylim=(-1.2, 1.2))figure()
title("\$a_n = (-1)^n\$")
legend("\$a_n\$")
xlabel("\$n\$")
ylabel("\$a_n\$")
x = Float64[j for j=0:9]
y = Float64[(-1)^n for n=0:9]
s = 2*ones(10)
c = rand(0:255, 10)
scatter(x, y, s, c, xlim=(-0.2, 9.2), ylim=(-1.2, 1.2))x10-element Vector{Float64}:
0.0
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0y10-element Vector{Float64}:
1.0
-1.0
1.0
-1.0
1.0
-1.0
1.0
-1.0
1.0
-1.0s10-element Vector{Float64}:
2.0
2.0
2.0
2.0
2.0
2.0
2.0
2.0
2.0
2.0c10-element Vector{Int64}:
110
119
18
234
21
4
196
246
222
134figure()
x = [z/10 for z=0:10]
y = LinRange(0, 1, 11)
s = LinRange(1, 3, 11)
c = LinRange(0, 255, 11)
scatter(x, y, s, c)x11-element Vector{Float64}:
0.0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1.0y11-element LinRange{Float64, Int64}:
0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0s11-element LinRange{Float64, Int64}:
1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0c11-element LinRange{Float64, Int64}:
0.0, 25.5, 51.0, 76.5, 102.0, 127.5, 153.0, 178.5, 204.0, 229.5, 255.0Můžeme snadno i změnit barevnou škálu.
figure()
ts = LinRange(0, 4pi, 40)
x = [t * sin(t) for t in ts]
y = [t * cos(t) for t in ts]
s = ts
c = LinRange(0, 255, 40)
scatter(x, y, s, c, colormap=GR.COLORMAP_SPRING)plot3
plot3 je 3D analog čárového grafu. Přijímá tedy tři vektory x, y a z, jejichž složky popořadě tvoří jednotlivé body křivky.
ts = LinRange(0, 20, 400)
xs = [ sin(t) for t in ts ]
ys = [ cos(t) for t in ts ]
zs = ts
figure()
plot3(xs, ys, zs, xlabel="x", ylabel="y", zlabel="z", title="Šroubovice")histogram a hexbin, Cvičení
Histogram bere vektor x hodnot a jako keyword argument nbins udávající počet přihrádek.
Budeme ho ilustrovat na náhodném generátoru bodů na kružnici (vzpomeňte dřívější diskuzi).
n = 10_000
xs = rand(n) .- 0.5
ys = rand(n) .- 0.5;figure()
scatter(xs, ys, xlim=(-1.2,1.2), ylim=(-1.2,1.2))for j=1:length(xs)
r = sqrt(xs[j]^2 + ys[j]^2)
xs[j] /= r
ys[j] /= r
endfigure()
scatter(xs, ys)φs = [ atan(ys[j], xs[j]) for j=1:length(xs) ];figure()
histogram(φs, nbins=50, xlabel="\$\\varphi\$", ylabel="hits")savefig("test.pdf")savefig("test.png")savefig("test.svg")Takovýto generátor evidentně preferuje směry odpovídající osám kvadrantů.
Dvourozměrný ekvivalent histogramu (tj. pro dvousložková data) je metoda hexbin, která funguje analogicky, jen rovinu rozděluje na šestiúhelníky.
xs = rand(10^6)
ys = randn(10^6)
figure()
hexbin(xs, ys)contour a contourf
Contour plot je vizualizace funkce dvou proměnných, která v jejím definičním oboru znázorňuje křivky, kde je daná funkce konstantní (tzv. kontury). Varianta s f v názvu vybarvuje plochy mezi konturami (fill).
Metody očekávají buď trojici vektorů x, y, z, kde x a y definují souřadnice mřížky a z obsahuje funkční hodnoty na této mřížce (tj. z je matice). Místo z můžeme předat funkci dvou proměnných.
figure()
contour(LinRange(-1, 1, 50), LinRange(-2, 2, 50), (x, y) -> 3x^2 + y^2,
colormap=GR.COLORMAP_AUTUMN, xlabel="\$x\$", ylabel="\$y\$")Pozor, jak se GR chová k pořadí os v buňce níže!
figure()
xs = LinRange(-10, 10, 150)
ys = LinRange(-15, 15, 100)
zs = [ sin(x)*cos(y) for y in ys, x in xs ]
contourf(xs, ys, zs)