Jdi na navigaci předmětu

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 GR

Zde 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 GR

Tvorbu 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 x a y reprezentující souřadnice bodů,
  • vektor x a funkce pro výpočet yy-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ě metoda title),
  • xlabel: titulek grafu (alternativně metoda xlabel),
  • ylabel: titulek grafu (alternativně metoda ylabel),
  • xlim, ylim: rozsah jednotlivých os (alternativně metody xlim a ylim),

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 (viz GR.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í: ???

  1. Pokuste* se změnit barvu čáry například na červenou.

* tj. úspěch není nutný.

figure()

@which plot(LinRange(-1, 1, 50), exp)
plot(args::Union{AbstractString, Function, AbstractMatrix, AbstractVector}...; kv...) in GR.jlgr at /home/kalvin/.julia/packages/GR/TPhYU/src/jlgr.jl:1894

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])
?legend
search: 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))
x
10-element Vector{Float64}:
 0.0
 1.0
 2.0
 3.0
 4.0
 5.0
 6.0
 7.0
 8.0
 9.0
y
10-element Vector{Float64}:
  1.0
 -1.0
  1.0
 -1.0
  1.0
 -1.0
  1.0
 -1.0
  1.0
 -1.0
s
10-element Vector{Float64}:
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
 2.0
c
10-element Vector{Int64}:
 110
 119
  18
 234
  21
   4
 196
 246
 222
 134
figure()
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)
x
11-element Vector{Float64}:
 0.0
 0.1
 0.2
 0.3
 0.4
 0.5
 0.6
 0.7
 0.8
 0.9
 1.0
y
11-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.0
s
11-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.0
c
11-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.0

Můž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
end
figure()
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)