Plots with Plots

    "1.0 pic ≊ 1e3 words"

Tom Breloff (@tbreloff)

breloff.com
github.com/tbreloff

I am a...

  • Math Geek
    • BA Math/Economics (U. of Rochester)
    • MS Math (NYU's Courant Institute)
  • Computer Geek
    • Since fourth grade... too many languages to list
  • Finance Geek
    • Spent a decade building and operating quantitative trading desks
  • AI Geek
    • Machine Learning, AGI, Neuroscience, Real-time Reinforcement Learning

... did I mention I'm a bit of a geek?

Plots is...

  • a user-facing API
  • a wrapper of several other graphics packages
  • a framework for describing visualizations
  • serious convenience
  • flexible and easy
  • a tool to visualize EVERYTHING

Plots is not...

  • a LSTM generator of movie scripts
  • a tool for supervillains
  • a simulated farming game

Words are boring! show me pictures!

Ok then... Lets visualize the last 16 years of my life...

If you want to follow along, then do:

In [ ]:
Pkg.add("Plots")
Pkg.add("PlotRecipes")
Pkg.add("PyPlot")

Pkg.checkout("Plots")
Pkg.checkout("PlotRecipes")
In [2]:
using Plots, PlotRecipes
pyplot()
Out[2]:
Plots.PyPlotBackend()
In [60]:
years = 2000:2016
activities = [
    ("Work",        [3,2,3,5,7,10,10,12,16,13,11,12,12,11,10,10,10]),
    ("Video Games", [8,7,6,4,1,.5,.5,.5,.5,.5,.5,.25,.25,.25,.25,.25,.25]),
    ("Sports",      [2,2,2,1,2,2,1,.5,.5,.5,.5,.5,1,1,.5,.5,.25]),
    ("Family",      [.1,.1,2,3,3,3,3,3,2,2,5,5,6,6,6,7,8]),
    ("Sleep",       zeros(17))
]
labels, hours = Plots.unzip(activities)
hours = hcat(hours...)
hours[:,5] = 24 - sum(hours,2)
pcts = hours ./ sum(hours,2)

portfoliocomposition(pcts, years, lab=labels', legend=:topleft, dir=:h)
plot!(xguide="Year", title="My adult life", bg=RGBA(1,1,1,0.5))
yaxis!("Hours per Day",(linspace(0,1,4),0:8:24))
Out[60]:
In [30]:
plot(
    heatmap(years, labels, pcts', leg=false, c=:blues),
    wireframe(labels, years, pcts, contours=true, leg=false, zticks=nothing),
    contour(years, labels, pcts', fill=true, c=:plasma),
    layout = @layout [a b{0.6w}; c{0.3h}]
)
Out[30]:

My path to Julia

In [31]:
languages = ["julia","c/c++","python","javascript","go","c#","java","matlab","other"]
pcts = [
    0 0 0 0 0 0 0.9 0 0.1
    0 0 0 0 0 0 0.9 0 0.1
    0 0 0 0 0 0 0.6 0 0.4
    0 0 0 0 0 0.2 0.5 0 0.3
    0 0.2 0 0 0 0.2 0.3 0 0.3
    0 0 1 0 0 0 0 0 0
    0 0.3 0.7 0 0 0 0 0 0
    0 0.6 0.4 0 0 0 0 0 0
    0 0.8 0.2 0 0 0 0 0 0
    0 0.7 0.3 0 0 0 0 0 0
    0 0.6 0.4 0 0 0 0 0 0
    0 0.6 0.4 0 0 0 0 0 0
    0 0.5 0.4 0.1 0 0 0 0 0
    0 0.15 0.15 0.15 0.15 0 0.15 0.15 0.1
    0.2 0 0.1 0 0 0 0.35 0.35 0
    0.9 0 0.1 0 0 0 0 0 0
    1 0 0 0 0 0 0 0 0
]
α = vcat(1, 0.3ones(8))';
anns = [
    (2002,   0.8, text("School",15,0.1pi)),
    (2007.5, 0.6, text("Banks", 15,0.1pi)),
    (2012.5, 0.4, text("Funds", 15,0.1pi)),
    (2015,   0.2, text("C Tech",15,0.1pi)),
];
In [32]:
portfoliocomposition(pcts, years, lab=languages', dir=:h, α = α)
plot!(xguide="Year", title="My programming time", bg=RGBA(1,1,1,0.7))
vline!([2004, 2011, 2014], line=(:black,0.5,6,:dash), anns=anns, primary=false)
Out[32]:

Why Julia?

  • It's easy to write fast code
  • Multiple dispatch is powerful and intuitive
  • Macros!
  • Code just looks clean

Why Plots?

"Powerful convenience"

A visualization library should:

  • Accept any input format
  • Provide smart defaults which adapt to your inputs
  • Let you dynamically change visuals at any granularity
  • Let you write both quick-one-liners and complex methods
  • Be modular and flexible
  • Give you tools to do EXACTLY what you want

... you know, just like Julia!

A visualization library should NOT:

  • Force you to dedicate a computer monitor to documentation
  • Impose a coding style on you
  • Make you think too much
  • Waste your precious time

So... why Plots?

I didn't think we had a visualization library that fulfilled those requirements...

... so I started building.

Plots Crash Course

  • Basic commands
  • Backends
  • Output
  • Attributes
  • Series Types
  • Layouts
  • Recipes

Basic commands

In [33]:
pyplot()
scatter(rand(100,4), palette = :blues, shape=:auto, markersize=(5:5:20)', alpha=0.2)
Out[33]:

Basic commands

In [34]:
histogram(randn(100,4), layout=4, legend=false)
Out[34]:

Backends

In [51]:
unicodeplots()
plot(cumsum(randn(1000)))
Out[51]:
       +------------------------------------------------------------+   
    16 |                                                           d| y1
       |                                                          ,"|   
       |...,.                                                 .,  | |   
       |]@Fl|                                                 |\ |P |   
       ||'`'l                                             .  ||]aW  |   
       |/   |.  .                  l                    d.],,/`|"/  |   
       |L____@__J__________________@____________________1O1@L1_____.|   
       |     |1."L.             W@]N                    | \ ||      |   
       |      \W F|            ,/ "|.                  |`   |`      |   
       |       /  l.           @    |                 .]            |   
       |       '  ||          .`    .                 //            |   
       |           |          /     l                .[             |   
       |           |     .    |     |       ..  ,    /|             |   
       |           l\.   @\   |     || a[|.uW@  Mda @/              |   
       |           ]P| .aPO  .`      \]|\ldl'O@@`""U`               |   
       |           " l,WT ", /       |/ "'[' "T`   \                |   
       |             |U`   \ |       ||   `   '                     |   
       |              '    |,`       "`                             |   
       |                   \/                                       |   
   -32 |                   |\                                       |   
       +------------------------------------------------------------+   
       0                                                         1000

Output

  • REPL:
    • display called when returned to the REPL
  • IJulia:
    • Inline plot when returned to a cell
  • To files (all equivalent):
    • png("tmp")
    • png("tmp.png")
    • png(current(), "tmp")
    • savefig("tmp.png")
    • savefig(current(), "tmp.png")
    • writemime(open("tmp.png","w"), MIME("image/png"), current())

Attributes

  • Aliases
  • "Magic Args"
  • Slicing and Dicing
  • Cycling

Aliases

Convenient shorthands and alternative names

In [53]:
pyplot()
y = rand(10)

# equivalent:

plot(y, c = :red,           ms = 10,         shape = :^,               yscale = :log)
plot(y, seriescolor = :red, markersize = 10, markershape = :utriangle, yscale = :log10)
Out[53]:

"Magic Args"

Set related attributes all at once and let Plots guess what you want

  • values can be in any order
  • single values will be treated like a length-1 Tuple
In [54]:
# equivalent:

plot(y, linewidth = 3, linealpha = 0.5, linecolor = :blue, linestyle = :dot,
        markersize = 10, markeralpha = 0.8, markercolor = :red,
        markerstrokewidth = 10, markerstrokecolor = :black, markerstrokealpha = 0.2,
        xlims = (0,15), xticks = 2:3:10)

plot(y, line = (3,0.5,:blue,:dot), marker = (10,0.8,:red,stroke(10,:black,.2)), xaxis = ((0,15),2:3:10))
Out[54]:

Slicing and Dicing

!!! This is very important... don't forget it !!!

In Plots, inputs are sliced by columns (with a few exceptions, like 3D surfaces).

A common mistake is to pass a N-length vector when you intend to "map" values. Use a (1 x N) matrix instead.

In [57]:
# Right
p1 = plot(rand(10,4), label = ["A" "B" "C" "D"])

# WRONG
p2 = plot(rand(10, 4), label = ["A","B","C","D"])

plot(p1, p2)
Out[57]:

Cycling

If there's not enough values, they are cycled, or repeated.

In [39]:
plot(rand(10,4), marker = (8, [:red :blue]))
Out[39]:

Series Types -- 2D

In [40]:
stypes = [:path, :step, :sticks, :scatter, :bar, :hline, :vline, :histogram, :density]
plot(randn(20,1), st = stypes, layout = length(stypes), w = 2, m = 4)
plot!(ticks=nothing, leg=false, title=stypes', size=(700,500))
Out[40]: