--- engine: julia --- ```{julia} #| error: false #| echo: false #| output: false using InteractiveUtils ``` # Plots und Datenvisualisierung in Julia: _Plots.jl_ Es gibt zahlreiche Grafikpakete für Julia. Zwei oft genutzte sind [Makie.jl](https://docs.makie.org/stable/) und [Plots.jl](https://docs.juliaplots.org/latest/). Bevor wir diese genauer vorstellen, seien noch einige andere Pakete aufgelistet. ## Kurze Übersicht: einige Grafikpakete | Paket/Doku | Tutorial | Beispiele | Bemerkungen | |:----|:--|:--|:--------| |[Plots.jl](https://docs.juliaplots.org/latest/) | [Tutorial](https://docs.juliaplots.org/latest/tutorial/) | [Galerie](https://goropikari.github.io/PlotsGallery.jl/) | konzipiert als einheitliches Interface zu verschiedenen _backends_ (Grafikbibliotheken) | | [Makie.jl](https://docs.makie.org/stable/) | [Basic tutorial](https://docs.makie.org/v0.21/tutorials/basic-tutorial) | [Beautiful Makie](https://beautiful.makie.org/) | "data visualization ecosystem for Julia", Backends: Cairo (Vektorgrafik), OpenGL, WebGL | |[PlotlyJS.jl](http://juliaplots.org/PlotlyJS.jl/stable/) | [Getting started](https://plotly.com/julia/getting-started/)| [Examples](https://plotly.com/julia/plotly-fundamentals/)| Interface zur [Plotly](https://plotly.com/graphing-libraries/) Javascript-Grafikbibliothek | | [Gadfly.jl](https://gadflyjl.org/stable/)| [Tutorial](https://gadflyjl.org/stable/tutorial/) | [Galerie](https://github.com/GiovineItalia/Gadfly.jl?tab=readme-ov-file#gallery)| "a plotting and data visualization system written in Julia, influenced by R's [ggplot2](https://ggplot2.tidyverse.org/)" | | [Bokeh.jl](https://cjdoris.github.io/Bokeh.jl/stable/) | | [Galerie](https://cjdoris.github.io/Bokeh.jl/stable/gallery/)| Julia-Frontend für [Bokeh](https://bokeh.org/) | |[VegaLite.jl](https://www.queryverse.org/VegaLite.jl/stable/) | [Tutorial](https://www.queryverse.org/VegaLite.jl/stable/gettingstarted/tutorial/)| [Examples](https://www.queryverse.org/VegaLite.jl/stable/examples/examples_barcharts/)| Julia-Frontend für [Vega-Lite](https://vega.github.io/vega-lite/)| | [Luxor.jl](http://juliagraphics.github.io/Luxor.jl/stable/) |[Tutorial](https://juliagraphics.github.io/Luxor.jl/stable/tutorial/helloworld/)|[Examples](https://juliagraphics.github.io/Luxor.jl/stable/example/moreexamples/)| Allgemeine Vektorgrafik/Illustrationen | | [Javis.jl](https://juliaanimators.github.io/Javis.jl/stable/) |[Tutorials](https://juliaanimators.github.io/Javis.jl/stable/tutorials/)| [Examples](https://juliaanimators.github.io/Javis.jl/stable/examples/)| *Animierte* Vektorgrafik | [TidierPlots.jl](https://github.com/TidierOrg/TidierPlots.jl)| [Reference](https://tidierorg.github.io/TidierPlots.jl/latest/) || "is a 100% Julia implementation of the R package ggplot2 powered by Makie.jl"| |[PythonPlot.jl](https://github.com/JuliaPy/PythonPlot.jl)| |[Examples (in Python)](https://matplotlib.org/stable/gallery/index.html)| Interface zu Matplotlib (Python), 1:1-Übertragung der Python-API, deswegen s. [Matplotlib-Dokumentation](https://matplotlib.org/stable/api/pyplot_summary.html) : {.striped .hover} ## Plots.jl ### Einfache Plots Die `plot()`-Funktion erwartet im einfachsten Fall: - als erstes Argument einen Vektor von $x$-Werten der Länge $n$ und - als zweites Argument einen gleichlangen Vektor mit den dazugehörigen $y$-Werten. - Das zweite Argument kann auch eine $n\times m$-Matrix sein. Dann wird jeder Spaltenvektor als eigener Graph (in der Docu `series` genannt) angesehen und es werden $m$ Kurven geplottet: ```{julia} using Plots x = range(0, 8π; length = 100) sx = @. sin(x) # the @. macro broadcasts (vectorizes) every operation cx = @. cos(2x^(1/2)) plot(x, [sx cx]) ``` - Die Funktionen des _Plots.jl_-Paketes wie `plot(), scatter(), contour(), heatmap(), histogram(), bar(),...` usw. starten alle einen neuen Plot. - Die Versionen `plot!(), scatter!(), contour!(), heatmap!(), histogram!(), bar!(),...` erweitern einen existierenden Plot: ```{julia} plot(x, sx) # plot only sin(x) plot!(x, cx) # add second graph plot!(x, sqrt.(x)) # add a thirth one ``` Plots sind Objekte, die zugewiesen werden können. Dann kann man sie später weiterverwenden, kopieren und insbesondere mit den `!`-Funktionen erweitern: ```{julia} plot1 = plot(x, [sx cx]) plot1a = deepcopy(plot1) # plot objects are quite deep structures scatter!(plot1, x, sx) # add scatter plot, i.e. unconnected data points ``` Die kopierte Version `plot1a` ist durch die `scatter!`-Anweisung nicht modifiziert worden und kann unabhängig weiterverwendet werden: ```{julia} plot!(plot1a, x, 2 .* sx) ``` Plot-Objekte kann man als Grafikdateien (PDF, SVG, PNG,...) abspeichern: ```{julia} savefig(plot1, "plot.png") ``` ```{julia} ;ls -l plot.png ``` Plot-Objekte können auch als Teilplot in andere Plots eingefügt werden, siehe Abschnitt @sec-subplot. ### Funktionsplots Man kann `plot()` auch eine Funktion und einen Vektor mit $x$-Werten übergeben: ```{julia} # https://mzrg.com/math/graphs.shtml f(x) = abs(sin(x^x)/2^((x^x-π/2)/π)) plot(f, 0:0.01:3) ``` Die parametrische Form $x = x(t),\ y = y(t)$ kann durch die Übergabe von zwei Funktionen und einen Vektor von $t$-Werten an `plot()` gezeichnet werden. ```{julia} # https://en.wikipedia.org/wiki/Butterfly_curve_(transcendental) xt(t) = sin(t) * (exp(cos(t))-2cos(4t)-sin(t/12)^5) yt(t) = cos(t) * (exp(cos(t))-2cos(4t)-sin(t/12)^5) plot(xt, yt, 0:0.01:12π) ``` ### Plot-Themen > "PlotThemes is a package to spice up the plots made with Plots.jl."\ Hier geht es zur illustrierten [Liste der Themen](https://docs.juliaplots.org/stable/generated/plotthemes/) oder: ```{julia} using PlotThemes # Liste der Themen keys(PlotThemes._themes) ``` ```{julia} Plots.showtheme(:juno) ``` ```{julia} using PlotThemes theme(:juno) # set a theme for all further plots plot(x, [sx cx 1 ./ (1 .+ x)]) ``` ### Plot-Attribute Die Funktionen des `Plots.jl`-Paketes haben eine große Anzahl von Optionen. `Plots.jl` teilt die Attribute in 4 Gruppen ein: ::::{.cell} ```{julia} #| output: asis plotattr(:Plot) # Attribute für den Gesamtplot ``` :::: ::::{.cell} ```{julia} #| output: asis plotattr(:Subplot) # Attribute für einen Teilplot ``` :::: ::::{.cell} ```{julia} #| output: asis plotattr(:Axis) # Attribute für eine Achse ``` :::: ::::{.cell} ```{julia} #| output: asis plotattr(:Series) # Attribute für eine Serie, also zB ein Linienzug im Plot ``` :::: Man kann auch nachfragen, was die einzelnen Attribute bedeuten und welche Werte zulässig sind: ```{julia} plotattr("linestyle") ``` Ein Beispiel: ```{julia} theme(:default) # zurück zum Standardthema x = 0:0.05:1 y = sin.(2π*x) plot(x, y, seriestype = :sticks, linewidth = 4, seriescolor = "#00b300", marker = :circle, markersize = 8, markercolor = :green, ) ``` Viele Angaben können auch sehr weit abgekürzt werden, siehe z.B. die Angabe `Aliases:` in der obigen Ausgabe des Kommandos `plotattr("linestyle")`. Das folgende `plot()`-Kommando is äquivalent zum vorherigen: ```{julia} #| eval: false plot(x, y, t = :sticks, w = 4, c = "#00b300", m = (:circle, 8, :green )) ``` ### Weitere Extras ```{julia} using Plots # Wiederholung schadet nicht using Plots.PlotMeasures # für Maßangaben in mm, cm,... using LaTeXStrings # für LaTeX-Konstrukte in Plot-Beschriftungen using PlotThemes # vorgefertigte Themen ``` Das Paket `LaTeXStrings.jl` stellt einen String-Konstruktor `L"..."` zur Verfügung. Diese Strings können LaTeX-Konstrukte, insbesondere Formeln, enthalten. Wenn der String keine expliziten Dollarzeichen enthält, wird er automatisch im LaTeX-Math-Modus interpretiert. ```{julia} xs = range(0, 2π, length = 100) data = [sin.(xs) cos.(xs) 2sin.(xs) (x->sin(x^2)).(xs)] # 4 Funktionen theme(:ggplot2) plot10 = plot(xs, data, fontfamily="Computer Modern", # LaTeX-String L"..." title = L"Winkelfunktionen $\sin(\alpha), \cos(\alpha), 2\sin(\alpha), \sin(\alpha^2)$", xlabel = L"Winkel $\alpha$", ylabel = "Funktionswert", # 1x4-Matrizen mit Farben, Marker,... für die 4 'Series' color=[:black :green RGB(0.3, 0.8, 0.2) :blue ], markers = [:rect :circle :utriangle :diamond], markersize = [2 1 0 4], linewidth = [1 3 1 2], linestyle = [:solid :dash :dot :solid ], # Achsen xlim = (0, 6.6), ylim = (-2, 2.3), yticks = -2:.4:2.3, # mit Schrittweite # Legende legend = :bottomleft, label = [ L"\sin(\alpha)" L"\cos(\alpha)" L"2\sin(\alpha)" L"\sin(\alpha^2)"], top_margin = 5mm, # hier wird Plots.PlotMeasures gebraucht ) # Zusatztext: annotate!(x-pos, y-pos, text("...", font, fontsize)) annotate!(plot10, 4.1, 1.8, text("nicht schön, aber viel","Computer Modern", 10) ) ``` ### Andere Plot-Funktionen Bisher haben wir vor allem Linien geplottet. Es gibt noch viele andere Typen wie _scatter plot, contour, heatmap, histogram, stick,..._ Dies kann man mit dem `seriestype`-Attribut steuern: ```{julia} theme(:default) x = range(0, 2π; length = 50) plot(x, sin.(x), seriestype=:scatter) ``` oder indem man die spezielle Funktion benutzt, die so heißt wie der `seriestype`: ```{julia} x = range(0, 2π; length = 50) scatter(x, sin.(x)) ``` ### Subplots und Layout {#sec-subplot} Mehrere Plots können zu einer Abbildung zusammengefasst werden. Die Anordnung bestimmt der `layout`-Parameter. Dabei bedeutet `layout=(m,n)`, dass die Plots in einem $m\times n$-Schema angeordnet werden: ```{julia} x = range(0, 2π; length = 100) plots = [] # vector of plot objects for f in [sin, cos, tan, sinc] p = plot(x, f.(x)) push!(plots, p) end plot(plots..., layout=(2,2), legend=false, title=["sin" "cos" "tan" "sinc"]) ``` ```{julia} plot(plots..., layout=(4,1), legend=false, title=["sin" "cos" "tan" "sinc"]) ``` Man kann Layouts auch schachteln und mit dem `@layout`-Macro explizite Breiten/Höhenanteile vorgeben: ```{julia} mylayout = @layout [ a{0.3w} [ b c{0.2h} ] d{0.2h} ] plot(plots..., layout=mylayout, legend=false, title=["sin" "cos" "tan" "sinc"]) ``` ### Backends `Plots.jl` ist konzipiert als ein einheitliches Interface zu verschiedenen _backends_ (Grafik-Engines). Man kann zu einem anderen Backend wechseln und dieselben Plot-Kommandos und -Attribute verwenden. Allerdings unterstützen nicht alle _backends_ alle Plot-Typen und -Attribute. Einen Überblick gibt es [hier](https://docs.juliaplots.org/stable/generated/supported/). Bisher wurde das Standard-Backend verwendet. Es heißt [GR](https://gr-framework.org/about.html) und ist eine am Forschungszentrum Jülich entwickelte und hauptsächlich in C geschriebene Grafik-Engine. ```{julia} using Plots backend() # Anzeige des gewählten backends, GR ist der default ``` Nochmal ein Beispiel ```{julia} x = 1:30 y = rand(30) plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Wasserstand") ``` und hier derselbe Plot mit dem `PlotlyJS`-Backend. ```{julia} plotlyjs() # change plots backend plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Wasserstand") ``` Dieses Backend ermöglich mit Hilfe von Javascript eine gewisse Interaktivität. Wenn man die Maus in das Bild bewegt, kann man mit der Maus zoomen, verschieben und 3D-Plots auch drehen. ```{julia} gr() # zurück zu GR als backend ``` ### 3D Plots Die Funktionen `surface()` und `contour()` ermöglichen den Plot einer Funktion $f(x,y)$. Als Argumente werden benötigt: - eine Menge (Vektor) $X$ von $x$-Werten, - eine Menge (Vektor) $Y$ von $y$-Werten und - eine Funktion von zwei Variablen, die dann auf $X \times Y$ ausgewertet und geplottet wird. ```{julia} f(x,y) = (1 - x/2 + x^5 + y^3) * exp(-x^2 - y^2) surface( -3:0.02:3, -3:0.02:3, f) ``` ```{julia} contour( -3:0.02:3, -3:0.02:3, f, fill=true, colormap=:summer, levels=20, contour_labels=false) ``` Kurven (oder auch einfach Punktmengen) in drei Dimensionen lassen sich plotten, indem man `plot()` mit 3 Vektoren aufruft, die jeweils die $x$, $y$ und $z$-Koordinaten der Datenpunkte enthalten. ```{julia} plotlyjs() t = range(0, stop=8π, length=100) # parameter t x = @. t * cos(t) # x(t), y(t), z(t) y = @. 0.1 * t * sin(t) z = @. 100 * t/8π plot(x, y, z, zcolor=reverse(z), markersize=3, markershape= :circle, linewidth=5, legend=false, colorbar=false) ``` > Wir verwenden mal das `plotlyjs`-Backend, damit ist der Plot interaktiv und kann mit der Maus gedreht und gezoomt werden. ### Plots.jl und _recipes_ Andere Pakete können die Möglichkeiten von `Plots.jl` erweitern, indem sie sogenannte _recipes_ für spezielle Plots und Datenstrukturen definieren, siehe [https://docs.juliaplots.org/latest/ecosystem/](https://docs.juliaplots.org/latest/ecosystem/), z.B.: - `StatsPlots.jl` direktes Plotten von _Dataframes_, spezielle statistische Plots usw. oder - `GraphRecipes.jl` [Plotten von Graphstrukturen](https://docs.juliaplots.org/latest/GraphRecipes/examples/) ### Ein Säulendiagramm Für das letzte Beispiel laden wir ein Paket, das über 700 freie (_"public domain"_) Datensätze, darunter z.B: - die Passagierliste der _Titanic_, - Verbrauchsdaten amerikanischer Autos aus den 70ern oder - historische Währungskurse bereitstellt: ```{julia} using RDatasets ``` ```{julia} #| error: false #| echo: false #| output: false #RDatasets.datasets() ``` Der Datensatz ["Motor Trend Car Road Tests"](https://rdrr.io/r/datasets/mtcars.html) ```{julia} cars = dataset("datasets", "mtcars") ``` Wir brauchen für den Plot nur die beiden Spalten `cars.Model` und `cars.MPG`, den Benzinverbrauch in _miles per gallon_ (Mehr heißt sparsamer!) ```{julia} theme(:bright) bar(cars.Model, cars.MPG, label = "Miles/Gallon", title = "Models and Miles/Gallon", xticks =:all, xrotation = 45, size = [600, 400], legend =:topleft, bottom_margin = 10mm ) ``` ### Was noch fehlt: Animation Hier sei auf die [Dokumentation](https://docs.juliaplots.org/latest/animations/) verwiesen und nur ein Beispiel (von ) angegeben: ```{julia} #| error: false #| warning: false using Plots, Random theme(:default) anim = @animate for i in 1:50 Random.seed!(123) scatter(cumsum(randn(i)), ms=i, lab="", alpha = 1 - i/50, xlim=(0,50), ylim=(-5, 7)) end gif(anim, fps=50) ``` :::: {.content-visible when-format="pdf"} ```{julia} #| echo: false Random.seed!(123) i = 33 scatter(cumsum(randn(i)), ms=i, lab="", alpha = 1 - i/50, xlim=(0,50), ylim=(-5, 7)) ``` ::::