How align axes when creating a plot matrix in vega using vconcat and hconcat? - vega-lite

Consider plot matrix (say a 3 x 3 grid of scatter plots) where all the dimensions share axes, but the different axes are not of the same scale (consider date, log-quantity, linear for example).
To do this you create a matrix via vconcat and hconcat of chart specs.
From the docs it seems like you should use minExtent and maxExtent to align axes however I am not clear on where you apply this config? It needs to be at the lowest chart spec level as the axes extents are all different depending on the rol and column of the matrix under consideration.
See the image for an example of the misalignment

As per to the docs, you need to provide the minExtent and maxExtent in axis config.
Below is the example config or refer editor:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "Two horizonally concatenated charts that show a histogram of precipitation in Seattle and the relationship between min and max temperature.",
"data": {"url": "data/weather.csv"},
"transform": [{"filter": "datum.location === 'Seattle'"}],
"columns": 3,
"config": {"axisY": {"minExtent": 50, "maxExtent": 100}},
"concat": [
{
"mark": "bar",
"encoding": {
"x": {"timeUnit": "month", "field": "date", "type": "ordinal"},
"y": {
"aggregate": "mean",
"field": "precipitation",
"axis": {"format": ".4f"}
}
}
},
{
"mark": "bar",
"encoding": {
"x": {"timeUnit": "month", "field": "date", "type": "ordinal"},
"y": {"aggregate": "median", "field": "precipitation"}
}
},
{
"mark": "point",
"encoding": {
"x": {"field": "temp_min", "bin": true},
"y": {"field": "temp_max", "bin": true},
"size": {"aggregate": "count"}
}
},
{
"mark": "point",
"encoding": {
"x": {"field": "temp_min", "bin": true},
"y": {"field": "temp_max", "bin": true},
"size": {"aggregate": "count"}
}
},
{
"mark": "point",
"encoding": {
"x": {"field": "temp_min", "bin": true},
"y": {"field": "temp_max", "bin": true},
"size": {"aggregate": "count"}
}
},
{
"mark": "point",
"encoding": {
"x": {"field": "temp_min", "bin": true},
"y": {"field": "temp_max", "bin": true},
"size": {"aggregate": "count"}
}
}
]
}

Related

How to position image on top left corner?

I am trying to position an overlayed image in the top left corner of my chart.
To that end, I am using the aggregate transform directly in the encoding of channels x, and y of the image, as it can be seen in this example.
Here is the specific code snippet of the image mark:
{
"mark": {"type":"image", "width":50, "align":"left"},
"encoding": {
"x": {"aggregate": "min", "field":"months", "type":"temporal"},
"y": {"aggregate": "max", "field":"price", "type":"quantitative"},
"url": {"value": "data/ffox.png"}
}
}
The image, is still appearing at around the middle of the y axis, and not at the top:
Image in the middle, not top
How can I place it at the top left corner?
The image is not getting displayed in top because height is not provided for that image mark. Because of that image is getting displayed in center, if you inspect your image you will see some default height. Image for default height
To bring your image on top left, just provide height as done below:
{
"mark": {"type": "image", "width": 50, "align": "left", "height": 40},
"encoding": {
"x": {"aggregate": "min", "field": "months", "type": "temporal"},
"y": {"aggregate": "max", "field": "price", "type": "quantitative"},
"url": {"value": "data/ffox.png"}
}
}
But with the aggregation specified, your image might slighly go outside the chart view, so you can provide the following way as well where you provide hardcoded x and y value for the image mark. Which will always show appropriate position for your image. Below is the code and editor url:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.1.0.json",
"width": "container",
"data": {"url": "data/sp500.csv"},
"transform": [{"timeUnit": "utcyearmonth", "field": "date", "as": "months"}],
"layer": [
{
"encoding": {
"x": {"field": "months", "type": "temporal", "title": null},
"y": {"field": "price", "type": "quantitative", "title": "Price"}
},
"layer": [
{
"mark": {
"type": "area",
"color": "#3873b9",
"fillOpacity": 0.7,
"tooltip": true
}
},
{"mark": {"type": "line", "color": "#4883c9"}}
]
},
{
"mark": {"type": "image", "width": 50, "height": 40, "align": "left"},
"encoding": {
"x": {
"datum": 0,
"type": "quantitative",
"scale": {"domain": [0, 1]},
"axis": null
},
"y": {
"datum": 0.8,
"type": "quantitative",
"scale": {"domain": [0, 1]},
"axis": null
},
"url": {"value": "data/ffox.png"}
}
}
],
"resolve": {
"axis": {"x": "independent", "y": "independent"},
"scale": {"x": "independent", "y": "independent"}
}
}
As wahab memon mentioned, the image went middle due to the missing of height property.
Simply adding back "height": 50, "baseline": "alphabetic" to mark can fix the position and the overshoot issue as well. (no need for hard coding)
Last but not least, the original code has redundant nested layers, please see the complete code below:
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.1.0.json",
"width": "container",
"data": {"url": "data/sp500.csv"},
"transform": [{"timeUnit": "utcyearmonth", "field": "date", "as": "months"}],
"encoding": {
"x": {"field": "months", "type": "temporal", "title": null},
"y": {"field": "price", "type": "quantitative", "title": "Price"}
},
"layer": [
{
"mark": {
"type": "area",
"color": "#3873b9",
"fillOpacity": 0.7,
"tooltip": true
}
},
{"mark": {"type": "line", "color": "#4883c9"}},
{
"mark": {"type": "image", "width": 50, "align": "left", "height": 50, "baseline": "alphabetic"},
"encoding": {
"x": {"aggregate": "min", "field": "months", "type": "temporal"},
"y": {"aggregate": "max", "field": "price", "type": "quantitative"},
"url": {"value": "data/ffox.png"}
}
}
]
}
Vega Editor

How to zoom only on a specific axis when mouse over it?

Starting from this concrete example where 2 plots are linked on their horizontal axis and free on their vertical axis, is there a way to zoom only on a specific axis when the mouse is over it ?
The zoom actually set is common to both axis.
If you want scrolling to be independent in each panel, you need to do two things:
set resolve.scale.x and resolve.scale.y to "independent", so the scales will not be linked.
use a different name for the bound selection in each panel, so the selectors will not be linked.
For example (vega editor):
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"url": "data/stocks.csv"},
"vconcat": [
{
"transform": [{"filter": "datum.symbol==='IBM'"}],
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"}
},
"selection": {"scroll_1": {"type": "interval", "bind": "scales"}}
},
{
"transform": [{"filter": "datum.symbol==='GOOG'"}],
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"}
},
"selection": {"scroll_2": {"type": "interval", "bind": "scales"}}
}
],
"resolve": {"scale": {"x": "independent", "y": "independent"}}
}

Minimum value for y-axis in Vega lite

I have a line chart with stock ticks based on this examples: https://vega.github.io/vega-lite/examples/interactive_multi_line_pivot_tooltip.html
I'm trying to set a min. val. for the y-axis and have tried the below:
"encoding": {
"x": {
"field": "date",
"type": "temporal"
},
"y": {
"field": "price",
"type": "quantitative",
"scale": {"domain": [150,350]},
},
},
This works with negative values like -150 but not positive. Also my tooltip disappears when setting the y variables..
Setting the scale domain appears to work with the example you linked to (editor):
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"url": "data/stocks.csv"},
"width": 400,
"height": 300,
"encoding": {"x": {"field": "date", "type": "temporal"}},
"layer": [
{
"encoding": {
"color": {"field": "symbol", "type": "nominal"},
"y": {
"field": "price",
"type": "quantitative",
"scale": {"domain": [100, 500]}
}
},
"layer": [
{"mark": {"type": "line", "clip": true}},
{
"transform": [{"filter": {"selection": "hover"}}],
"mark": {"type": "point", "clip": true}
}
]
},
{
"transform": [{"pivot": "symbol", "value": "price", "groupby": ["date"]}],
"mark": "rule",
"encoding": {
"opacity": {
"condition": {"value": 0.3, "selection": "hover"},
"value": 0
},
"tooltip": [
{"field": "AAPL", "type": "quantitative"},
{"field": "AMZN", "type": "quantitative"},
{"field": "GOOG", "type": "quantitative"},
{"field": "IBM", "type": "quantitative"},
{"field": "MSFT", "type": "quantitative"}
]
},
"selection": {
"hover": {
"type": "single",
"fields": ["date"],
"nearest": true,
"on": "mouseover",
"empty": "none",
"clear": "mouseout"
}
}
}
]
}
Note that I also set the clip mark property to hide marks outside the axis boundaries.

Brush zooming in X and Y in Vega-lite

In Vega-lite, is it possible to zoom in to a plot with a brush in the X and Y direction at the same time?
Using this example as a base:
https://vega.github.io/vega-lite/examples/interactive_overview_detail.html
I tried encoding the Y, but I'm not sure how to point "scale": {"domain": {"selection": "brush"}} in the Y axis direction.
unexpected result
If not, is it possible to achieve similar results with the "bind": "scales"? The goal is to have a "key-map" of the chart with a zoom-in, and a small box showing where the zoom is on the broader time-series.
Code I've been trying with that example:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"url": "data/sp500.csv"},
"vconcat": [{
"width": 480,
"mark": "area",
"encoding": {
"x": {
"field": "date",
"type": "temporal",
"scale": {"domain": {"selection": "brush"}},
"axis": {"title": ""}
},
"y": {"field": "price",
"type": "quantitative",
"scale": {"domain": {"selection": "brush"}}
}
}
}, {
"width": 480,
"height": 60,
"mark": "area",
"selection": {
"brush": {"type": "interval", "encodings": ["x","y"]}
},
"encoding": {
"x": {
"field": "date",
"type": "temporal"
},
"y": {
"field": "price",
"type": "quantitative",
"axis": {"tickCount": 3, "grid": false}
}
}
}]
}`
You can do this by specifying the field or the encoding within the domain; for example:
"domain": {"selection": "brush", "encoding": "y"}
Putting this into your example looks like this (view live):
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"data": {"url": "data/sp500.csv"},
"vconcat": [
{
"width": 480,
"mark": "area",
"encoding": {
"x": {
"field": "date",
"type": "temporal",
"scale": {"domain": {"selection": "brush", "encoding": "x"}},
"axis": {"title": ""}
},
"y": {
"field": "price",
"type": "quantitative",
"scale": {"domain": {"selection": "brush", "encoding": "y"}}
}
}
},
{
"width": 480,
"height": 60,
"mark": "area",
"selection": {"brush": {"type": "interval", "encodings": ["x", "y"]}},
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {
"field": "price",
"type": "quantitative",
"axis": {"tickCount": 3, "grid": false}
}
}
}
]
}

Is there a way to add a line break between the plot and X axis (vega-lite)

I am not sure if it makes sense but will it be possible to add a line break between the start of the plot and the X-axis.
For e.g.
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": {"step": 10},
"height": 120,
"data": {"url": "data/cars.json"},
"mark": "area",
"encoding": {
"x": {"field": "Name", "type": "nominal", "scale": {"round": false}},
"y": {"aggregate": "count", "type": "quantitative"}
}
}
Hopeful output:
One way to do this is by adding a scale.domain argument:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": {"step": 10},
"height": 120,
"data": {"url": "data/cars.json"},
"mark": "area",
"encoding": {
"x": {"field": "Name", "type": "nominal", "scale": {"round": false}},
"y": {"aggregate": "count", "type": "quantitative", "scale": {"domain": [-0.5, 6]}}
}
}
Another way to do this is to use the y and y2 encodings to set a bottom value for the area chart:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": {"step": 10},
"height": 120,
"data": {"url": "data/cars.json"},
"mark": "area",
"transform": [{"calculate": "0.1", "as": "bottom"}],
"encoding": {
"x": {"field": "Name", "type": "nominal", "scale": {"round": false}},
"y": {"aggregate": "count", "type": "quantitative"},
"y2": {"field": "bottom"}
}
}
Another way to do something similar, adding it as another answer to make it convenient for others.
offset property of axis shifts the axis line by fixed pixels, independent of the data. Using offset adds a gap between the x and y axis lines though.
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"width": {"step": 10},
"height": 120,
"data": {"url": "data/cars.json"},
"mark": "area",
"encoding": {
"x": {"field": "Name", "type": "nominal", "scale": {"round": false}, "axis": {"offset": 5}},
"y": {"aggregate": "count", "type": "quantitative"}
}
}