Plots

Author

Darren Irwin

Plotting is a huge topic. Here I’ll do my best to give you an efficient introduction to key concepts and commands, equipping you with the tools to build plots of arbitrary complexity. My emphasis here is on ‘low-level’ commands (e.g. draw a point, draw a line), rather than ‘high-level’ commands (e.g. make a polished fancy plot in a single command). I emphasize the former because it will give you the confidence and power to develop plots to do whatever you want, and it is easy to learn the high-level commands later.

There are a number of different plotting packages in Julia, many of them excellent in different ways. Here we will use the ‘Plots’ package because it is perhaps simplest to work with and is most integrated (or composable) with other packages in the Julia ecosystem. (After some experience with this package, a lot of your knowledge will be transferable to other packages.)

Add the Plots package

If you haven’t already done so (perhaps you did this several pages prior), install the Plots package as follows:

If using the REPL, type ] to activate the Package mode, then enter add Plots . Then press the delete key to return to the REPL, and enter using Plots.

If using Pluto, simply type add Plots in a code cell and then shift-enter to execute the cell.

These steps might take quite a bit of time because the package and its dependencies are big.

Make a simple plot

We’ll make a simple plot and learn some of the functions involved.

Draw some lines

Let’s define some x values and y values of points, and then plot them by connecting those points with lines:

using Plots
xValues = [1.5, 1.8, 2.3, 2.7, 3.2, 3.5]
yValues = [2.8, 2.5, 2.3, 2.3, 2.5, 2.8]
plot(xValues, yValues, linewidth=3)

So we drew some lines between the points defined those coordinates.

Draw some points

Now, let’s say we want to add some points to the same plot. We can use scatter!() :

scatter!([2.1, 2.9], [3.5, 3.5])

The ! in the scatter!() function is used to indicate that this function will change the existing plot (rather than make a new plot).

Draw a shape

Now, let’s add a filled shape to the same plot:

plot!(Shape([2.4, 2.5, 2.6], [2.9, 3.1, 2.9]))

Here, we created a Shape object and fed that as an argument to the plot!() function.

As the plot was updated, you can see that the Plots package adjusted the limits of the x and y axes to fit the plotted points. We can control that ourselves:

plot!(xlim = [1, 4], ylim = [2, 4])

You can change just about anything in the plot

We can adjust all sorts of other things about the plot. For instance, add axis labels and a title, and remove the legend:

p1 = plot!(title = "A happy plot",
    xlabel = "My x axis",
    ylabel = "My y axis",
    legend = false)

To find out more about attributes of plots and how to change them, type ??plot in the Julia REPL (the first ? will activate the help mode, and then enter ?plot).

In the last code block above, we both generate a plot and assign it to the name p1, which gives us a way to invoke it later (you’ll see why further below).

Show mathematical functions

Above, we fed the plot function a set of x and y values. We can also feed it a function, and it will do its best to provide a reasonable plot of the function:

plot(sin)

We can add some more functions to the plot:

plot!(x -> 3cos(x))
p2 = plot!(x -> (x^2 - 5))

In the above, the arguments given to plot!() are anonymous functions. This means they are not given names and are not stored in memory. Rather, the syntax x -> f(x) simply provides a mapping of x onto y values, and the plot!() function interprets that and shows us a part of the relationship.

Add text to the plot

We can use the annotate!() function to add text to our plot, at the desired coordinates:

annotate!(-3, 10, "Here's some text", :left)

This adds text at location -3, 10 and the :left indicates that it should be left-aligned to that location.

Parametric plots

Another way we can invoke a plot is to provide it two functions, both of which depend on a variable that ranges from a minimum value to a maximum value. We call the function in this format: plot(x_function, y_function, minimum, maximum) :

plot(sin, cos, 0, 2pi)
plot!(aspect_ratio = :equal)
plot(x -> x*sin(x), x -> x*cos(x), 0, 2pi)
p3 = plot!(aspect_ratio = :equal, legend=false)

3D plots

We can expand from the above to 3 dimensions:

time = 0:0.1:20
xvals = sin.(time)
yvals = cos.(time) 
zvals = time
p4 = plot(xvals, yvals, zvals)

Surface plots

This is one kind of 3D plot, where we have a value for z at every value of x and y:

xes = -1:0.01:1
p5 = surface(xes, xes, (x, y) -> x^2 + y^2, legend=false)

Here, the first argument is the x values, the second is the y values, and the third is the z values, which in this case we provided as an anonymous function relating x and y to z.

Heatmaps

We can show the above as a heatmap:

p6 = heatmap(xes, xes, (x, y) -> x^2 + y^2)

There are many other kinds of plotting commands. Keep in mind though that if it is difficult to find a high-level command to do exactly what you want, the low-level commands for plotting points, lines, and shapes along with some good logical thinking will result in anything you can imagine!

Layouts

We can combine separate plots into one figure quite simply. We first define each plot and assign it a name, resulting in it being stored as an object in memory. Then, we assemble these into a figure.

Above, we defined a series of plots and gave them the names p1 through p5. We’ll now call the `plot() function in a way that causes some of these to be plotted as panels in one graphing window:

plot(p1, p2, p4, p5)

Because we have provided four plots to be shown, the default assumption is that we want a 2x2 grid. We can alter this by using the layout keyword and grid() functions:

plot(p1, p3, p6, layout = (1, 3))

Try switching the 3 and 1 to see what you get.

plot(p3, p4, p6, p5, layout = grid(2, 2, heights=[0.4, 0.6], widths=[0.3, 0.7]))

The @layout macro

Earlier, we learned about how the @ symbol is used to indicate a macro, something that is a tool to write non-standard code that is then re-written by the macro into actual Julia code. You can think of it as a convenient shortcut. The @layout macro is a wonderful example:

plot(p1, p2, p3, p4, p5, p6, 
    layout = @layout [ s s s
                        s s
                         s])

After the @layout, we simply write brackets within which is a visual representation of the layout of the plot. The s symbol here is arbitrary–you can use whichever letter you want–the key thing is that the pattern of white space and line breaks between the symbols conveys to the macro what the layout arrangement should be.

If we want our sub-plots to take up differing amounts of space, we can add that in this way:

bigPlot = plot(p4, p5, p1, p2, p3, p6, 
                layout = @layout [ s s s
                                s{0.8w} s
                                s{0.2h}])

The {0.8w} and {0.2h} mean that that subplot should take up 0.8 proportion of the width or 0.2 proportion of the height.

Save your plot

We can now save the plot, and give a path/filename that ends by designating the format (e.g. pdf, png, jpg):

savefig(bigPlot, "myplot.pdf")

Next steps

This has been a quick intro to plotting, but I hope it gives a good idea of the rich capability of the Plots package and how to get started producing your own plots in Julia.

Now let’s jump into a full data analysis. We’ll learn how to import data into Julia, do a bunch of processing and calculations, and graph results.