Here’s a similar interactive plot using Makie:
using WGLMakie
using Downloads, CSV, DataFrames
file = Downloads.download("https://vega.github.io/vega-datasets/data/stocks.csv")
df = CSV.read(file, DataFrame)
wide = unstack(df, :date, :symbol, :price)
symbols = names(wide, r"[A-Z]")
data = Array(wide[!, symbols])
cursor = Observable(1)
stock_levels = @lift permutedims(data) ./ data[$cursor, :]
fig, ax, _ = series(stock_levels, labels=symbols, color=:Set1, linewidth=2.5)
vlines!(cursor, color=:red)
text!(cursor, 0, text=@lift(string(" ", $cursor)), color=:red)
axislegend()
on(events(ax).mouseposition) do ev
x, y = round.(Int, mouseposition(ax))
if x != cursor[] && 1 ≤ x ≤ nrow(wide)
cursor[] = x
end
autolimits!(ax)
end
fig
(I’m just moving the mouse around, unfortunately the mouse pointer is not visible in the recording.)
Some remarks:
- It feels quite sluggish.
- Support for dates is still poor in Makie so here I just show the row numbers to keep the focus on the mechanics of interaction.
- I also couldn’t find how to show the red label under the red line (outside of the axis). Does anyone have a solution for that?
- WGLMakie is still experimental and I think it shows: I found it fiddly while developing, for example sometimes I got errors with Observables or things were not updating as expected until restarted the kernel and reloaded the IJulia page. Just use GLMakie instead of WGLMakie and it should be more robust.
Finally, a great thing with VegaLite (as I understand) is that the results can be shared/deployed easily as static web pages. So here’s a variation of the above example using JSServe to make a static export:
using WGLMakie, JSServe
using Downloads, CSV, DataFrames
file = Downloads.download("https://vega.github.io/vega-datasets/data/stocks.csv")
df = CSV.read(file, DataFrame)
wide = unstack(df, :date, :symbol, :price)
symbols = names(wide, r"[A-Z]")
data = Array(wide[!, symbols])
app = App() do session::Session
cursor = Slider(1:nrow(wide))
stock_levels = @lift permutedims(data) ./ data[$cursor, :]
with_margins(x) = x .+ (x[2]-x[1]) .* (-0.1, 0.1)
limits = @lift (nothing, with_margins(extrema(skipmissing($stock_levels))))
fig = series(stock_levels, labels=symbols, color=:Set1, linewidth=2.5, axis=(; limits))
axislegend()
vlines!(cursor, color=:red)
text!(cursor, 0, text=@lift(string(" ", $cursor)), color=:red)
return JSServe.record_states(session, DOM.div(cursor, fig))
end
Page(exportable=true, offline=true)
open("stocks.html", "w") do io
println(io, "<html><head></head><body>")
show(io, MIME"text/html"(), app)
println(io, "</body></html>")
end
You can try the result here.
Remarks:
- This uses a slider because I don’t think JSServe supports mouse events currently (without custom JavaScript).
- I couldn’t get the axis limits to update in the static export (but it works when running the
App
in the notebook). - Again lot’s of restart/reload needed during development.
- The static export is not sluggish at all