Vega-Lite: How do I include image marks in a doughnut chart? - vega-lite

I would like to have image marks surrounding my doughnut chart instead of texts. The example for image marks use x and y for its coordinate. How should I adjust that for a doughnut chart where we work with radius and theta?
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple pie chart with labels.",
"data": {
"values": [
{"category": "a", "value": 4, "image": url},
{"category": "b", "value": 6, "image": url},
{"category": "c", "value": 10, "image": url},
{"category": "d", "value": 3, "image": url},
{"category": "e", "value": 7, "image": url},
{"category": "f", "value": 8, "image": url}
]
},
"encoding": {
"theta": {"field": "value", "type": "quantitative", "stack": true},
"color": {"field": "category", "type": "nominal", "legend": null}
},
"layer": [{
"mark": {"type": "arc", "outerRadius": 80}
}, {
"mark": {"type": "text", "radius": 90},
"encoding": {
"text": {"field": "category", "type": "nominal"}
}
}],
"view": {"stroke": null}
}
New vega version:
Open the Chart in the Vega Editor

After some trials and reading through the doc, it seems Image Mark cannot be positioned by theta encoding, but the example shows that x and y encodings are supported.
Therefore, I worked out this positioning via simple trigonometry and an extra layer to place the images in the doughnut:
{
"transform": [
{"joinaggregate": [{"op":"sum", "field": "value", "as": "total"}]},
{
"window": [{"op": "sum", "field": "value", "as": "cum"}],
"frame": [null, 0]
},
{"calculate": "cos(2*PI*(datum.cum-datum.value/2)/datum.total)", "as": "y"},
{"calculate": "sin(2*PI*(datum.cum-datum.value/2)/datum.total)", "as": "x"}
],
"mark": {"type": "image", "width": 20, "height": 20},
"encoding": {
"url": {"field": "image"},
"x": {"field": "x", "type": "quantitative", "scale": {"domain": [-2, 2]}, "axis": null},
"y": {"field": "y", "type": "quantitative", "scale": {"domain": [-2, 2]}, "axis": null}
}
}
Yet another Vega Editor
As the order is messed up by the color encoding mentioned in comments below, a new window transform is added to generate an extra ordering field which is provided to color field
Renewed Vega Editor
3 changes were made: (2021-07-16)
Using cos in the calculate of y
Using sin in the calculate of x
Messing up the data value to check if working
Old & Wrong Vega Editor

Related

How to Annotate a Line in line Chart in Vega-Lite?

How to annotate a line in line chart in vega-lite
For the below code https://vega.github.io/editor/#/
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"layer": [
{
"data": {"url": "data/stocks.csv"},
"mark": "line",
"encoding": {
"x": {"field": "date", "type": "temporal"},
"y": {"field": "price", "type": "quantitative"},
"color": {"field": "symbol", "type": "nominal"}
}
},
{
"data": {"values": [{}]},
"mark": {"type": "rule", "strokeDash": [2, 2], "size": 2},
"encoding": {"x": {"datum": {"year": 2006}}}
}
]
}
We get plot
If I want to annotate the line at a specific position like (2004,400)
I tried this, it is working, but I don't want to pass hardcoded values like "a": 2004, "b": 400,
{
"data": {
"values": [
{"a": 2004, "b": 400}
]
},
"mark": {"type": "text", "fontSize" : 16, "fontWeight":"bold", "align" : "left"},
"encoding": {
"text": {"value": "Optimum"},
"x": {"field": "a", "type": "quantitative", "title":""},
"y": {"field": "b", "type": "quantitative", "title":""}
}
},
How to pass specific values from the data like average value of date (say:2004) and average value of price (say:400)?
or
just next to the line in the middle of y-axis
Transform and Aggregate Worked, I needed only for Y axis average position, so below code worked good. For another axis we can use same transform.
{
"mark": {"type": "text", "fontSize" : 16, "fontWeight":"bold", "align" : "left"},
"transform": [
{
"aggregate": [{
"op": "mean",
"field": "price",
"as": "mean_y_axis"
}],
"groupby": ["date"]
}
],
"encoding": {
"text": {"value": "Optimum"},
"x": {"field": "date",
"type": "quantitative"},
"y": {"field": "mean_y_axis",
"type": "quantitative"}
}
}

Vega Lite: Normalized Stacked Bar Chart + Overlay percentages as text

I have a stacked normalized bar chart similar to this:
https://vega.github.io/editor/#/examples/vega-lite/stacked_bar_normalize
I'm trying to show the related percentages (per bar segment) as text on the bars similar to: https://gist.github.com/pratapvardhan/00800a4981d43a84efdba0c4cf8ee2e1
I tried adding a transform field to calculate the percentages, but still couldn't get it to work after hours of trying.
I'm lost help 🥺
My best try:
{
"description":
"A bar chart showing the US population distribution of age groups and gender in 2000.",
"data": {
"url": "data/population.json"
},
"transform": [
{"filter": "datum.year == 2000"},
{"calculate": "datum.sex == 2 ? 'Female' : 'Male'", "as": "gender"},
{
"stack": "people",
"offset": "normalize",
"as": ["v1", "v2"],
"groupby": ["age"],
"sort": [{"field": "gender", "order": "descending"}]
}
],
"encoding": {
"y": {
"field": "v1",
"type": "quantitative",
"title": "population"
},
"y2": {"field": "v2"},
"x": {
"field": "age",
"type": "ordinal"
},
"color": {
"field": "gender",
"type": "nominal",
"scale": {
"range": ["#675193", "#ca8861"]
}
}
},
"layer":[
{ "mark": "bar"},
{"mark": {"type": "text", "dx": 0, "dy": 0},
"encoding": {
"color":{"value":"black"},
"text": { "field": "v1", "type": "quantitative", "format": ".1f"}}
}
]
}
You can use a joinaggregate transform to normalize each group, and then use "format": ".1%" to display fractions as percents. Using this, there is no need to manually compute the stack transform; it is simpler to specify the stack via the encoding, as in the example you linked to.
Here is the result (open in editor):
{
"description": "A bar chart showing the US population distribution of age groups and gender in 2000.",
"data": {"url": "data/population.json"},
"transform": [
{"filter": "datum.year == 2000"},
{"calculate": "datum.sex == 2 ? 'Female' : 'Male'", "as": "gender"},
{
"joinaggregate": [{"op": "sum", "field": "people", "as": "total"}],
"groupby": ["age"]
},
{"calculate": "datum.people / datum.total", "as": "fraction"}
],
"encoding": {
"y": {
"aggregate": "sum",
"field": "people",
"title": "population",
"stack": "normalize"
},
"order": {"field": "gender", "sort": "descending"},
"x": {"field": "age", "type": "ordinal"},
"color": {
"field": "gender",
"type": "nominal",
"scale": {"range": ["#675193", "#ca8861"]}
}
},
"layer": [
{"mark": "bar"},
{
"mark": {"type": "text", "dx": 20, "dy": 0, "angle": 90},
"encoding": {
"color": {"value": "white"},
"text": {"field": "fraction", "type": "quantitative", "format": ".1%"}
}
}
]
}

How to create relative mark positions in Vega LIte

I have a pie chart with width & height set to "container". I would like to label each slice of the pie. Therefore I included a layer and it creates the correct text. However, I don't know how to implement a relative radius size. How would you go about it?
With an absolute radius (e.g. 30) it works, but I need a relative position.
`"layer": [{"mark": {"type": "arc"}},
{"mark": {"type": "text", "radius":30},
"encoding": {"text": {"field": "*", "type": "nominal"}}
}]`
You can create relative radii using an Expression Reference for the radius value.
For example, here is a chart where the radius of the pie chart is set to 40% of the chart size, and the text is set at 45% of the chart size (here I chose to measure the chart size as the minimum of the width and height, which seems reasonable for a circular chart):
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "A simple pie chart with labels.",
"data": {
"values": [
{"category": "a", "value": 4},
{"category": "b", "value": 6},
{"category": "c", "value": 10},
{"category": "d", "value": 3},
{"category": "e", "value": 7},
{"category": "f", "value": 8}
]
},
"encoding": {
"theta": {"field": "value", "type": "quantitative", "stack": true}
},
"layer": [
{
"mark": {"type": "arc", "radius": {"expr": "0.4 * min(width, height)"}},
"encoding": {"color": {"field": "category", "type": "nominal", "legend": null}}
},
{
"mark": {"type": "text", "radius": {"expr": "0.45 * min(width, height)"}},
"encoding": {"text": {"field": "category", "type": "nominal"}}
}
],
"view": {"stroke": null}
}
(open in editor)

Is it possible to have columns of donut chart with gray arc indicating the missing part?

Currently, the donut chart assumed that the max value is 100%. What I would like is for the category Chinese to have a percentage of 50% with gray arc indicating the missing 50%. The two other donut charts will behave similarly in their respective column.
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "A simple donut chart with embedded data.",
"data": {
"values": [
{"category": "English", "value": 4},
{"category": "Malay", "value": 6},
{"category": "Chinese", "value": 10}
]
},
"mark": {"type": "arc", "innerRadius": 80},
"encoding": {
"column": {"field": "category"},
"theta": {"field": "value", "type": "quantitative"},
"color": {"field": "category", "type": "nominal"}
},
"view": {"stroke": null}
}
It's a bit more complex, but one way to do this is to use a transform to calculate the total, and then a layer chart to plot that total in the background.
Here is an example (open in editor):
{
"data": {
"values": [
{"category": "English", "value": 4},
{"category": "Malay", "value": 6},
{"category": "Chinese", "value": 10}
]
},
"transform": [
{"joinaggregate": [{"op": "sum", "field": "value", "as": "total"}]}
],
"facet": {"column": {"field": "category"}},
"spec": {
"layer": [
{
"mark": {"type": "arc", "innerRadius": 80, "color": "lightgray"},
"encoding": {"theta": {"field": "total", "type": "quantitative"}}
},
{
"mark": {"type": "arc", "innerRadius": 80},
"encoding": {
"theta": {"field": "value", "type": "quantitative"},
"color": {"field": "category"}
}
}
],
"view": {"stroke": null}
}
}

Sort the stack segments of barchart

I played with stacked bar charts and wanted to create the Spanish flag with Vega Lite.
I specified the stripes height and color in the data but don't manage to sort the individual stacks:
I set the scale to null, so that the color is taken from the specified field.
I encoded the position of the stripe in the pos attribute, and want to sort the segements by that.
I've also tried to slightly vary the color of the red stripes, but that didn't help.
Spec:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "Fun with Flags",
"data": {
"values": [
{"h": 5, "color": "#aa151b", "pos": 6, "country": "spain"},
{"h": 5, "color": "#f1bf00", "pos": 4, "country": "spain"},
{"h": 5, "color": "#aa152b", "pos": 2, "country": "spain"}
]
},
"width": {"step": 300},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "country", "type": "nominal"},
"y": {"field": "h", "type": "quantitative"},
"color": {
"field": "color",
"scale": null,
"type": "nominal",
"sort": {"field": "pos", "op": "min", "order": "descending"}
}
}
}
Here's a link to the Vega Editor with the Spec.
Instead of sorting the color encoding, I had to specify the order channel, as described in the docs.
This gives me the following spec:
{
"$schema": "https://vega.github.io/schema/vega-lite/v4.json",
"description": "Fun with Flags",
"data": {
"values": [
{"h": 5, "color": "#aa152b", "pos": 6, "country": "spain"},
{"h": 5, "color": "#f1bf00", "pos": 4, "country": "spain"},
{"h": 5, "color": "#aa152b", "pos": 2, "country": "spain"}
]
},
"width": {"step": 300},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "country", "type": "nominal"},
"y": {"field": "h", "type": "quantitative"},
"color": {
"field": "color",
"scale": null,
"type": "nominal"
},
"order": {
"field": "pos",
"type": "quantitative"
}
}
}
And correct Spanish flag:
Link to Vega Editor