Putting it together
We will now look at how to combine traces and a layout to create a plot.
We'll also discuss how to integrate with various front-ends.
Plot
The full definition of the Plot object is
mutable struct Plot{TT<:AbstractVector{<:AbstractTrace},TL<:AbstractLayout,TF<:AbstractVector{<:PlotlyFrame}}
data::TT
layout::TL
frames::TF
divid::UUID
config::PlotConfig
endGiven one or more traces (with a subtypes of AbstractTrace) and optionally a Layout, we construct a Plot object with any of the following constructors
# A blank canvas no traces or a layout
Plot()
# A vector of traces and a layout
Plot{T<:AbstractTrace}(data::AbstractVector{T}, layout::AbstractLayout)
# A vector of traces -- default layout supplied
Plot{T<:AbstractTrace}(data::AbstractVector{T})
# a single trace: will be put into a vector -- default layout supplied
Plot(data::AbstractTrace)
# a single trace and a layout (trace put into a vector)
Plot(data::AbstractTrace, layout::AbstractLayout)Notice that none of the recommended constructors require you to pass the divid field manually (this is an internal field to enable the unique identification and display of multiple plots in a single web page).
Convenience methods
There are also a number of convenience methods to the Plot function that will attempt to construct the traces for you. The following signatures show the variety of arguments that can be used to create a plot:
PlotlyBase.Plot — Type
Plot(x, y; ...)
Plot(x, y, l; kind, config, kwargs...)
Build a plot of with one trace of type kindand set x to x and y to y. All keyword arguments are passed directly as keyword arguments to the constructed trace.
NOTE: If y is a matrix, one trace is constructed for each column of y
NOTE: If x and y are both matrices, they must have the same number of columns (say N). Then N traces are constructed, where the ith column of x is paired with the ith column of y.
Plot(y; ...)
Plot(y, l; kwargs...)
Build a scatter plot and set y to y. All keyword arguments are passed directly as keyword arguments to the constructed scatter.
Plot(f, x0, x1; ...)
Plot(f, x0, x1, l; config, kwargs...)
Construct a plot of f from x0 to x1, using the layout l. All keyword arguments are applied to the constructed trace.
Plot(fs, x0, x1; ...)
Plot(fs, x0, x1, l; config, kwargs...)
For each function in f in fs, construct a scatter trace that plots f from x0 to x1, using the layout l. All keyword arguments are applied to all constructed traces.
Here are some examples:
using PlotlyJS
p0 = Plot([0, 1, 3], [10, 5, 2])
plot(p0)t = range(0, 3, 41);
p1 = Plot(t, [2sin.(t) 3cos.(t) cos.(t).-sin.(t)], Layout(showlegend=true))
plot(p1)p2 = Plot(x -> (x - 0.6)^2, -2, 2, Layout(showlegend=false))
plot(p2)p3 = Plot([sin, cos], 0, 2*pi)
plot(p3)Especially convenient is the group keyword argument when calling Plot(::AbstractDataFrame, ... ; ...). Here is an example below:
using RDatasets
iris = RDatasets.dataset("datasets", "iris");
p = Plot(iris, x=:SepalLength, y=:SepalWidth, mode="markers", marker_size=8,
group=:Species,
Layout(title="Iris Species"))
plot(p)See the section on Groups for more detail.
SyncPlots
A Plot is a pure Julia object and doesn't interact with plotly.js by itself. This means that we can't view the actual plotly figure that the data represents until we have a way to display it to the screen.
To do that we need to link the Plot to one or more display front-ends. The (lowercase) plot() function is used to set up the link.
To actually connect to the display front-ends we use the WebIO.jl package. Our interaction with WebIO is wrapped up in a type called SyncPlot that is defined as follows:
mutable struct SyncPlot
plot::PlotlyBase.Plot
scope::Scope
window::Union{Nothing,Blink.Window}
endAs its name suggests, a SyncPlot will keep the Julia representation of a plot (the Plot instance) in sync with a plot with a front-end. The scope field refers to a WebIO.Scope object used for passing messages between Julia and JavaScript.
The Plot function will create a new Plot object while the plot function will create a new SyncPlot. The plot function passes all arguments to construct a Plot and then sets up the display. All Plot methods are also defined for plot.
By leveraging WebIO.jl we can render our figures anywhere WebIO can render. At time of writing this includes Jupyter notebooks, Jupyterlab, Mux.jl web apps, and Electron windows from Blink.jl. Please see the WebIO.jl documentation for additional information.
When using PlotlyJS.jl at the Julia REPL a plot will automatically be displayed in two possible ways.
- A
Plot()call will create a temporary HTML file with the plot embedded inside. PlotlyJS.jl code will then try to launch the application that handles.htmlfiles on the user's computer, typically their default browser. - A
plot()call will launch a new Electron window. This is a dedicated browser window we have full control over. To see a plotp, just typepby itself at the REPL and execute the line. Alternatively you can calldisplay(p).
In addition to being able to see our charts in many front-end environments, WebIO also provides a two-way communication bridge between JavaScript and Julia. In fact, when a SyncPlot is constructed, we automatically get listeners for all plotly.js javascript events.
What's more is that we can hook up Julia functions as callbacks when those events are triggered. In the very contrived example below we have Julia print out details regarding points on a plot whenever a user hovers over them on the display:
using PlotlyJS
using PlotlyJS: WebIO
p = plot(rand(10, 4));
display(p) # usually optional
WebIO.on(p["hover"]) do data
if haskey(data, "points")
pt = first(data["points"])
println("\nYou hovered over the point x=", pt["x"], ", y=", pt["y"])
end
endIn this next example, whenever we click on a point we change its marker symbol to a star and marker color to gold:
using PlotlyJS
using PlotlyJS: WebIO
colors = (fill("red", 10), fill("blue", 10))
symbols = (fill("circle", 10), fill("circle", 10))
ys = (rand(10), rand(10))
p = plot(
[scatter(y=y, marker=attr(color=c, symbol=s, size=15), line_color=c[1])
for (y, c, s) in zip(ys, colors, symbols)]
)
display(p) # usually optional
WebIO.on(p["click"]) do data
colors = (fill("red", 10), fill("blue", 10))
symbols = (fill("circle", 10), fill("circle", 10))
for point in data["points"]
colors[point["curveNumber"] + 1][point["pointIndex"] + 1] = "gold"
symbols[point["curveNumber"] + 1][point["pointIndex"] + 1] = "star"
end
restyle!(p, marker_color=colors, marker_symbol=symbols)
endWhile mostly nonsensical, hopefully these examples show you that it is possible to build rich, interactive, web-based data visualization applications with business logic implemented entirely in Julia!.
Display configuration
When calling plot or Plot, we can specify some configuration options using the config keyword argument.
The config argument must be set to an instance of PlotConfig, which should be constructed using keyword arguments.
As an example, if we were to execute the following code, we would see a static chart (no hover information or ability to zoom/pan) with 4 lines instead of an interactive one:
Plot(rand(10, 4), config=PlotConfig(staticPlot=true))See the API docs for PlotConfig for a full list of options.