Plotting in Julia

Overview

Since it’s inception, plotting in Julia has been a mix of happiness and frustration

Some initially promising libraries have stagnated, or failed to keep up with user needs

New packages have appeared to compete with them, but not all are fully featured

The good news is that the Julia community now has several very good options for plotting

In this lecture we’ll try to save you some of our pain by focusing on what we believe are currently the best libraries

First we look at two high quality plotting packages that have proved useful to us in a range of applications

After that we turn to a relative newcomer called Plots.jl

The latter package takes a different – and intriguing – approach that combines and exploits the strengths of several existing plotting libraries

Below we assume that

How to Read this Lecture

If you want to get started quickly with relatively simple plots, you can skip straight to the section on Plots.jl

If you want a deeper understanding and more flexibility, continue from the next section and read on

Credits: Thanks to @albep, @vgregory757 and @spencerlyon2 for help with the code examples below

PyPlot

Let’s look at PyPlot first

PyPlot is a Julia front end to the excellent Python plotting library Matplotlib

Installing PyPlot

One disadvantage of PyPlot is that it not only requires Python but also much of the scientific Python stack

Fortunately, installation of the latter has been greatly simplified by the excellent Anaconda Python distribution

Moreover, the tools that come with Anaconda (such as Jupyter) are too good to miss out on

So please go ahead and install Anaconda if you haven’t yet

Next, start up Julia and type Pkg.add("PyPlot")

Usage

There are two different interfaces to Matplotlib and hence to PyPlot

Let’s look at them in turn

The Procedural API

Matplotlib has a straightforward plotting API that essentially replicates the plotting routines in MATLAB

These plotting routines can be expressed in Julia with almost identical syntax

Here’s an example

using PyPlot
x = linspace(0, 10, 200)
y = sin(x)
plot(x, y, "b-", linewidth=2)

The resulting figure looks as follows

../_images/pyplot_eg1.png

The Object Oriented API

Matplotlib also has a more powerful and expressive object oriented API

Because Julia isn’t object oriented in the same sense as Python, the syntax required to access this interface via PyPlot is a little awkward

Here’s an example:

using PyPlot
x = linspace(0, 10, 200)
y = sin(x)
fig, ax = subplots()
ax[:plot](x, y, "b-", linewidth=2)

The resulting figure is the same

Here we get no particular benefit from switching APIs, while introducing a less attractive syntax

However, as plots get more complex, the more explicit syntax will give us greater control

Here’s a similar plot with a bit more customization

using PyPlot
x = linspace(0, 10, 200)
y = sin(x)
fig, ax = subplots()
ax[:plot](x, y, "r-", linewidth=2, label="sine function", alpha=0.6)
ax[:legend](loc="upper center")

The resulting figure has a legend at the top center

../_images/pyplot_eg2.png

We can render the legend in LaTeX by changing the ax[:plot] line to

ax[:plot](x, y, "r-", linewidth=2, label=L"$y = \sin(x)$", alpha=0.6)

Note the L in front of the string to indicate LaTeX mark up

The result looks as follows

../_images/pyplot_eg3.png

Multiple Plots on One Axis

Here’s another example, which helps illustrate how to put multiple plots on one figure

We use Distributions.jl to get the values of the densities given a randomly generated mean and standard deviation

using PyPlot
using Distributions

u = Uniform()

fig, ax = subplots()
x = linspace(-4, 4, 150)
for i in 1:3
    # == Compute normal pdf from randomly generated mean and std == #
    m, s = rand(u) * 2 - 1, rand(u) + 1
    d = Normal(m, s)
    y = pdf(d, x)
    # == Plot current pdf == #
    ax[:plot](x, y, linewidth=2, alpha=0.6, label="draw $i")
end
ax[:legend]()

It generates the following plot

../_images/pyplot_eg4.png

Subplots

A figure containing n rows and m columns of subplots can be created by the call

fig, axes = subplots(num_rows, num_cols)

Here’s an example that generates 6 normal distributions, takes 100 draws from each, and plots each of the resulting histograms

using PyPlot
using Distributions

u = Uniform()
num_rows, num_cols = 2, 3
fig, axes = subplots(num_rows, num_cols, figsize=(12, 8))
subplot_num = 0

for i in 1:num_rows
    for j in 1:num_cols
        ax = axes[i, j]
        subplot_num += 1
        # == Generate a normal sample with random mean and std == #
        m, s = rand(u) * 2 - 1, rand(u) + 1
        d = Normal(m, s)
        x = rand(d, 100)
        # == Histogram the sample == #
        ax[:hist](x, alpha=0.6, bins=20)
        ax[:set_title]("histogram $subplot_num")
        ax[:set_xticks]([-4, 0, 4])
        ax[:set_yticks]([])
    end
end

The resulting figure is as follows

../_images/pyplot_eg5.png

3D Plots

Here’s an example of how to create a 3D plot

using PyPlot
using Distributions
using QuantEcon: meshgrid

n = 50
x = linspace(-3, 3, n)
y = x

z = Array(Float64, n, n)
f(x, y) = cos(x^2 + y^2) / (1 + x^2 + y^2)
for i in 1:n
    for j in 1:n
        z[j, i] = f(x[i], y[j])
    end
end

fig = figure(figsize=(8,6))
ax = fig[:gca](projection="3d")
ax[:set_zlim](-0.5, 1.0)
xgrid, ygrid = meshgrid(x, y)
ax[:plot_surface](xgrid, ygrid, z, rstride=2, cstride=2, cmap=ColorMap("jet"), alpha=0.7, linewidth=0.25)

It creates this figure

../_images/pyplot_eg6.png

PlotlyJS

Now let’s turn to another plotting package — a promising new library called PlotlyJS, authored by Spencer Lyon

PlotlyJS is a Julia interface to the plotly.js visualization library

It can be installed by typing Pkg.add("PlotlyJS") from within Julia

It has several advantages, one of which is beautiful interactive plots

While we won’t treat the interface in great detail, we will frequently use PlotlyJS as a backend for Plots.jl

(More on this below)

Examples

Let’s look at some simple examples

Here’s a version of the sine function plot you saw above

using PlotlyJS
x = linspace(0, 10, 200)
y = sin(x)
plot(scatter(x=x, y=y, marker_color="blue", line_width=2))

Here’s the resulting figure:

../_images/plotlyjs_eg1.png

Here’s a replication of the figure with multiple Gaussian densities

using PlotlyJS
using Distributions

traces = GenericTrace[]
u = Uniform()

x = linspace(-4, 4, 150)
for i in 1:3
    # == Compute normal pdf from randomly generated mean and std == #
    m, s = rand(u) * 2 - 1, rand(u) + 1
    d = Normal(m, s)
    y = pdf(d, x)
    trace = scatter(x=x, y=y, name="draw $i")
    push!(traces, trace)
end

plot(traces, Layout())

The output looks like this (modulo randomness):

../_images/plotlyjs_eg2.png

Plots.jl

Plots.jl is another relative newcomer to the Julia plotting scene, authored by Tom Breloff

The approach of Plots.jl is to

  1. provide a “frontend” plotting language
  2. render the plots by using one of several existing plotting libraries as “backends”

In other words, Plots.jl plotting commands are translated internally to commands understood by a selected plotting library

Underlying libraries, or backends, can be swapped very easily

This is neat because each backend has a different look, as well as different capabilities

Also, Julia being Julia, it’s quite possible that a given backend won’t install or function on your machine at a given point in time

With Plots.jl, you can just change to another one

Simple Examples

We produced some simple plots using Plots.jl back in our introductory Julia lecture

Here’s another simple one:

using Plots
x = linspace(0, 10, 200)
y = sin.(x)
plot(x, y, color=:blue, linewidth=2, label="sine")

On our machine this produces the following figure

../_images/plotsjl_sine.png

No backend was specified in the preceding code, and in this case it defaulted to PlotlyJS.jl

We can make this explicit by adding one extra line

using Plots
plotlyjs()   # specify backend
x = linspace(0, 10, 200)
y = sin.(x)
plot(x, y, color=:blue, linewidth=2, label="sine")

To switch your backend to PyPlot, change plotlyjs() to pyplot()

Your figure should now look more like the plots produced by PyPlot

Here’s a slightly more complex plot using Plots.jl with PyPlot backend

using Plots
using LaTeXStrings        # Install this package
pyplot()
x = linspace(0, 10, 100)
plot(x,
    sin,
    color=:red,
    lw=2,
    yticks=-1:1:1,
    title="sine function",
    label=L"$y = \sin(x)$",  # L for LaTeX string
    alpha=0.6)

Here’s the figure it produces:

../_images/plots_sine_complex.png

Use legend=:none if you want no legend on the plot

Notice that in the preceding code example, the second argument to plot() is a function rather than an array of data points

This is valid syntax, as is

plot(sin, 0, 10)  # Plot the sine function from 0 to 10

Plots.jl accommodates these useful variations in syntax by exploiting multiple dispatch

Multiple Plots on One Axis

Next, let’s replicate the figure with multiple Gaussian densities

using Distributions
using Plots
plotlyjs()

x = linspace(-4, 4, 150)
y_vals = Array(Vector, 3)
labels = Array(String, 1, 3)
for i = 1:3
    m, s = 2*(rand() - 0.5), rand() + 1
    d = Normal(m, s)
    y_vals[i] = pdf(d, x)
    labels[i] = string("mu = ", round(m, 2))
end

plot(x, y_vals, linewidth=2, alpha=0.6, label=labels)

Also, when you have multiple y-series, Plots.jl can accept one x-values vector and apply it to each y-series

Here’s the resulting figure:

../_images/plotsjl_normals.png

Subplots

Let’s replicate the subplots figure shown above

using Distributions
using Plots
using LaTeXStrings
pyplot()

draws = Array(Vector, 6)
titles = Array(String, 1, 6)
for i = 1:6
    m, s = 2*(rand() - 0.5), rand() + 1
    d = Normal(m, s)
    draws[i] = rand(d, 100)
    t = string(L"$\mu = $", round(m, 2), L", $\sigma = $", round(s, 2))
    titles[i] = t
end

histogram(draws,
            layout=6,
            title=titles,
            legend=:none,
            titlefont=font(9),
            bins=20)

Notice that the font and bins settings get applied to each subplot

Here’s the resulting figure:

../_images/plotsjl_histograms.png

When you want to pass individual arguments to subplots, you can use a row vector of arguments

  • For example, in the preceding code, titles' is a 1 x 6 row vector

Here’s another example of this, with a row vector of different colors for the histograms

using Distributions
using Plots
using LaTeXStrings
pyplot()

draws = Array(Vector, 6)
titles = Array(String, 1, 6)
for i = 1:6
    m, s = 2*(rand() - 0.5), rand() + 1
    d = Normal(m, s)
    draws[i] = rand(d, 100)
    t = string(L"$\mu = $", round(m, 2), L", $\sigma = $", round(s, 2))
    titles[i] = t
end

histogram(draws,
    layout=6,
    title=titles,
    legend=:none,
    titlefont=font(9),
    color=[:red :blue :yellow :green :black :purple],
    bins=20)

The result is a bit garish but hopefully the message is clear

../_images/plotsjl_histograms2.png

3D Plots

Here’s a sample 3D plot

using Plots
plotlyjs()

n = 50
x = linspace(-3, 3, n)
y = x

z = Array(Float64, n, n)
f(x, y) = cos(x^2 + y^2) / (1 + x^2 + y^2)
for i in 1:n
    for j in 1:n
        z[j, i] = f(x[i], y[j])
    end
end

surface(x, y, z)

The resulting figure looks like this:

../_images/plotsjl_3d.png

Further Reading

Hopefully this tutorial has given you some ideas on how to get started with Plots.jl

We’ll see more examples of this package in action through the lectures

Additional information can be found in the official documentation

Exercises

Exercise 1

The identity function \(f(x) = x\) is approximated on the nonnegative numbers \([0, \infty)\) with increasing degrees of precision by the sequence of functions

\[\begin{split}d_n(x) = \sum_{k=1}^{n 2^n} \frac{k-1}{2^n} \mathbb{1} \left\{ \frac{k-1}{2^n} \leq x < \frac{k}{2^n} \right\} + n \mathbb{1}\{ x \geq n \}\end{split}\]

for \(n = 1, 2, \ldots\)

Here \(\mathbb{1}\{ P \} = 1\) if the statement \(P\) is true and 0 otherwise

(This result is often used in measure theory)

Plot the functions \(d_n, \; n=1, \ldots, 6\) on the interval \([0, 10]\) and compare them to the identity function

Do they get closer to the identity as \(n\) gets larger?