16  Plots and Data Visualization in Julia: Plots.jl

Julia has numerous graphics packages. Two frequently used ones are Makie.jl and Plots.jl. Before presenting Plots.jl in detail, we list some others.

16.1 Brief Overview: Some Graphics Packages

Package/Documentation Tutorial Examples Remarks
Plots.jl Tutorial Gallery designed as a unified interface to various backends (graphics libraries)
Makie.jl Basic tutorial Beautiful Makie “data visualization ecosystem for Julia”, backends: Cairo (vector graphics), OpenGL, WebGL
PlotlyJS.jl Getting started Examples Interface to the Plotly JavaScript graphics library
Gadfly.jl Tutorial Gallery “a plotting and data visualization system written in Julia, influenced by R’s ggplot2
Bokeh.jl Gallery Julia frontend for Bokeh
VegaLite.jl Tutorial Examples Julia frontend for Vega-Lite
Luxor.jl Tutorial Examples General vector graphics/illustrations
Javis.jl Tutorials Examples Animated vector graphics
TidierPlots.jl Reference “is a 100% Julia implementation of the R package ggplot2 powered by Makie.jl”
PythonPlot.jl Examples (in Python) Interface to Matplotlib (Python), 1:1 transfer of the Python API, therefore see Matplotlib documentation

16.2 Plots.jl

16.2.1 Simple Plots

The plot() function expects, in the simplest case:

  • as the first argument a vector of \(x\)-values of length \(n\) and
  • as the second argument a vector of the same length with the corresponding \(y\)-values.
  • The second argument can also be an \(n\times m\) matrix. Each column is treated as a separate graph (called a series in the documentation), plotting \(m\) curves:
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])
  • Functions like plot(), scatter(), contour(), heatmap(), histogram(), bar(), etc. from Plots.jl all start a new plot.
  • The versions plot!(), scatter!(), contour!(), heatmap!(), histogram!(), bar!(), etc. extend an existing plot:
plot(x, sx)          # plot only sin(x)
plot!(x, cx)         # add second graph
plot!(x, sqrt.(x))   # add a third one

Plots are objects that can be assigned. Then they can be used later, copied, and in particular extended with the ! functions:

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

The copied version plot1a remains unchanged by the scatter! call and can be used independently:

plot!(plot1a, x, 2 .* sx)

Plot objects can be saved as graphics files (PDF, SVG, PNG, etc.):

savefig(plot1, "plot.png")
"/home/hellmund/Julia/Book26/JuliaBook/chapters/plot.png"
;ls -l plot.png
-rw-r--r-- 1 hellmund hellmund 55780 Mar 11 15:51 plot.png

Plot objects can also be inserted as subplots into other plots, see section Section 16.2.7.

16.2.2 Function Plots

plot() can also be passed a function and a vector of \(x\)-values:

# https://mzrg.com/math/graphs.shtml

f(x) = abs(sin(x^x)/2^((x^x-π/2)/π))

plot(f, 0:0.01:3)

The parametric form \(x = x(t),\ y = y(t)\) is plotted by passing two functions and a vector of \(t\)-values to plot().

# 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π)

16.2.3 Plot Themes

“PlotThemes is a package to spice up plots made with Plots.jl.”
See the illustrated list of themes

using PlotThemes

# list of themes
keys(PlotThemes._themes)
KeySet for a Dict{Symbol, PlotTheme} with 21 entries. Keys:
  :juno
  :default
  :dao
  :ggplot2
  :gruvbox_dark
  :dark
  :gruvbox_light
  :solarized
  :wong
  :dracula
  :solarized_light
  :mute
  :boxed
  :rose_pine
  :wong2
  :sand
  :lime
  :rose_pine_dawn
  :bright
  ⋮
Plots.showtheme(:juno)
using PlotThemes

theme(:juno)   # set a theme for all further plots

plot(x, [sx  cx  1 ./ (1 .+ x)])

16.2.4 Plot Attributes

The Plots.jl functions have numerous options. Attributes are divided into 4 groups:

plotattr(:Plot)  # attributes for the overall plot

Defined Plot attributes are: background_color, background_color_outside, display_type, dpi, extra_kwargs, extra_plot_kwargs, fontfamily, foreground_color, html_output_format, inset_subplots, layout, link, overwrite_figure, plot_title, plot_titlefontcolor, plot_titlefontfamily, plot_titlefonthalign, plot_titlefontrotation, plot_titlefontsize, plot_titlefontvalign, plot_titleindex, plot_titlelocation, plot_titlevspan, pos, show, size, tex_output_standalone, thickness_scaling, warn_on_unsupported, window_title

plotattr(:Subplot) # attributes for a subplot

Defined Subplot attributes are: annotationcolor, annotationfontfamily, annotationfontsize, annotationhalign, annotationrotation, annotations, annotationvalign, aspect_ratio, background_color_inside, background_color_subplot, bottom_margin, camera, clims, color_palette, colorbar, colorbar_continuous_values, colorbar_discrete_values, colorbar_fontfamily, colorbar_formatter, colorbar_scale, colorbar_tickfontcolor, colorbar_tickfontfamily, colorbar_tickfonthalign, colorbar_tickfontrotation, colorbar_tickfontsize, colorbar_tickfontvalign, colorbar_ticks, colorbar_title, colorbar_title_location, colorbar_titlefontcolor, colorbar_titlefontfamily, colorbar_titlefonthalign, colorbar_titlefontrotation, colorbar_titlefontsize, colorbar_titlefontvalign, extra_kwargs, fontfamily_subplot, foreground_color_subplot, foreground_color_title, framestyle, left_margin, legend_background_color, legend_column, legend_font, legend_font_color, legend_font_family, legend_font_halign, legend_font_pointsize, legend_font_rotation, legend_font_valign, legend_foreground_color, legend_position, legend_title, legend_title_font, legend_title_font_color, legend_title_font_family, legend_title_font_halign, legend_title_font_pointsize, legend_title_font_rotation, legend_title_font_valign, margin, projection, projection_type, right_margin, subplot_index, title, titlefontcolor, titlefontfamily, titlefonthalign, titlefontrotation, titlefontsize, titlefontvalign, titlelocation, top_margin

plotattr(:Axis)  # attributes for an axis

Defined Axis attributes are: discrete_values, draw_arrow, flip, foreground_color_axis, foreground_color_border, foreground_color_grid, foreground_color_guide, foreground_color_minor_grid, foreground_color_text, formatter, grid, gridalpha, gridlinewidth, gridstyle, guide, guide_position, guidefontcolor, guidefontfamily, guidefonthalign, guidefontrotation, guidefontsize, guidefontvalign, lims, link, minorgrid, minorgridalpha, minorgridlinewidth, minorgridstyle, minorticks, mirror, rotation, scale, showaxis, tick_direction, tickfontcolor, tickfontfamily, tickfonthalign, tickfontrotation, tickfontsize, tickfontvalign, ticks, unit, unitformat, widen

plotattr(:Series) # attributes for a series, e.g., a line in the plot

Defined Series attributes are: arrow, bar_edges, bar_position, bar_width, bins, colorbar_entry, connections, contour_labels, contours, extra_kwargs, fill_z, fillalpha, fillcolor, fillrange, fillstyle, group, hover, label, levels, line_z, linealpha, linecolor, linestyle, linewidth, marker_z, markeralpha, markercolor, markershape, markersize, markerstrokealpha, markerstrokecolor, markerstrokestyle, markerstrokewidth, normalize, orientation, permute, primary, quiver, ribbon, series_annotations, seriesalpha, seriescolor, seriestype, show_empty_bins, smooth, stride, subplot, weights, x, xerror, y, yerror, z, z_order, zerror

You can also query what individual attributes mean and which values are allowed:

plotattr("linestyle")
:linestyle

Style of the line (for path and bar stroke). Choose from [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]

Aliases: (:linestyles, :ls, :s, :style).

Type: Symbol.

`Series` attribute, defaults to `solid`.

An example:

theme(:default)  # return to default theme 

x = 0:0.05:1
y = sin.(2π*x)

plot(x, y, seriestype = :sticks, linewidth = 4, seriescolor = "#00b300",
        marker = :circle, markersize = 8, markercolor = :green,
)

Many specifications can be abbreviated significantly; see, e.g., the Aliases: in the output of plotattr("linestyle").

The following plot() command is equivalent to the previous one:

plot(x, y, t = :sticks, w = 4, c = "#00b300", m = (:circle, 8, :green ))

16.2.5 Additional Extras

using Plots                # no harm in repeating
using Plots.PlotMeasures   # for measurements in mm, cm,...
using LaTeXStrings         # for LaTeX constructs in plot labels
using PlotThemes           # predefined themes

The LaTeXStrings.jl package provides the L"..." string constructor. These strings can contain LaTeX constructs, especially formulas. Without explicit dollar signs, they are automatically interpreted in LaTeX math mode.

xs = range(0, 2π, length = 100)

data = [sin.(xs) cos.(xs) 2sin.(xs) (x->sin(x^2)).(xs)]   # 4 functions

theme(:ggplot2)

plot10 = plot(xs, data,
           fontfamily="Computer Modern",
    
           # LaTeX string L"..."   
           title = L"Trigonometric functions $\sin(\alpha), \cos(\alpha), 2\sin(\alpha), \sin(\alpha^2)$",
           xlabel = L"angle $\alpha$",
           ylabel = "value", 
    
           # 1x4-matrices with colors, markers,...  for the 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 ],
           
           # axes
           xlim = (0, 6.6),
           ylim = (-2, 2.3),
           yticks = -2:.4:2.3,       # with step size
           
           # legend 
           legend = :bottomleft,
           label = [ L"\sin(\alpha)" L"\cos(\alpha)" L"2\sin(\alpha)"  L"\sin(\alpha^2)"],
    
           top_margin = 5mm,     # here Plots.PlotMeasures is needed
)

# additional text: annotate!(x-pos, y-pos, text("...", font, fontsize))

annotate!(plot10, 4.1, 1.8, text("Some remark","Computer Modern", 10) )

16.2.6 Other Plot Functions

So far, we have mainly plotted lines. Many other types exist, such as scatter plots, contours, heatmaps, histograms, sticks, etc.

This can be controlled with the seriestype attribute:

theme(:default)

x = range(0, 2π; length = 50)
plot(x, sin.(x), seriestype=:scatter)

or by using the specific function named after the seriestype:

x = range(0, 2π; length = 50)
scatter(x, sin.(x))

16.2.7 Subplots and Layout

Multiple plots can be combined into one figure. The arrangement is determined by the layout parameter: layout=(m,n) arranges plots in an \(m\times n\) grid:

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"])
plot(plots..., layout=(4,1), legend=false, title=["sin" "cos" "tan" "sinc"])

Layouts can also be nested and explicit width/height proportions can be specified using the @layout macro:

mylayout = @layout [
               a{0.3w} [ b
                         c{0.2h}  ]  
               d{0.2h}
            ]

plot(plots..., layout=mylayout, legend=false, title=["sin" "cos" "tan" "sinc"])

16.2.8 Backends

Plots.jl provides a unified interface to various backends (graphics engines). You can switch backends and use the same plot commands and attributes.

However, not all backends support all plot types and attributes. An overview is available here.

So far, we have used the default backend: GR, a graphics engine developed at the Jülich Research Center, primarily in C.

using Plots
backend()      # display the selected backend, GR is the default
Plots.GRBackend()

Another example:

x = 1:30
y = rand(30)
plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Water level")

The same plot with the PlotlyJS backend:

plotlyjs()       # change plots backend
plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Water level")

This backend enables interactivity via JavaScript. Hovering over the image allows zooming and panning; 3D plots can also be rotated.

gr() # return to GR as backend
Plots.GRBackend()

16.2.9 3D Plots

The surface() and contour() functions plot a function \(f(x,y)\). Required arguments are:

  • a set (vector) \(X\) of \(x\)-values,
  • a set (vector) \(Y\) of \(y\)-values and
  • a function of two variables that is then evaluated on \(X \times Y\) and plotted.
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)
contour( -3:0.02:3, -3:0.02:3,  f, fill=true, colormap=:summer, levels=20, contour_labels=false)

Curves (or point sets) in three dimensions are plotted by calling plot() with three vectors containing the \(x\), \(y\), and \(z\) coordinates of the data points.

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)

The plotlyjs backend makes the plot interactive: it can be rotated and zoomed with the mouse.

16.2.10 Plots.jl and recipes

Other packages can extend Plots.jl by defining so-called recipes for special plots and data structures (see https://docs.juliaplots.org/latest/ecosystem/), e.g.:

16.2.11 A Bar Chart

For the last example, we load a package providing over 700 free (“public domain”) datasets, including:

  • the Titanic passenger list,
  • fuel consumption data for American cars from the 70s, or
  • historical exchange rates:
using RDatasets

The dataset “Motor Trend Car Road Tests”

cars = dataset("datasets", "mtcars")
32×12 DataFrame
7 rows omitted
Row Model MPG Cyl Disp HP DRat WT QSec VS AM Gear Carb
String31 Float64 Int64 Float64 Int64 Float64 Float64 Float64 Int64 Int64 Int64 Int64
1 Mazda RX4 21.0 6 160.0 110 3.9 2.62 16.46 0 1 4 4
2 Mazda RX4 Wag 21.0 6 160.0 110 3.9 2.875 17.02 0 1 4 4
3 Datsun 710 22.8 4 108.0 93 3.85 2.32 18.61 1 1 4 1
4 Hornet 4 Drive 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1
5 Hornet Sportabout 18.7 8 360.0 175 3.15 3.44 17.02 0 0 3 2
6 Valiant 18.1 6 225.0 105 2.76 3.46 20.22 1 0 3 1
7 Duster 360 14.3 8 360.0 245 3.21 3.57 15.84 0 0 3 4
8 Merc 240D 24.4 4 146.7 62 3.69 3.19 20.0 1 0 4 2
9 Merc 230 22.8 4 140.8 95 3.92 3.15 22.9 1 0 4 2
10 Merc 280 19.2 6 167.6 123 3.92 3.44 18.3 1 0 4 4
11 Merc 280C 17.8 6 167.6 123 3.92 3.44 18.9 1 0 4 4
12 Merc 450SE 16.4 8 275.8 180 3.07 4.07 17.4 0 0 3 3
13 Merc 450SL 17.3 8 275.8 180 3.07 3.73 17.6 0 0 3 3
21 Toyota Corona 21.5 4 120.1 97 3.7 2.465 20.01 1 0 3 1
22 Dodge Challenger 15.5 8 318.0 150 2.76 3.52 16.87 0 0 3 2
23 AMC Javelin 15.2 8 304.0 150 3.15 3.435 17.3 0 0 3 2
24 Camaro Z28 13.3 8 350.0 245 3.73 3.84 15.41 0 0 3 4
25 Pontiac Firebird 19.2 8 400.0 175 3.08 3.845 17.05 0 0 3 2
26 Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.9 1 1 4 1
27 Porsche 914-2 26.0 4 120.3 91 4.43 2.14 16.7 0 1 5 2
28 Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
29 Ford Pantera L 15.8 8 351.0 264 4.22 3.17 14.5 0 1 5 4
30 Ferrari Dino 19.7 6 145.0 175 3.62 2.77 15.5 0 1 5 6
31 Maserati Bora 15.0 8 301.0 335 3.54 3.57 14.6 0 1 5 8
32 Volvo 142E 21.4 4 121.0 109 4.11 2.78 18.6 1 1 4 2

We only need two columns for the plot: cars.Model and cars.MPG, the fuel consumption in miles per gallon (higher is more economical).

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
   )

16.2.12 What is Missing: Animation

See the documentation; here is an example (from https://www.juliafordatascience.com/animations-with-plots-jl/):

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)