Vega Lite Independent Scale with Multiple Layers and Facet - vega

Is it possible to have an independent scale for each facet and each layer? The resolve works great when you have either a facet or an extra layer, but I cannot get it to do both, wondering if it is even possible.
What I want is:
The two scales on each side
mixed with
the faceting here

The way this would be expressed in Vega-Lite is using a layer, with resolve set, within a facet. Something like this:
{
"data": {
"url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv"
},
"facet": {
"column": {
"field": "weather",
"type": "nominal"
}
},
"spec": {
"layer": [
{
"encoding": {
"x": {
"field": "date",
"timeUnit": "month",
"type": "temporal"
},
"y": {
"aggregate": "mean",
"field": "temp_max",
"type": "quantitative"
}
},
"mark": {
"color": "salmon",
"type": "line"
}
},
{
"encoding": {
"x": {
"field": "date",
"timeUnit": "month",
"type": "temporal"
},
"y": {
"aggregate": "mean",
"field": "precipitation",
"type": "quantitative"
}
},
"mark": {
"color": "steelblue",
"type": "line"
}
}
],
"resolve": {
"scale": {
"y": "independent"
}
}
}
}
While this spec is valid according to the Vega-Lite schema, there is unfortunately a bug in the vega-lite renderer that makes it unable to render this spec.
As a workaround, you can manually concatenate two layered charts with a filter transform that selects the desired subset of data for each. For example:
{
"data": {
"url": "https://vega.github.io/vega-datasets/data/seattle-weather.csv"
},
"hconcat": [
{
"layer": [
{
"mark": {"type": "line", "color": "salmon"},
"encoding": {
"x": {"type": "temporal", "field": "date", "timeUnit": "month"},
"y": {
"type": "quantitative",
"aggregate": "mean",
"field": "temp_max"
}
}
},
{
"mark": {"type": "line", "color": "steelblue"},
"encoding": {
"x": {"type": "temporal", "field": "date", "timeUnit": "month"},
"y": {
"type": "quantitative",
"aggregate": "mean",
"field": "precipitation"
}
}
}
],
"resolve": {"scale": {"y": "independent", "x": "shared"}},
"transform": [{"filter": "(datum.weather === 'sun')"}]
},
{
"layer": [
{
"mark": {"type": "line", "color": "salmon"},
"encoding": {
"x": {"type": "temporal", "field": "date", "timeUnit": "month"},
"y": {
"type": "quantitative",
"aggregate": "mean",
"field": "temp_max"
}
}
},
{
"mark": {"type": "line", "color": "steelblue"},
"encoding": {
"x": {"type": "temporal", "field": "date", "timeUnit": "month"},
"y": {
"type": "quantitative",
"aggregate": "mean",
"field": "precipitation"
}
}
}
],
"resolve": {"scale": {"y": "independent", "x": "shared"}},
"transform": [{"filter": "(datum.weather === 'fog')"}]
}
],
"$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json"
}

Related

How to display multiple charts with varying x-scales side by side in Vega

I have a set of charts with the same y-scale but varying x-scales. I am using hconcat to display them side by side. In order to conserve space and avoid repetition, I have disabled the y-axis for all but the first chart. However, this is causing the title of the first chart to offset:
This is a link to a Vega Editor.
As the blue circle indicates, the two titles, "Chain" and "Mini Invaders," are not in line. Is there a way to fix this?
I have tried to express these charts using facet but as far as I can tell, facets do not permit varying x-scales. However, please do let me know if this is somehow possible with facets.
You need labelBound: true in your spec.
Editor
{
"config": {
"view": {
"continuousWidth": 400,
"continuousHeight": 300,
"stroke": "#000000",
"strokeOpacity": 1,
"strokeWidth": 2
},
"axis": {"labelFontSize": 24, "titleFontSize": 24, "labelBound":true},
"legend": {"labelFontSize": 24, "labelLimit": 0, "titleFontSize": 32},
"title": {"baseline": "bottom", "fontSize": 24}
},
"hconcat": [
{
"layer": [
{
"mark": {"type": "area", "clip": true, "opacity": 0.2},
"encoding": {
"color": {
"field": "Variations",
"legend": {
"orient": "top",
"symbolOpacity": 1,
"symbolSize": 200,
"symbolStrokeWidth": 3,
"symbolType": "stroke"
},
"scale": {
"domain": ["Original Algorithm"],
"range": [
"#e41a1c",
"#377eb8",
"#4daf4a",
"#984ea3",
"#a65628",
"#646464"
]
},
"type": "nominal"
},
"x": {"field": "step", "type": "quantitative"},
"y": {
"axis": {"labels": true, "tickCount": 5, "title": null},
"field": "lower",
"type": "quantitative"
},
"y2": {"field": "upper"}
}
},
{
"mark": {"type": "line", "clip": true},
"encoding": {
"color": {"field": "Variations", "type": "nominal"},
"x": {"field": "step", "type": "quantitative"},
"y": {
"field": "regret",
"scale": {"domain": [0, 1]},
"type": "quantitative"
}
}
}
],
"height": 200,
"title": "Chain",
"transform": [{"filter": "(datum.domain === 'Chain')"}],
"width": 200
},
{
"layer": [
{
"mark": {"type": "area", "clip": true, "opacity": 0.2},
"encoding": {
"color": {
"field": "Variations",
"legend": {
"orient": "top",
"symbolOpacity": 1,
"symbolSize": 200,
"symbolStrokeWidth": 3,
"symbolType": "stroke"
},
"scale": {
"domain": ["Original Algorithm"],
"range": [
"#e41a1c",
"#377eb8",
"#4daf4a",
"#984ea3",
"#a65628",
"#646464"
]
},
"type": "nominal"
},
"x": {"field": "step", "type": "quantitative"},
"y": {
"axis": {"labels": false, "tickCount": 5, "title": null},
"field": "lower",
"type": "quantitative"
},
"y2": {"field": "upper"}
}
},
{
"mark": {"type": "line", "clip": true},
"encoding": {
"color": {"field": "Variations", "type": "nominal"},
"x": {"field": "step", "type": "quantitative"},
"y": {
"field": "regret",
"scale": {"domain": [0, 1]},
"type": "quantitative"
}
}
}
],
"height": 200,
"title": "Mini Invaders",
"transform": [{"filter": "(datum.domain === 'Mini Invaders')"}],
"width": 200
}

How to add vertical rules as new layer and same x-axis?

When add the strips as a new layer (in the 2-layers chart), stops to work: there are no visualization and a "WARN Cannot project a selection on encoding channel "y", which has no field".
The first two layer-definitions bellow was working fine when only two lines.
vglSpec.push(['#vis2a',{
$schema: vglVers,
data: {"url":"MyDataset1"},
// old "encoding": { x: {"field": "instant", "type": "temporal"} }
width:680,
layer: [
{
"mark": {"stroke": "#68C", "type": "line", "point": true},
"encoding": { x: {"field": "instant", "type": "temporal"}, "y": {
"field": "n_count",
"type": "quantitative"
}},
"selection": {"grid": {"type":"interval", "bind":"scales"}} //zoom
},
{
"mark": {"stroke": "red", "type": "line", "strokeOpacity": 0.4},
"encoding": { x: {"field": "instant", "type": "temporal"}, "y": {
"field": "instant_totmin",
"type": "quantitative"
}}
},
{
"mark": "rule",
"data": {"url":"MyDataset2"}, // little subset of instant of Dataset1
"encoding": {
"x": { "field": "instant", "type": "temporal"},
"color": {"value": "yellow"},
"size": {"value": 5}
},
//resolve:? x is same axis and the only visualization field
}
],
resolve: {"scale": {"y": "independent"}}
}]);
PS: only removed names and titles, all real script.
Emulating with dummy data: working fine!
Please click on the 3rd example of rule guide... And replace or adapt it for this VEGA-lite script:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"url": "data/movies.json"},
"layer": [
{
"mark": "bar",
"encoding": {
"x": {"bin": true, "field": "IMDB_Rating", "type": "quantitative"},
"y": {"aggregate": "count", "type": "quantitative"}
}
},
{
"mark": "rule",
"data": {"values": [{"IMDB_Rating":3.5},{"IMDB_Rating":7.8}]},
"encoding": {
"x": { "field": "IMDB_Rating","type": "quantitative" },
"color": {"value": "yellow"},
"size": {"value": 4}
}
}
]
}
You're using an independent y-scale, and the y-scale of a rule mark with no y encoding is not well defined. The best way to address this is probably to combine the rule mark with one of the other layers, so it can use that y scale:
vglSpec.push(['#vis2a',{
$schema: vglVers,
data: {"url":"MyDataset1"},
// old "encoding": { x: {"field": "instant", "type": "temporal"} }
width:680,
layer: [
{
"mark": {"stroke": "#68C", "type": "line", "point": true},
"encoding": { x: {"field": "instant", "type": "temporal"}, "y": {
"field": "n_count",
"type": "quantitative"
}},
"selection": {"grid": {"type":"interval", "bind":"scales"}} //zoom
},
{
layer: [
{
"mark": {"stroke": "red", "type": "line", "strokeOpacity": 0.4},
"encoding": { x: {"field": "instant", "type": "temporal"}, "y": {
"field": "instant_totmin",
"type": "quantitative"
}}
},
{
"mark": "rule",
"data": {"url":"MyDataset2"}, // little subset of instant of Dataset1
"encoding": {
"x": { "field": "instant", "type": "temporal"},
"color": {"value": "yellow"},
"size": {"value": 5}
},
//resolve:? x is same axis and the only visualization field
}
]
}
],
resolve: {"scale": {"y": "independent"}}
}]);
(note, I've not actually tried this solution because you didn't include data in your question, but the approach should work).

Why column facet in Vega Lite not working properly with layer?

I'm trying to create 3 column plot and it works when there's no layer.
But when I add the layer - 3 columns get merged into one plot (open in editor).
How to make it to be separated into 3 columns by the duration field?
CODE
For the plot with the full data please use editor link above.
{
"encoding": {
"column": { "field": "duration", "type": "nominal" },
"x": { "field": "bin_i", "type": "ordinal" }
},
"layer": [
{
"mark": { "type": "bar", "size": 2 },
"encoding": {
"y": { "field": "min", "type": "quantitative" },
"y2": { "field": "max", "type": "quantitative" }
}
},
{
"mark": { "type": "tick" },
"encoding": {
"y": { "field": "mean", "type": "quantitative" }
}
}
],
"data": {
"values": [
{
"bin_i": 1,
"duration": 1,
"max": 1.9642835793718165,
"mean": 1.0781367168962268,
"min": 0.3111818864927448
},
...
]
}
}
A layered chart does not accept a faceted encoding. If you want to facet a layered chart, you should use the facet operator rather than a facet encoding.
For your example, it would look like this (Vega Editor):
{
"facet": {"column": {"field": "duration", "type": "nominal"}},
"spec": {
"encoding": {
"x": {"field": "bin_i", "type": "ordinal"}
},
"layer": [
{
"mark": {"type": "bar", "size": 2},
"encoding": {
"y": {"field": "min", "type": "quantitative"},
"y2": {"field": "max"}
}
},
{
"mark": {"type": "tick"},
"encoding": {
"y": {"field": "mean", "type": "quantitative"}
}
}
]
},
"data": {
"values": [
{
"bin_i": 1,
"duration": 1,
"max": 1.9642835793718165,
"mean": 1.0781367168962268,
"min": 0.3111818864927448
},
...
]
}
}

Best way to split data into few lines?

I have data as shown:
2021 43466.822 medium variant
2021 43510.982 high variant
2021 43416.407 low variant
2021 43468.429 constant fertility
2021 43580.45 instant replacement
And need to get chart:
https://image.prntscr.com/image/eBKqmOUsSa_6PBlomh5Erg.png
I have tried the transform fold option, but it does not help me. And making a lot of layers for that - will be a lot of code. Is there any smart way? Also I will need an legend like the one shown.
vegalite({
height:300,
autosize: "fit",
width:width,
title: {text:"Ukraine Population Prospects",
subtitle:"Total population, million"
},
data: {
url:"https://gist.githubusercontent.com/turiy/005f2ce11637fefcde8e9d6efdb0c2e6/raw/19e67bb3a6d63e7fd9f49a596e5d24404469bd63/population_prospects.csv"},
transform: [{"calculate": "datum.population/1000", "as": "population"},{fold:["medium variant","high variant", "low variant", "constant fertility","instant replacement", "momentum", "zero migration", "constant mortality", "no change"]}],
layer: [
{ mark: "line",
encoding:{
"x": {
"timeUnit": "utcyear",
"field": "year",
"type": "temporal",
"axis": {
"values":[1950,1991,2020,2100],
"domain": false,
"gridDash": {"value": [1,1]}
}
},
"y": {
"field": "population",
"type": "quantitative",
"scale": {"domain": [15,55]},
"axis": {
"domain": false ,
"gridDash": {"value": [1,1]}
}
}
},
color: {"value":"#0000ff"},
transform:[{filter:{"timeUnit": "utcyear", "field": "year", "range": [1950, 2020]}}]
},
{
mark: "line",legend:{title:"low variant"},
encoding:{
x: {
"timeUnit": "utcyear",
"field": "year",
"type": "temporal",
"axis": {
"values":[1950,1991,2020,2100],
"domain": false,
"gridDash": {"value": [1,1]}}
},
y: {
"field": "population",
"type": "quantitative",
"scale": {"domain": [15,55]},
"axis": {
"domain": false ,
"gridDash": {"value": [1,1]}
}
},
legends:{
"orient": "top-right",
"stroke": "color",
"title": "Origin",
"encode": {
"symbols": {
"update": {
"fill": {"value": ""},
"strokeWidth": {"value": 2},
"size": {"value": 64}
}
}
}
},
color: {"field": "key", "type":"nominal"}
},
transform:[{filter:{"timeUnit": "year", "field": "year", "range": [2020, 2100]}},
{filter:{field:"type", "equal":"low variant"}}]
}
]})
And I am getting like this https://image.prntscr.com/image/3Y9WNk4SQzGYWDr2JKWV9A.png
If your variants are listed by name in a column as in your example dataset, you can use a detail encoding to split them into different lines (vega editor link):
{
"data": {
"url": "https://gist.githubusercontent.com/turiy/005f2ce11637fefcde8e9d6efdb0c2e6/raw/19e67bb3a6d63e7fd9f49a596e5d24404469bd63/population_prospects.csv"
},
"mark": "line",
"encoding": {
"detail": {"type": "nominal", "field": "type"},
"x": {"type": "quantitative", "field": "year"},
"y": {"type": "quantitative", "field": "population"}
}
}
If you use color rather than detail, each line will be a different color and a legend will be included.
To add labels at the right side of the chart, you can use a text mark with an aggregate transform; something like this:
{
"data": {
"url": "https://gist.githubusercontent.com/turiy/005f2ce11637fefcde8e9d6efdb0c2e6/raw/19e67bb3a6d63e7fd9f49a596e5d24404469bd63/population_prospects.csv"
},
"layer": [
{
"mark": "line",
"encoding": {
"detail": {"type": "nominal", "field": "type"},
"x": {"type": "quantitative", "field": "year"},
"y": {"type": "quantitative", "field": "population"}
}
},
{
"transform": [
{"filter": "datum.type != 'estimate'"},
{
"aggregate": [{"op": "argmax", "field": "year", "as": "rightmost"}],
"groupby": ["type"]
}
],
"mark": {"type": "text", "align": "left"},
"encoding": {
"text": {"type": "nominal", "field": "rightmost.type"},
"x": {"type": "quantitative", "field": "rightmost.year"},
"y": {"type": "quantitative", "field": "rightmost.population"}
}
}
],
"width": 400
}

How to plot several variables on an axis with Vega-Lite?

Following Vega-Lite's Seattle weather tutorial, it was easy to plot avg min temperature by month:
{
"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"data": {
"url": "https://vega.github.io/vega-lite/data/seattle-weather.csv"
},
"mark": "line",
"encoding": {
"x": {
"timeUnit": "month",
"field": "date",
"type": "temporal"
},
"y": {
"aggregate": "mean",
"field": "temp_min",
"type": "quantitative"
}
}
}
This dataset also has temp_max variable. How can I plot both temp_min and temp_max on y-axis?
You can use layering as described at https://vega.github.io/vega-lite/docs/layer.html.
{
"data": {"url": "data/seattle-weather.csv"},
"layer": [
{
"mark": "line",
"encoding": {
"x": {
"timeUnit": "month",
"field": "date",
"type": "temporal"
},
"y": {
"aggregate": "mean",
"field": "temp_min",
"type": "quantitative"
}
}
},
{
"mark": "line",
"encoding": {
"x": {
"timeUnit": "month",
"field": "date",
"type": "temporal"
},
"y": {
"aggregate": "mean",
"field": "temp_max",
"type": "quantitative"
}
}
}
]
}