# Vega Lite Examples in Haskell¶

The aim of this notebook is to use hvega - which is a Haskell port of the Elm Vega module (note, this is a link to an old version) - to describe the Vega-Lite Gallery in an IHaskell notebook. I used ot have a single notebook with all the examples in it, but I have since split it out into multiple notebooks, called VegaLiteGallery-XXXX.ipynb. This notebook remains to explain how to load hvega and describes how the examples are created.

## Jupyter Lab versus Notebook¶

Jupyter Lab - which can be run using Tweag's jupyterWith environment - has native support for Vega and Vega-Lite visualizations, and so is the preferred medium for hvega. This notebook was run using Jupter Lab, with the shell.nix file and the command

nix-shell --command "jupyter notebook"



When run using Jupyter notebooks, the visualizations are displayed using the VegaEmbed JavaScript library, and do not provide a PNG version for display outside of the browser (e.g. PDF or the various ipynb viewers).

Unfortunately (as of version 0.3 of ihaskell-hvega), the support for jupyter lab is disappointing (due to a combination of IHaskell, jupyterWith, and the version 1.2 to 2 chanhes in Jupyter lab), so I am switching back to the notebook version for now.

## Versions¶

Let's check what version of hvega, and other useful packages, were last used.

Note that hvega supports version 4 of the Vega-Lite specification. Hopefully it is (mostly) forwards-compatible!

In [1]:
:! ghc-pkg latest ihaskell
:! ghc-pkg latest ghc
:! ghc-pkg latest hvega

ihaskell-0.10.1.2
ghc-8.8.3
hvega-0.11.0.0
ihaskell-hvega-0.3.2.0

Since many of the strings used in hvega are actually Data.Text.Text, we turn on OverloadedStrings.

In [2]:
:ext OverloadedStrings

In [3]:
-- VegaLite uses these names
import Prelude hiding (filter, lookup, repeat)

import Graphics.Vega.VegaLite

-- IHaskell automatically imports this if the ihaskell-vega module is installed


The following imports are only to show the JSON representation of a Vega-Lite visualization, and are not needed in "general" use of hvega.

In [4]:
import Data.Aeson.Encode.Pretty (encodePretty)
import Data.ByteString.Lazy.Char8 as BL8 hiding (filter, map, repeat)

-- Allow the VegaLite specification to be pretty-printed
ppSpec = BL8.putStrLn . encodePretty . fromVL

-- If you are viewing this in an IHaskell notebook rather than Jupyter Lab,
-- use the following to see the visualizations (we don't actually need a
-- function, but this is left in to make it easier to switch between
-- lab and notebook interfaces).
--
vlShow = id


As of version 0.3, there are now toHtml and toHtmlFile routines which create a HTML representation of the visualization - using Vega Embed as the viewer (this is similar to how the visualizations for notebooks is handled by ihaskell-vega).

In [5]:
:type toHtml

toHtml :: VegaLite -> Text

## Simple Bar Chart¶

{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json", "description": "A simple bar chart with embedded data.", "data": { "values": [ {"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43}, {"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53}, {"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52} ] }, "mark": "bar", "encoding": { "x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}}, "y": {"field": "b", "type": "quantitative"} } } In [6]: barFromColumns :: VegaLite barFromColumns = let desc = description "A simple bar chart with embedded data." dvals = dataFromColumns [] . dataColumn "a" (Strings ["A", "B", "C", "D", "E", "F", "G", "H", "I"]) . dataColumn "b" (Numbers [28, 55, 43, 91, 81, 53, 19, 87, 52]) enc = encoding . position X [PName "a", PmType Nominal, PAxis [AxLabelAngle 0]] . position Y [PName "b", PmType Quantitative] in toVegaLite [desc, dvals [], mark Bar [], enc []]  The specification can be displayed, since it is "just" JSON: In [7]: ppSpec barFromColumns  { "mark": "bar", "data": { "values": [ { "a": "A", "b": 28 }, { "a": "B", "b": 55 }, { "a": "C", "b": 43 }, { "a": "D", "b": 91 }, { "a": "E", "b": 81 }, { "a": "F", "b": 53 }, { "a": "G", "b": 19 }, { "a": "H", "b": 87 }, { "a": "I", "b": 52 } ] }, "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"encoding": {
"x": {
"field": "a",
"type": "nominal",
"axis": {
"labelAngle": 0
}
},
"y": {
"field": "b",
"type": "quantitative"
}
},
"description": "A simple bar chart with embedded data."
}

The toHtml function creates the following HTML "snippet" from the visualization (toHtmlFile will write it to a file). These functions were added in version 0.2.1.0.

In [8]:
toHtml barFromColumns

"<!DOCTYPE html>\n<html>\n<head>\n  <script src=\"https://cdn.jsdelivr.net/npm/[email protected]\"></script>\n  <script src=\"https://cdn.jsdelivr.net/npm/[email protected]\"></script>\n  <script src=\"https://cdn.jsdelivr.net/npm/vega-embed\"></script>\n</head>\n<body>\n<div id=\"vis\"></div>\n<script type=\"text/javascript\">\n  var spec = {\"mark\":\"bar\",\"data\":{\"values\":[{\"a\":\"A\",\"b\":28},{\"a\":\"B\",\"b\":55},{\"a\":\"C\",\"b\":43},{\"a\":\"D\",\"b\":91},{\"a\":\"E\",\"b\":81},{\"a\":\"F\",\"b\":53},{\"a\":\"G\",\"b\":19},{\"a\":\"H\",\"b\":87},{\"a\":\"I\",\"b\":52}]},\"$schema\":\"https://vega.github.io/schema/vega-lite/v4.json\",\"encoding\":{\"x\":{\"field\":\"a\",\"type\":\"nominal\",\"axis\":{\"labelAngle\":0}},\"y\":{\"field\":\"b\",\"type\":\"quantitative\"}},\"description\":\"A simple bar chart with embedded data.\"};\n vegaEmbed('#vis', spec).then(function(result) {\n // Access the Vega view instance (https://vega.github.io/vega/docs/api/view/) as result.view\n }).catch(console.error);\n</script>\n</body>\n</html>\n" Now use Vega-Embed to view the visualization directly. This should be viewable as a PNG when viewing with a non-Javascript enabled viewer, at least for notebooks processed by Jupyter Lab, but appears to fail with IHaskell notebooks. In [9]: vlShow barFromColumns  The data can also be created using dataFromRows, as shown below: In [10]: barFromRows = let desc = description "A simple bar chart with embedded data." dvals = dataFromRows [] . dataRow [("a", Str "A"), ("b", Number 28)] . dataRow [("a", Str "B"), ("b", Number 55)] . dataRow [("a", Str "C"), ("b", Number 43)] . dataRow [("a", Str "D"), ("b", Number 91)] . dataRow [("a", Str "E"), ("b", Number 81)] . dataRow [("a", Str "F"), ("b", Number 53)] . dataRow [("a", Str "G"), ("b", Number 19)] . dataRow [("a", Str "H"), ("b", Number 87)] . dataRow [("a", Str "I"), ("b", Number 52)] enc = encoding . position X [PName "a", PmType Nominal, PAxis [AxLabelAngle 0]] . position Y [PName "b", PmType Quantitative] in toVegaLite [desc, dvals [], mark Bar [], enc []]  Are the two sepcifications correct (there is currently no Eq instance defined for the VegaLite type so the underlying JSON representation is extracted using fromVL)? In [11]: fromVL barFromColumns == fromVL barFromRows  True An important piece of information is the expected specification, given on the Vega-Lite example page. I use the Aeson Template-Haskell support to embed the specification as a Haskell value (I have taken to using the suffix Spec for the variable name). So for this example we have In [12]: :ext QuasiQuotes  In [13]: import Data.Aeson.QQ.Simple (aesonQQ) barSpec = [aesonQQ| { "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A", "b": 28}, {"a": "B", "b": 55}, {"a": "C", "b": 43},
{"a": "D", "b": 91}, {"a": "E", "b": 81}, {"a": "F", "b": 53},
{"a": "G", "b": 19}, {"a": "H", "b": 87}, {"a": "I", "b": 52}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "a", "type": "nominal", "axis": {"labelAngle": 0}},
"y": {"field": "b", "type": "quantitative"}
}
}
|]


This can then be compared to the output of toVegaLite:

In [14]:
fromVL barFromRows == barSpec

True

In the gallery example I use a more-complicated validation function, defined in each notebook and cleverly named validate, which will report - in a hopefully-readable manner - any differences between the expected and created specification. There are several differences why there may be a difference, such as

• at the time I created the example, the gallery was using an old (pre version 3) version of the Vega-Lite specification (whereas now the examples are using a newer version, but I haven't updated them)

• Vega-Lite is a "flexible" schema, in that there's often more-than-one-way to define the same thing (for example, setting a value to "" or null can often mean the same thing, and some settings are optional

• hvega doesn't support one form of creating a visualization (normally a simpler, short cut) but you can create the same visualization

• actual bugs or missing functionality in hvega: issues welcome

If you are looking for the actual notebooks, then look for