hvega: Create Vega-Lite visualizations (version 4) in Haskell.

[ bsd3, graphics, library ] [ Propose Tags ]

This is based on the elm-vegalite package (http://package.elm-lang.org/packages/gicentre/elm-vegalite/latest) by Jo Wood of the giCentre at the City University of London.

[Skip to Readme]
Versions [faq],,,,,,,,,,,,,,,,,,,,
Change log CHANGELOG.md
Dependencies aeson (>=0.11 && <1.5), aeson-pretty (==0.8.*), base (>=4.9 && <5), bytestring, directory, filepath, http-conduit (==2.3.*), hvega, tagsoup (==0.14.*), text (==1.2.*), unordered-containers (==0.2.*) [details]
License BSD-3-Clause
Copyright 2018-2020 Douglas Burke
Author Douglas Burke
Maintainer dburke.gw@gmail.com
Category Graphics
Home page https://github.com/DougBurke/hvega
Bug tracker https://github.com/DougBurke/hvega/issues
Source repo head: git clone https://github.com/DougBurke/hvega
Uploaded by DouglasBurke at 2020-03-27T16:16:03Z
Distributions LTSHaskell:, NixOS:, Stackage:
Executables getbetelgeuse, playtutorial
Downloads 7345 total (79 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2020-03-27 [all 1 reports]


[Index] [Quick Jump]



Build associated tools (in general you won't need this)


Use -f <flag> to enable a flag, or -f -<flag> to disable that flag. More info


Maintainer's Corner

For package maintainers and hackage trustees

Readme for hvega-

[back to package description]


vega-lite version

Create Vega-Lite visualizations in Haskell. It targets version 4.7 of the Vega-Lite specification. Note that the module does not include a viewer for these visualizations (which are JSON files), but does provide several helper functions, such as toHtmlFile, which create HTML that can be viewed with a browser to display the visualization. Other approaches include automatic display in IHaskell notebooks - with the ihaskell-vega package - or use of external viewers such as Vega View and Vega-Desktop.

It started off being a copy on an early version (2.2.1) of the Elm Vega library, which is released under a BSD3 license by Jo Wood of the giCentre at the City University of London.

This code is released under the BSD3 license.



The Vega-Lite example gallery contain a number of visualizations of the cars.json dataset, which has a number of columns to display, such as "Horsepower", "Miles_per_Gallon", and "Origin". The following code will create a visualization that plots the efficiency of the cars (the "mpg") as a function of its Horsepower, and color-code by the origin of the car:

let cars =  dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" []

    enc = encoding
            . position X [ PName "Horsepower", PmType Quantitative ]
            . position Y [ PName "Miles_per_Gallon", PmType Quantitative ]
            . color [ MName "Origin", MmType Nominal ]

    bkg = background "rgba(0, 0, 0, 0.05)"

in toVegaLite [ bkg, cars, mark Circle [], enc [] ]

When the JSON is viewed with a Vega-Lite aware viewer, the resultant plot is

Simple scatterplot


In late 2019 and early 2020 the star Betelgeuse - a member of the constellation Orion - dimmed enough that you could see it. Betelgeuse is a member of the class of Red Supergiant stars, which are massive enough that they will go supernova at some point, and so there was some speculation that we could see a "naked-eye" supernova (even though the current models suggest that Betelgeuse has about 100,000 more years to go before this happens). This interest lead to a lot of observations added to the American Association of Variable Star Observers database, which we are going to look at below. This example is rather-more involved than the case one, since it involves data filtering and creation, multiple plots, faceting, and interactive selection.

let titleStr = "Betelegeuse's magnitude measurements, collated by AAVSO"

    w = width 600
    h = height 150

    pos1Opts fld ttl = [PName fld, PmType Quantitative, PAxis [AxTitle ttl]]
    x1Opts = pos1Opts "days" "Days since January 1, 2020"
    y1Opts = pos1Opts "magnitude" "Magnitude" ++ [PSort [Descending], yRange]
    yRange = PScale [SDomain (DNumbers [-1, 3])]

    filtOpts = [MName "filterName", MmType Nominal]
    filtEnc = color (MLegend [ LTitle "Filter", LTitleFontSize 16, LLabelFontSize 14 ] : filtOpts)
              . shape filtOpts

    circle = mark Point [ MOpacity 0.5, MFilled False ]

    encOverview = encoding
                  . position X x1Opts
                  . position Y y1Opts
                  . filtEnc

    selName = "brush"
    pos2Opts fld = [PName fld, PmType Quantitative, PAxis [AxNoTitle],
                   PScale [SDomain (DSelectionField selName fld)]]
    x2Opts = pos2Opts "days"
    y2Opts = pos2Opts "magnitude" ++ [PSort [Descending]]

    encDetail = encoding
                . position X x2Opts
                . position Y y2Opts
                . filtEnc

    xlim = (Number (-220), Number 100)
    ylim = (Number (-0.5), Number 2.5)
    overview = asSpec [ w
                      , h
                      , encOverview []
                      , selection
                        . select selName Interval [ Encodings [ChX, ChY]
                                                  , SInitInterval (Just xlim) (Just ylim)
                        $ []
                      , circle

    detailPlot = asSpec [ w
                        , h
                        , encDetail []
                        , circle

    headerOpts = [ HLabelFontSize 16
                 , HLabelAlign AlignRight
                 , HLabelAnchor AEnd
                 , HLabelPadding (-24)
                 , HNoTitle
                 , HLabelExpr "'Filter: ' + datum.label"

    details = asSpec [ columns 1
                     , facetFlow [ FName "filterName"
                                 , FmType Nominal
                                 , FHeader headerOpts
                     , spacing 10
                     , specification detailPlot

in toVegaLite [ title titleStr [ TFontSize 18 ]
              , dataFromUrl "https://raw.githubusercontent.com/DougBurke/hvega/master/hvega/data/betelgeuse-2020-03-19.json" []
              , transform
                . filter (FExpr "datum.filterName[0] === 'V'")
                . filter (FExpr "datum.magnitude < 4")
                . calculateAs "datum.jd - 2458849.0" "days"
                $ []
              , vConcat [overview, details]
              , configure
                . configuration (Axis [ TitleFontWeight Normal, TitleFontSize 16, LabelFontSize 14 ])
                $ []

This can be viewed as

PNG version of the lightcurve


A tutorial is provided as part of the module: it is based, as is so much of the module, on the Elm Vega walk through. The tutorial is available on hackage - and includes the plot outputs - and the plots it creates are also available by importing the Graphics.Vega.Tutorials.VegaLite module.

The Vega-Lite Example Gallery has been converted to IHaskell notebooks in the notebooks directory. Start with the overview notebook, which describes the set up, and then the individual sections have their own notebooks (with names of VegaLiteGallery-<section>.ipynb.

Unfortunately the plots created by VegaEmbed do not always appear in the notebook when viewed with either GitHub's viewer or ipynb viewer, but things seem better when using Jupyter Lab (rather than notebook) to create the notebooks (since Vega is natively supported in this environment). However, the native support is for Vega-Lite version 1, and so many of the more-recent capabilities are either ignored or cause the visualization to fail.

The notebooks have been re-created using Jupyter Lab (thanks to Tweag I/O's JupyterWith environment), which should make the plots appear on GitHub (you may need to reload the notebooks as I find they don't display on the first try).

Differences to Elm Vega

Elm Vega has changed significantly since I started hvega, and no-longer exposes data types directly but uses functions instead: for example, rather than PName, it uses a function like pName. It is an open question whether hvega will make the same switch. Over time the naming of certain operations or data types has diverged between hevga and Elm Vega.

One of the more-obvious changes is that the output of toVegaLite is a separate type from the input values - that is VegaLite and VLSpec - since it makes it easier to display the output of hvega in IHaskell. The JSON specification is retrieved from this type with fromVL. The release adds some more type safety (restricting the functions that can be applied to encoding and transform for instance).