You need a plot target in place, and then we're ready to plot some data. The idea of the library is to wrap vega by simply treating a chart spec as a JSON object.
- Identify a plot which looks similar to your desired visualisation
- Customise it, by modifiying the JSON spec to be your desired visualisation
As always... lean into vega;
Visualisation as JSON
We can easily manipulate JSON objects using ujson.
My worflow cascade;
- Pipe "raw" data into a vega example
- Record a list of modifiers which were useful modifications to an example for re-use... (or just use the example as is)
- Spec has been modified enough that a list of modifiers is confusing. Extend the WithBaseSpec class directly via a file or resource (see "Custom.scala"). Then pipe data into it.
Each "plot" is a case class which accepts a list of "modifiers". Each case class has the signature accepting a single argument of type;
Seq[ujson.Value => Unit]
i.e. a list of functions which modifiy a
This signature appears often enough that it is aliased as;
type JsonMod = Seq[ujson.Value => Unit]
Each time a plot "case class" (e.g.
BarPlot) is created, each of these functions is applied to a "base spec". To start with, a base spec will be an example from the vega website). The signature of
case class BarChart(override val mods : JsonMod=List())(using PlotTarget) extends FromUrl(SpecUrl.BarChart)
The constiuents of this definition are;
- mods change the spec (to make it look the way you want - for example adding your own data)
- PlotTarget is a side effect which is run when the case class is created. Often will display the plot in a browser.
- The final part tells this case class, where to obtain a "base specification". In this case, https://vega.github.io/vega/examples/bar-chart.vg.json
What that means, is that to add a title, we need to read the vega docs. To skip some steps, try...
SimpleBarChartLite(List(spec => spec("title") = "Got Viz?"))
We'll revisit this in more detail below. Crucially, to know where to add stuff to the spec, you're going to need the vega documentation.
Finally, a small number of "helpers" appear often enough that they are honoured with an implementation in the library;
Is one level of abstraction deeper.
The core promise of the library, is that it wraps Vega. It goes one further step, by making the "examples" on the vega website, easy to plot, and then customise.
viz.vega.plots.LineChartLite( List( viz.Utils.fixDefaultDataUrl ) ) // res1: LineChartLite = LineChartLite(mods = List(Fix default data url))
As we've changed the home of the chart (which no longer is on the vega lite examples homepage), we also need to adapt it's data url to point to the right place, else data loading will fail. It's not a bad excercise to allow that failure.
This is our hint on how we're going to manage minor modifications to plots.
Here, we have the line chart example from vega lite.
viz.vega.plots.xxx contains all the examples on the vega, and vega-lite websites. vega-lite charts have "lite" appended.
Someone was apparently crazy enough to implement pacman in vega. As "proof" that we really did all the examples, and for your gaming pleasure.
viz.vega.plots.Pacman() // res3: Pacman = Pacman(mods = List())
More seriously though, this library is targeted at "work".
We need a way to customise charts, which we've hinted at above, by providing a list of "modifiers". A very common customisation, is to want to display your own data (!), from the JVM / ammonite / scala runtime, in the chart. Conceptually this is no different from all the other modification we will make - just change the JSON object.