A Swift Introduction To VegaLite.jl — StatPlots For Julia | by Emmett Boudreau | Towards Data Science

Data visualization is an incredibly vital technique a Data Scientist or Analyst can do to find out a lot more about their data. Visualizing your data can make correlations significantly easier to spot, and just as well can give a good idea of other aspects of data, such as variation and sample counts. Needless to say, visualizing data is pretty important when it comes to scientific computing. If there is a programming language that wants to project an ecosystem onto Data Scientists who might want to use said language, then it is likely this ecosystem will need to contain some visualization libraries. One of the youngest languages targeted at this domain is Julia.
Julia is an awesome programming language, especially for science. It has multiple dispatch, it is fast, and it is very high-level. However, one region where Julia does have some short-comings when compared with similar options is in its ecosystem. I actually talked about this a lot in another article where I discussed the problems that are facing the Julia language and its respective ecosystem. If you are interested in such reading, you may check out that article here:
towardsdatascience.com
Given that Julia does have a rather immature ecosystem, does that mean that the options for data visualization in Julia is quite limited? The first answer to that question is no, but the second answer is that even if it were to be limited — Julia is a very good friend of LLVM compiler libraries and the C programming language, which means that most often it could call on an external library in any sort of case where a Julian package is not apt for an application.
While Julia is relatively immature, and it is true that one might get some better visualization and more robust API’s using a different language, there are still some formidable options written completely in Julia. One example of such a library is Vegalite.jl. Vegalite.jl is a modulated visualization library for Julia that I would say is most likely most similar to Plot.ly... Which really is not such a bad thing —
I love Plot.ly!
Another thing worth mentioning is that Julia also now has a Plot.ly port, so you could always use Plot.ly as well. Furthermore, they even have Plot.ly Dash for Julia. If you would like to learn more about some libraries that are not Plot.ly or Vegalite.jl for the Julia language, then I would highly suggest this article, where I discuss the Plots.jl package as well as my personal favorite for Julia, Gadfly.jl:
towardsdatascience.com
One last thing, before we dive into this package, those that might want to follow along or look at the source may view such inside of this notebook:
github.com
he first thing I would like to discuss about Vegalite is how inclusive the library actually is. Just looking at the documentation’s home page, we quickly get a grandiose reveal of just how much is included inside of the Vegalite.jl package. This package can be used for so many things, and this is not just limited to plotting.
Vegalite’s biggest strength is in data. The way Vegalite handles plotting and geometry data is a more modern software-engineering approach of using JSON data as arguments in order to make plotting more data-based and less session-based. The question with this assertion is of course whether or not this brings any benefit to the package.
The first thing that should be said about this method is that it likely out-performs most of the other packages it might end up competing with. This JSON-way of doing things is certainly very effective when it comes to CPU processing at least, however I might have a concern for my memory if I used this package a lot in my work. Another thing I would like to add is that the precompile was really fast. One thing I hate about a lot of Julia modules is that precompiling can take a disturbing amount of time, Plots.jl is a great example of this. That being said, it is a pretty cool method, but also there are certainly some oddities with this methodology.
In the world of data visualization, it is convention to use certain approaches to these sorts of scenarios. For example, most language’s code for such a thing would be something akin to this:
plt = plot(feature, other_feature, arguments)
In Vegalite, however, things get a little funky. The macro @vlplot is used for just about everything. This macro returns a type, which we will take a look at when we start plotting, and this type is also dispatched to work with some pretty cool things. The power of this package really lies within the inner constructor of that type.
Now that we have an understanding of Vegalite, let us take a dive in and get some plotting done in it!
import VegaLite: @vlplot
As touched on before, there really is not a lot in this package that we will actually need to import. That in mind, I will keep my environment clean by directly importing the macro. Now let us look at a basic scatter plot example. The data we need to provide for this will be of type Array(s). We provide respective arrays for key-word arguments. Vegalite also has a bunch of data-sets one may use to download some data, so I will do that and then assign the data to a new array that is then ready to be put into our macro call.
using VegaDatasetsdata = dataset("cars")
We can then use the right bitwise operator to basically “ inject” this data into our macro and create a new plot:
data |> @vlplot(:point)
Congratulations, you have just produced your first ever VegaLite plot! Well, I am not sure if we should be calling it a plot, it is more like a bullet point. VegaLite is actually drawing all of the data that we provided to the macro, the only problem is it does not have a maximum or a minimum. Allow to elaborate, whenever graphics math is done, we need to find a perfect pixel on the screen to put a given shape. The problem is that resolutions and screen sizes are constantly different. That being said, if I put a picture of myself 30px high on a 1080p screen, or in a graphic of that size, it would not be in anywhere near the same place if we were to change to 240p.
To mitigate this, a fraction is calculated by dividing the value by the resolution to get the percentage that the pixel is of the whole. After that, the product of that percentage and the resolution or image size is subtracted from the image size, and then that is our final location for the pixel. How do I know so much about this? I actually made my own weird little graphing library, and wrote articles about it here on Medium a while ago, you may view the last update I have posted on the package, which I might pick up work on soon!
towardsdatascience.com
In order to get our VegaLite plot to actually work, we will need to add in two new key-word arguments to our macro’s call. These are just our x and y values.
data |> @vlplot(:point, x=:Miles_per_Gallon, y=:Horsepower)
The last thing I wanted to do in this quick introduction to the amazing VegaLite.jl package is just go over some basic changes one could make to their plot. I did not want to go too far into detail about everything in Vegalite because there is simply too much there, at least for one article, perhaps I will return to talking more about this package in the future.
Firstly, scatter plots are a great thing, and an amazing tool, but they are not useful in every situation, so let us change the type of plot we are working with. The first argument, and the only positional argument, the symbol point is what we will want to replace. As an example, bar:
data |> @vlplot(:bar, x=:Miles_per_Gallon, y=:Horsepower)
There are all kinds of key-words we can provide here, but to name some of them:
parameters
width
opacity
transform
There are a lot of them, so we will just be focusing on the basics, with a little bit of flare. Let us now make some new data to work with:
arm_motion_data = [(; φ1 = cos(t), φ2 = sin(t), t) for t in 0:0.01:2pi]
Now lets create some arguments. Now let us create a basic plot, I am going to be using expressions to create shapes, which is ideal for things like animation, but here I will use it out of convenience.
arm_motion_data |>VegaLite.@vlplot(    width = 500,    height = 500,    transform = [        {calculate = "cos(datum.φ1)", as = "px1"},        {calculate = "sin(datum.φ1)", as = "py1"},        {calculate = "datum.px1 + cos(datum.φ2)", as = "px2"},        {calculate = "datum.py1 + sin(datum.φ2)", as = "py2"},    ],)
Finally, I will use the addition, +, operator in order to add on some new marks:
+VegaLite.@vlplot(    mark = :rule,    x = {datum = 0, scale = {domain = [0, 2.8]}},    y = {datum = 0, scale = {domain = [-1.4, 1.4]}},    x2 = "px1:q",    y2 = "py1:q") +VegaLite.@vlplot(mark = :point, x = "px1:q", y = "py1:q") +VegaLite.@vlplot(mark = :rule, x = "px1:q", y = "py1:q", x2 = "px2:q", y2 = "py2:q") +VegaLite.@vlplot(mark = :point, x = "px2:q", y = "py2:q")
The whole result looks something like this:
arm_motion_data = [(; φ1 = cos(t), φ2 = sin(t), t) for t in 0:0.01:2pi]arm_motion_data |>VegaLite.@vlplot(    width = 500,    height = 500,    transform = [        {calculate = "cos(datum.φ1)", as = "px1"},        {calculate = "sin(datum.φ1)", as = "py1"},        {calculate = "datum.px1 + cos(datum.φ2)", as = "px2"},        {calculate = "datum.py1 + sin(datum.φ2)", as = "py2"},    ],) +VegaLite.@vlplot(    mark = :rule,    x = {datum = 0, scale = {domain = [0, 2.8]}},    y = {datum = 0, scale = {domain = [-1.4, 1.4]}},    x2 = "px1:q",    y2 = "py1:q") +VegaLite.@vlplot(mark = :point, x = "px1:q", y = "py1:q") +VegaLite.@vlplot(mark = :rule, x = "px1:q", y = "py1:q", x2 = "px2:q", y2 = "py2:q") +VegaLite.@vlplot(mark = :point, x = "px2:q", y = "py2:q")
If you use the Julia programming language, or even if you happen to just be interested in it, I would highly recommend checking out the Vegalite package. The package has all sorts of awesome features, and a really cool and interesting methodology that I have not really seen before. Thank you very much for reading, and I hope that this neat little package and article peaked your interest when it comes to visualizing data in Julia!