follows | id |
---|---|
gallery |
litvis |
@import "../assets/litvis.less"
For all interactive examples on this page, the code block header must include the interactive
keyword.
Examples that use data from external sources tend to use files from the Vega-Lite and giCentre data servers. For consistency the paths to these data locations are defined here:
vegaPath : String
vegaPath =
"https://cdn.jsdelivr.net/npm/vega-datasets@2/data/"
giCentrePath : String
giCentrePath =
"https://gicentre.github.io/data/"
Adding maTooltip ttEncoding
to a visible mark will enable tooltips describing the data that have been encoded in that mark.
defaultTooltip : Spec
defaultTooltip =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
enc =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
in
toVegaLite [ width 400, height 400, data, enc [], point [ maTooltip ttEncoding ] ]
The data value shown in a tooltip can be customised by encoding any data field with the tooltip channel.
customTooltip : Spec
customTooltip =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
enc =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
<< tooltip [ tName "Origin" ]
in
toVegaLite [ width 400, height 400, data, enc [], point [] ]
To show multiple values, encode with the tooltips (plural) channel. The format of the values shown in the tooltip can be controlled with tFormat.
topTrumps : Spec
topTrumps =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
trans =
transform
-- Convert to Kg
<< calculateAs "datum.Weight_in_lbs * 0.45359" "Weight"
enc =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
<< tooltips
[ [ tName "Name" ]
, [ tName "Origin" ]
, [ tName "Year", tTimeUnit year ]
, [ tName "Cylinders", tQuant ]
-- Show weight (in Kgs) without decimal places and a thousands separator
, [ tName "Weight", tFormat ",.0f" ]
]
in
toVegaLite [ width 400, height 400, data, trans [], enc [], point [] ]
Marks can be associated with clickable hyperlinks by encoding them with the hyperlink channel. Try clicking on any of the circle symbols below:
hyperScatter : Spec
hyperScatter =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
trans =
transform
<< calculateAs "'https://www.google.com/search?q=' + datum.Name" "url"
enc =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
<< tooltips [ [ tName "Name" ], [ tName "Year", tTimeUnit year ] ]
<< hyperlink [ hName "url" ]
in
toVegaLite [ width 400, height 400, data, trans [], enc [], point [] ]
You can make any chart zoomable by binding its positional scales to an interval selection. Additionally, this example sets an initial zoomed-in view by setting the positional x and y scales to be a small part of their respective data domains. Drag to pan. Zoom in or out with mouse-wheel/zoom gesture.
zoomScatter : Spec
zoomScatter =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
ps =
params
<< param "zoomer" [ paSelect seInterval [], paBindScales ]
enc =
encoding
<< position X
[ pName "Horsepower"
, pQuant
-- Set initial x-axis scaling (will change with zooming)
, pScale [ scDomain (doNums [ 75, 150 ]) ]
]
<< position Y
[ pName "Miles_per_Gallon"
, pQuant
-- Set initial y-axis scaling (will change with zooming)
, pScale [ scDomain (doNums [ 20, 40 ]) ]
]
<< size [ mName "Cylinders", mQuant ]
in
toVegaLite [ data, ps [], enc [], circle [] ]
Drag out a rectangular area ("interval selection") to highlight points that fall within the rectangle.
intervalHighlight : Spec
intervalHighlight =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
ps =
params
<< param "myBrush" [ paSelect seInterval [] ]
enc =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
<< color
[ mCondition (prParam "myBrush")
-- Encoding when selected:
[ mName "Cylinders", mOrdinal ]
-- Encoding when not selected:
[ mStr "grey" ]
]
in
toVegaLite [ width 400, height 400, ps [], data, enc [], point [] ]
Mouse over individual points or select multiple points with the shift key
hoverHighlight : Spec
hoverHighlight =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
ps =
params
<< param "myPaintbrush"
[ paSelect sePoint [ seOn "mouseover", seNearest True ] ]
enc =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
<< fillOpacity
[ mCondition (prParamEmpty "myPaintbrush")
[ mNum 1 ]
[ mNum 0.3 ]
]
<< color
[ mCondition (prParamEmpty "myPaintbrush")
[ mStr "rgb(230,110,90)" ]
[ mStr "rgb(80,80,80)" ]
]
<< size
[ mCondition (prParamEmpty "myPaintbrush")
[ mNum 200 ]
[ mNum 50 ]
]
in
toVegaLite [ width 400, height 400, ps [], data, enc [], point [ maFilled True ] ]
Input GUI elements such as sliders and checkboxes can be used to select and filter data.
sliderSelection : Spec
sliderSelection =
let
data =
dataFromUrl (vegaPath ++ "cars.json") []
trans =
transform
<< calculateAs "year(datum.Year)" "Year"
ps =
params
<< param "CylYr"
[ paSelect sePoint [ seToggle tpFalse, seFields [ "Cylinders", "Year" ] ]
, paValues (dataObjects [ [ ( "Cylinders", num 4 ), ( "Year", num 1977 ) ] ])
, paBindings
[ ( "Cylinders", ipRange [ inName "Cylinders", inMin 3, inMax 8, inStep 1 ] )
, ( "Year", ipRange [ inName "Year", inMin 1969, inMax 1981, inStep 1 ] )
]
]
encPos =
encoding
<< position X [ pName "Horsepower", pQuant ]
<< position Y [ pName "Miles_per_Gallon", pQuant ]
enc1 =
encoding
<< color [ mCondition (prParam "CylYr") [ mName "Origin" ] [ mStr "grey" ] ]
spec1 =
asSpec [ ps [], enc1 [], circle [] ]
trans2 =
transform
<< filter (fiSelection "CylYr")
enc2 =
encoding
<< color [ mName "Origin" ]
<< size [ mNum 100 ]
spec2 =
asSpec [ trans2 [], enc2 [], circle [] ]
in
toVegaLite [ width 400, height 400, data, trans [], encPos [], layer [ spec1, spec2 ] ]
Move pointer over bars to highlight and click a bar to select. Shift-click allows you to select multiple bars.
hoverAndHighlight : Spec
hoverAndHighlight =
let
cfg =
configure
<< configuration (coScale [ sacoBandPaddingInner 0.2 ])
data =
dataFromColumns []
<< dataColumn "a" (strs [ "A", "B", "C", "D", "E", "F", "G", "H", "I" ])
<< dataColumn "b" (nums [ 28, 55, 43, 91, 81, 53, 19, 87, 52 ])
ps =
params
<< param "highlight" [ paSelect sePoint [ seOn "mouseover" ] ]
<< param "select" [ paSelect sePoint [] ]
enc =
encoding
<< position X [ pName "a", pOrdinal ]
<< position Y [ pName "b", pQuant ]
<< fillOpacity [ mCondition (prParam "select") [ mNum 1 ] [ mNum 0.3 ] ]
<< strokeWidth
[ mConditions
[ ( prParamEmpty "select", [ mNum 2 ] )
, ( prParamEmpty "highlight", [ mNum 1 ] )
]
[ mNum 0 ]
]
in
toVegaLite
[ cfg []
, data []
, ps []
, enc []
, bar [ maFill "#4C78A8", maStroke "black", maCursor cuPointer ]
]
Aggregate operations such as counts and averages are subject to any data filtering transformations. If the filtering is dependent on a selection, we can create dynamic positioning of marks. Here the average of a selection is represented by a horizontal rule that responds to a dynamic selection. Drag over bars to update selection average.
dynamicAverage : Spec
dynamicAverage =
let
data =
dataFromUrl (vegaPath ++ "seattle-weather.csv") []
ps =
params
<< param "myBrush" [ paSelect seInterval [ seEncodings [ chX ] ] ]
encPosition =
encoding
<< position Y [ pName "precipitation", pAggregate opMean ]
enc1 =
encoding
<< position X [ pName "date", pOrdinal, pTimeUnit month ]
<< opacity [ mCondition (prParam "myBrush") [ mNum 1 ] [ mNum 0.7 ] ]
spec1 =
asSpec [ ps [], enc1 [], bar [] ]
trans =
transform
<< filter (fiSelection "myBrush")
spec2 =
asSpec [ trans [], rule [ maColor "firebrick", maSize 3 ] ]
in
toVegaLite [ data, encPosition [], layer [ spec1, spec2 ] ]
For continuous marks like area, line and trail we can highlight parts of it by layering a filtered subset on top of the original data. In the example below we select an interval, but only in the X position channel, allowing a time period to be highlighted (drag a rectangle to see the effect).
timeBrush : Spec
timeBrush =
let
data =
dataFromUrl (vegaPath ++ "unemployment-across-industries.json") []
trans =
transform
<< filter (fiSelection "myBrush")
ps =
params
<< param "myBrush" [ paSelect seInterval [ seEncodings [ chX ] ] ]
enc =
encoding
<< position X
[ pName "date"
, pTimeUnit yearMonth
, pAxis [ axTitle "", axFormat "%Y" ]
]
<< position Y [ pName "count", pAggregate opSum ]
specBackground =
asSpec [ ps [], area [ maOpacity 0.2 ] ]
specHighlight =
asSpec [ trans [], area [ maOpacity 1 ] ]
in
toVegaLite [ width 400, data, enc [], layer [ specBackground, specHighlight ] ]
Using seBindLegend we can create two-way interaction between a legend and the data it summarises. Clicking on a data item highlights it in the legend and double-clicking on a legend entry highlights the associated data.
interactiveLegend : Spec
interactiveLegend =
let
data =
dataFromUrl (giCentrePath ++ "westMidlands/westMidsCrimesAggregated.tsv") []
ps =
params
<< param "mySelection"
[ paSelect sePoint [ seOn "click", seFields [ "crimeType" ] ]
, paBindLegend "dblclick"
]
cScale =
categoricalDomainMap
[ ( "Anti-social behaviour", "rgb(59,118,175)" )
, ( "Burglary", "rgb(81,157,62)" )
, ( "Criminal damage and arson", "rgb(141,106,184)" )
, ( "Drugs", "rgb(239,133,55)" )
, ( "Robbery", "rgb(132,88,78)" )
, ( "Vehicle crime", "rgb(213,126,190)" )
]
enc =
encoding
<< position X [ pName "month", pTemporal, pTitle "" ]
<< position Y [ pName "reportedCrimes", pQuant, pTitle "Reported crimes" ]
<< color [ mCondition (prParam "mySelection") [ mName "crimeType", mScale cScale ] [ mStr "black" ] ]
<< opacity [ mCondition (prParam "mySelection") [ mNum 1 ] [ mNum 0.1 ] ]
in
toVegaLite [ width 540, ps [], data, enc [], circle [] ]
Interactive highlight and labels positioned at end of each line.
interactiveHighlight : Spec
interactiveHighlight =
let
cfg =
configure
<< configuration (coView [ vicoStroke Nothing ])
data =
dataFromUrl (vegaPath ++ "stocks.csv") [ parse [ ( "date", foDate "" ) ] ]
ps =
params
<< param "myHover"
[ paSelect sePoint [ seOn "mouseover", seFields [ "symbol" ] ]
, paValues (dataObjects [ [ ( "symbol", str "AAPL" ) ] ])
]
trans =
transform
<< filter (fiExpr "datum.symbol !== 'IBM'")
enc =
encoding
<< color [ mCondition (prParamEmpty "myHover") [ mName "symbol", mLegend [] ] [ mStr "grey" ] ]
<< opacity [ mCondition (prParamEmpty "myHover") [ mNum 1 ] [ mNum 0.2 ] ]
enc1 =
encoding
<< position X [ pName "date", pTemporal, pTitle "" ]
<< position Y [ pName "price", pQuant, pTitle "Price" ]
spec1 =
asSpec
[ enc1 []
, layer
[ -- Transparent layer to make it easier to trigger selection"
asSpec [ ps [], line [ maStrokeWidth 8, maStroke "transparent" ] ]
, asSpec [ line [] ]
]
]
enc2 =
encoding
<< position X [ pName "date", pTemporal, pAggregate opMax ]
<< position Y [ pName "price", pQuant, pAggregate (opArgMax (Just "date")) ]
enc2_1 =
encoding
<< text [ tName "symbol" ]
spec2 =
asSpec
[ enc2 []
, layer
[ asSpec [ circle [] ]
, asSpec [ enc2_1 [], textMark [ maAlign haLeft, maDx 4 ] ]
]
]
in
toVegaLite [ width 540, cfg [], data, trans [], enc [], layer [ spec1, spec2 ] ]
For more detailed comparison between values, we can position a vertical rule mark by filtering dates with a single valued selection. We can annotate the rule by showing point and text marks for that selection. The original lines, filtered rule and filtered point and text marks are all layered on top of each other. Adapted from this example by Jake Vanderplas
dynamicAnnotation : Spec
dynamicAnnotation =
let
data =
dataFromUrl (vegaPath ++ "stocks.csv") [ parse [ ( "date", foDate "" ) ] ]
ps =
params
<< param "label"
[ paSelect sePoint
[ seNearest True, seOn "mouseover", seEncodings [ chX ] ]
]
enc1 =
encoding
<< position X [ pName "date", pTemporal ]
<< position Y [ pName "price", pQuant ]
<< color [ mName "symbol" ]
spec1 =
asSpec
[ enc1 []
, layer
[ asSpec [ line [] ]
, asSpec [ ps [], enc1_2 [], point [] ]
]
]
enc1_2 =
encoding
<< opacity [ mCondition (prParamEmpty "label") [ mNum 1 ] [ mNum 0 ] ]
spec2 =
asSpec [ trans2 [], layer [ spec2_1, spec2_2 ] ]
trans2 =
transform << filter (fiSelectionEmpty "label")
spec2_1 =
asSpec [ enc2_1 [], rule [ maColor "gray" ] ]
enc2_1 =
encoding << position X [ pName "date", pTemporal ]
spec2_2 =
asSpec [ enc2_2 [], textMark [ maAlign haLeft, maDx 5, maDy -5 ] ]
enc2_2 =
encoding
<< position X [ pName "date", pTemporal ]
<< position Y [ pName "price", pQuant ]
<< text [ tName "price", tQuant ]
<< color [ mName "symbol" ]
in
toVegaLite [ width 540, height 300, data, layer [ spec1, spec2 ] ]
Instead of labelling the vertical rule directly we can add a tooltip associated with the selection indicated by the rule.
dynamicTooltips : Spec
dynamicTooltips =
let
data =
dataFromUrl (vegaPath ++ "stocks.csv") [ parse [ ( "date", foDate "" ) ] ]
enc =
encoding
<< position X [ pName "date", pTemporal ]
transSelFilter =
transform
<< filter (fiSelectionEmpty "hover")
enc1 =
encoding
<< position Y [ pName "price", pQuant ]
<< color [ mName "symbol" ]
spec1 =
asSpec
[ enc1 []
, layer
[ asSpec [ line [] ]
, asSpec [ transSelFilter [], point [] ]
]
]
ps =
params
<< param "hover"
[ paSelect sePoint
[ seFields [ "date" ]
, seOn "mouseover"
, seClear "mouseout"
, seNearest True
]
]
transPivot =
transform
<< pivot "symbol" "price" [ piGroupBy [ "date" ] ]
enc2 =
encoding
<< opacity [ mCondition (prParamEmpty "hover") [ mNum 0.3 ] [ mNum 0 ] ]
<< tooltips
[ [ tName "AAPL", tQuant ]
, [ tName "AMZN", tQuant ]
, [ tName "GOOG", tQuant ]
, [ tName "IBM", tQuant ]
, [ tName "MSFT", tQuant ]
]
spec2 =
asSpec [ ps [], transPivot [], enc2 [], rule [] ]
in
toVegaLite [ width 540, height 300, data, enc [], layer [ spec1, spec2 ] ]
Instead of simply reporting the data under the current selection, we can use the selection to rescale the data, so they show values relative to those under the selection. This uses lookupSelection to join the data under the selection to the primary data source.
dynamicRescaling : Spec
dynamicRescaling =
let
data =
dataFromUrl (vegaPath ++ "stocks.csv") [ parse [ ( "date", foDate "" ) ] ]
ps =
params
<< param "index"
[ paSelect sePoint [ seToggle tpFalse, seOn "mouseover", seEncodings [ chX ], seNearest True ]
, paValues (dataObjects [ [ ( "x", dt [ dtYear 2005, dtMonthNum Jan, dtDate 1 ] ) ] ])
]
trans =
transform
<< lookupSelection "symbol" "index" "symbol"
<< calculateAs "datum.index && datum.index.price > 0 ? (datum.price - datum.index.price)/datum.index.price : 0"
"indexed_price"
pointEnc =
encoding
<< position X [ pName "date", pTemporal, pAxis [] ]
pointSpec =
asSpec [ ps [], pointEnc [], point [ maOpacity 0 ] ]
lineEnc =
encoding
<< position X [ pName "date", pTemporal, pAxis [] ]
<< position Y [ pName "indexed_price", pQuant, pAxis [ axFormat "%" ] ]
<< color [ mName "symbol" ]
lineSpec =
asSpec [ trans [], lineEnc [], line [] ]
ruleTrans =
transform
<< filter (fiSelection "index")
ruleEnc =
encoding
<< position X [ pName "date", pTemporal, pAxis [] ]
<< color [ mStr "firebrick" ]
textEnc =
encoding
<< position Y [ pNum 310 ]
<< text [ tName "date", tTimeUnit yearMonth ]
labelledRuleSpec =
asSpec
[ ruleTrans []
, ruleEnc []
, layer
[ asSpec [ rule [ maStrokeWidth 0.5 ] ]
, asSpec [ textEnc [], textMark [ maAlign haCenter, maFontWeight (fwValue 100) ] ]
]
]
in
toVegaLite [ width 650, height 300, data, layer [ pointSpec, lineSpec, labelledRuleSpec ] ]
Here we show tooltips for multiple series under the current selection.
multiSeriesTooltip : Spec
multiSeriesTooltip =
let
data =
dataFromUrl (vegaPath ++ "seattle-weather.csv") []
cfg =
configure
<< configuration (coAxis [ axcoMinExtent 30 ] |> coAxisYFilter)
enc =
encoding
<< position X [ pName "date", pTimeUnit yearMonthDate, pAxis [ axTitle "" ] ]
<< tooltips
[ [ tName "date", tTimeUnit yearMonthDate ]
, [ tName "temp_max", tQuant ]
, [ tName "temp_min", tQuant ]
]
enc1 =
encoding
<< position Y [ pName "temp_max", pQuant ]
spec1 =
asSpec [ line [ maColor "orange" ], enc1 [] ]
enc2 =
encoding
<< position Y [ pName "temp_min", pQuant ]
spec2 =
asSpec [ line [ maColor "red" ], enc2 [] ]
ps =
params
<< param "hover" [ paSelect sePoint [ seToggle tpFalse, seOn "mouseover" ] ]
enc3 =
encoding
<< color [ mCondition (prParamEmpty "hover") [] [ mStr "transparent" ] ]
spec3 =
asSpec [ ps [], enc3 [], rule [] ]
in
toVegaLite [ width 540, cfg [], data, enc [], layer [ spec1, spec2, spec3 ] ]
Moving the mouse over an airport shows its direct connections.
connections : Spec
connections =
let
dataBoundaries =
dataFromUrl (vegaPath ++ "us-10m.json") [ topojsonFeature "states" ]
dataAirports =
dataFromUrl (vegaPath ++ "airports.csv") []
dataFlights =
dataFromUrl (vegaPath ++ "flights-airport.csv") []
cfg =
configure
<< configuration (coView [ vicoStroke Nothing ])
ps =
params
<< param "mySelection"
[ paSelect sePoint
[ seOn "mouseover"
, seNearest True
, seToggle tpFalse
, seFields [ "origin" ]
]
]
backdropSpec =
asSpec
[ dataBoundaries
, geoshape [ maFill "#ddd", maStroke "#fff" ]
]
lineTrans =
transform
<< filter (fiSelectionEmpty "mySelection")
<< lookup "origin" dataAirports "iata" (luAs "o")
<< lookup "destination" dataAirports "iata" (luAs "d")
lineEnc =
encoding
<< position Longitude [ pName "o.longitude" ]
<< position Latitude [ pName "o.latitude" ]
<< position Longitude2 [ pName "d.longitude" ]
<< position Latitude2 [ pName "d.latitude" ]
lineSpec =
asSpec
[ dataFlights
, lineTrans []
, lineEnc []
, rule [ maColor "black", maOpacity 0.35 ]
]
airportTrans =
transform
<< aggregate [ opAs opCount "" "routes" ] [ "origin" ]
<< lookup "origin"
dataAirports
"iata"
(luFields [ "state", "latitude", "longitude" ])
<< filter (fiExpr "datum.state !== 'PR' && datum.state !== 'VI'")
airportEnc =
encoding
<< position Longitude [ pName "longitude" ]
<< position Latitude [ pName "latitude" ]
<< size [ mName "routes", mQuant, mScale [ scRange (raNums [ 0, 1000 ]) ], mLegend [] ]
<< order [ oName "routes", oSort [ soDescending ] ]
airportSpec =
asSpec [ dataFlights, airportTrans [], ps [], airportEnc [], circle [] ]
in
toVegaLite
[ cfg []
, width 900
, height 500
, projection [ prType albersUsa ]
, layer [ backdropSpec, lineSpec, airportSpec ]
]