Examples
Setup
Mix.install([
{:vega_lite, "~> 0.1.0"},
{:kino, "~> 0.1.0"}
])
alias VegaLite, as: Vl
VegaLite
To dynamically stream data to a plot, all you need is a regular
VegaLite specification
that you can pass to Kino.VegaLite.start/1
. You don't have to specify any data up-front.
vl_widget =
Vl.new(width: 400, height: 400)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
|> Kino.VegaLite.start()
Then you can push data to the plot widget at any point and see it update dynamically:
for i <- 1..300 do
point = %{x: i / 10, y: :math.sin(i / 10)}
# The :window option ensures we only show the latest
# 100 data points on the plot
Kino.VegaLite.push(vl_widget, point, window: 100)
Process.sleep(25)
end
You can also explicitly clear the data:
Kino.VegaLite.clear(vl_widget)
Kino.render/1
Above we saw how you can render and stream data to the plot from separate cells.
However, the widget can also be rendered explicitly at any point with
the Kino.render/1
function.
vl_widget =
Vl.new(width: 400, height: 400)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
|> Kino.VegaLite.start()
|> Kino.render()
for i <- 1..300 do
point = %{x: i / 10, y: :math.sin(i / 10)}
Kino.VegaLite.push(vl_widget, point, window: 100)
Process.sleep(25)
end
In fact, you can think of this function as a generalized IO.puts/2
that works
for any type and is rendered by Livebook similarly to regular evaluation results.
# Arbitrary data structures
Kino.render([%{name: "Jake Peralta"}, %{name: "Amy Santiago"}])
# Static plots
vl =
Vl.new(width: 400, height: 400)
|> Vl.data_from_series(x: 1..100, y: 1..100)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
Kino.render(vl)
Kino.render(vl)
Kino.render("Plain text")
"Cell result 🚀"
Periodical updates
You may want to have a plot running forever and updating in the background.
There is a dedicated Kino.VegaLite.periodically/4
function that allows you do do just that!
You just need to specify the interval and the reducer callback like this,
then you interact with the widget as usually.
vl_widget =
Vl.new(width: 400, height: 400)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
|> Kino.VegaLite.start()
|> Kino.render()
# Add a callback to run every 25ms
Kino.VegaLite.periodically(vl_widget, 25, 0, fn i ->
point = %{x: i / 10, y: :math.sin(i / 10)}
# Interacting with the widget is as usual
Kino.VegaLite.push(vl_widget, point, window: 100)
# Continue with the new accumulator value
{:cont, i + 1}
end)