Part of the power of Plots lies is in the many combinations of allowed input data. You shouldn't spend your time transforming and massaging your data into a specific format. Let Plots do that for you.
There are a few rules to remember, and you'll be a power user in no time.
Inputs are arguments, not keywords
plot function has several methods:
plot(y): treats the input as values for the
y-axis and yields a unit-range as
plot(x, y): creates a 2D plot
plot(x, y, z): creates a 3D plot
The reason lies in the flexibility of Julia's multiple dispatch, where every combination of input types can have unique behavior, when desired.
Columns are series
In most cases, passing a (
m) matrix of values (numbers, etc) will create
m series, each with
n data points. This follows a consistent rule… vectors apply to a series, matrices apply to many series. This rule carries into keyword arguments.
scatter(rand(10,4), markershape = [:circle, :rect]) will create 4 series, each assigned the markershape vector [:circle,:rect]. However,
scatter(rand(10,4), markershape = [:circle :rect]) will create 4 series, with series 1 and 3 having markers shaped as
:circle and series 2 and 4 having markers shaped as
:rect (i.e. as squares). The difference is that in the first example, it is a length-2 column vector, and in the second example it is a (1 × 2) row vector (a Matrix).
The flexibility and power of this can be illustrated by the following piece of code:
using Plots # 10 data points in 4 series xs = range(0, 2π, length = 10) data = [sin.(xs) cos.(xs) 2sin.(xs) 2cos.(xs)] # We put labels in a row vector: applies to each series labels = ["Apples" "Oranges" "Hats" "Shoes"] # Marker shapes in a column vector: applies to data points markershapes = [:circle, :star5] # Marker colors in a matrix: applies to series and data points markercolors = [ :green :orange :black :purple :red :yellow :brown :white ] plot( xs, data, label = labels, shape = markershapes, color = markercolors, markersize = 10 )
This example plots the four series with different labels, marker shapes, and marker colors by combining row and column vectors to decorate the data.
The following example illustrates how Plots.jl handles: an array of matrices, an array of arrays of arrays and an array of tuples of arrays.
x1, x2 = [1, 0], [2, 3] # vectors y1, y2 = [4, 5], [6, 7] # vectors m1, m2 = [x1 y1], [x2 y2] # 2x2 matrices plot([m1, m2]) # array of matrices -> 4 series, plots each matrix column, x assumed to be integer count plot([[x1,y1], [x2,y2]]) # array of array of arrays -> 4 series, plots each individual array, x assumed to be integer count plot([(x1,y1), (x2,y2)]) # array of tuples of arrays -> 2 series, plots each tuple as new series
Unconnected Data within same groups
As shown in the examples, you can plot a single polygon by using a single call to
plot using the
:path line type. You can use several calls to
plot to draw several polygons.
Now, let's say you're plotting
n polygons grouped into
g groups, with
g. While you can use
plot to draw separate polygons with each call, you cannot group two separate plots back into a single group. You'll end up with
n groups in the legend, rather than
To adress this, you can use
NaN as a path separator. A call to
plot would then draw one path with disjoints The following code draws
n=4 rectangles in
using Plots plotlyjs() function rectangle_from_coords(xb,yb,xt,yt) [ xb yb xt yb xt yt xb yt xb yb NaN NaN ] end some_rects=[ rectangle_from_coords(1, 1, 5, 5) rectangle_from_coords(10, 10, 15, 15) ] other_rects=[ rectangle_from_coords(1, 10, 5, 15) rectangle_from_coords(10, 1, 15, 5) ] plot(some_rects[:,1], some_rects[:,2], label = "some group") plot!(other_rects[:,1], other_rects[:,2], label = "other group")
Using the StatsPlots extension package, you can pass a
DataFrame as the first argument (similar to Gadfly or R's ggplot2). For data fields or certain attributes (such as
group) a symbol will be replaced with the corresponding column(s) of the
DataFrame. Additionally, the column name might be used as the An example:
using StatsPlots, RDatasets gr() iris = dataset("datasets", "iris") @df iris scatter( :SepalLength, :SepalWidth, group = :Species, m = (0.5, [:+ :h :star7], 12), bg = RGB(0.2, 0.2, 0.2) )
Functions can typically be used in place of input data, and they will be mapped as needed. 2D and 3D parametric plots can also be created, and ranges can be given as vectors or min/max. For example, here are alternative methods to create the same plot:
using Plots tmin = 0 tmax = 4π tvec = range(tmin, tmax, length = 100) plot(sin.(tvec), cos.(tvec))
plot(sin, cos, tvec)
plot(sin, cos, tmin, tmax)
Vectors of functions are allowed as well (one series per function).
Images can be directly added to plots by using the Images.jl library. For example, one can import a raster image and plot it with Plots via the commands:
using Plots, Images img = load("image.png") plot(img)
PDF graphics can also be added to Plots.jl plots using
load("image.pdf"). Note that Images.jl requires that the PDF color scheme is RGB.
using Plots function make_batman() p = [(0, 0), (0.5, 0.2), (1, 0), (1, 2), (0.3, 1.2), (0.2, 2), (0, 1.7)] s = [(0.2, 1), (0.4, 1), (2, 0), (0.5, -0.6), (0, 0), (0, -0.15)] m = [(p[i] .+ p[i + 1]) ./ 2 .+ s[i] for i in 1:length(p) - 1] pts = similar(m, 0) for (i, mi) in enumerate(m) append!( pts, map(BezierCurve([p[i], m[i], p[i + 1]]), range(0, 1, length = 30)) ) end x, y = Plots.unzip(Tuple.(pts)) Shape(vcat(x, -reverse(x)), vcat(y, reverse(y))) end # background and limits plt = plot( bg = :black, xlim = (0.1, 0.9), ylim = (0.2, 1.5), framestyle = :none, size = (400, 400), legend = false, )
# create an ellipse in the sky pts = Plots.partialcircle(0, 2π, 100, 0.1) x, y = Plots.unzip(pts) x = 1.5x .+ 0.7 y .+= 1.3 pts = collect(zip(x, y)) # beam beam = Shape([(0.3, 0.0), pts, pts, (0.3, 0.0)]) plot!(beam, fillcolor = plot_color(:yellow, 0.3))
# spotlight plot!(Shape(x, y), c = :yellow)
# buildings rect(w, h, x, y) = Shape(x .+ [0, w, w, 0, 0], y .+ [0, 0, h, h, 0]) gray(pct) = RGB(pct, pct, pct) function windowrange(dim, denom) range(0, 1, length = max(3, round(Int, dim/denom)))[2:end - 1] end for k in 1:50 local w, h, x, y = 0.1rand() + 0.05, 0.8rand() + 0.3, rand(), 0.0 shape = rect(w, h, x, y) graypct = 0.3rand() + 0.3 plot!(shape, c = gray(graypct)) # windows I = windowrange(w, 0.015) J = windowrange(h, 0.04) local pts = vec([(Float64(x + w * i), Float64(y + h * j)) for i in I, j in J]) windowcolors = Symbol[rand() < 0.2 ? :yellow : :black for i in 1:length(pts)] scatter!(pts, marker = (stroke(0), :rect, windowcolors)) end plt
# Holy plotting, Batman! batman = Plots.scale(make_batman(), 0.07, 0.07, (0, 0)) batman = translate(batman, 0.7, 1.23) plot!(batman, fillcolor = :black)