I have a metric and want to show a gradient color in an area chart corresponding to good (0 to .1 = green), ok (>.1 to .3 = yellow), bad (> .3 = red) for that metric at various time points. How an I make the color cut points relative to the actual scale on the y axis?
I thought I could specify the color breaks with offset and it looked correct with my first pass:
{
"width": 600,
"data": {
"values": [
{
"__row__": 0,
"calendar_month_year": "Dec-23",
"concentration_risk_monthly": 0.51
},
{
"__row__": 1,
"calendar_month_year": "Jan-24",
"concentration_risk_monthly": 0.5
},
{
"__row__": 2,
"calendar_month_year": "Feb-24",
"concentration_risk_monthly": 0.27
},
{
"__row__": 3,
"calendar_month_year": "Mar-24",
"concentration_risk_monthly": 0.22
},
{
"__row__": 4,
"calendar_month_year": "Apr-24",
"concentration_risk_monthly": 0.25
},
{
"__row__": 5,
"calendar_month_year": "May-24",
"concentration_risk_monthly": 0.22
}
]
},
"mark": {
"type": "area",
"color": {
"x1": 1,
"y1": 1,
"x2": 1,
"y2": 0,
"gradient": "linear",
"stops": [
{"offset": 0, "color": "#00800166"},
{"offset": 0.3, "color": "#F7B50066"},
{"offset": 1, "color": "#A9281F66"}
]
}
},
"encoding": {
"x": {
"field": "calendar_month_year",
"type": "ordinal",
"sort": {"field": "__row__"},
"axis": {"title": null},
"scale": {"padding": 0}
},
"y": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"axis": {"title": null}
}
}
}
But if I change the data to be below .05 for that metric (well below the .1 for green):
{
"width": 600,
"data": {
"values": [
{
"__row__": 0,
"calendar_month_year": "Dec-23",
"concentration_risk_monthly": 0.051
},
{
"__row__": 1,
"calendar_month_year": "Jan-24",
"concentration_risk_monthly": 0.05
},
{
"__row__": 2,
"calendar_month_year": "Feb-24",
"concentration_risk_monthly": 0.027
},
{
"__row__": 3,
"calendar_month_year": "Mar-24",
"concentration_risk_monthly": 0.022
},
{
"__row__": 4,
"calendar_month_year": "Apr-24",
"concentration_risk_monthly": 0.025
},
{
"__row__": 5,
"calendar_month_year": "May-24",
"concentration_risk_monthly": 0.022
}
]
},
"mark": {
"type": "area",
"color": {
"x1": 1,
"y1": 1,
"x2": 1,
"y2": 0,
"gradient": "linear",
"stops": [
{"offset": 0.0, "color": "#00800166"},
{"offset": 0.3, "color": "#F7B50066"},
{"offset": 1.0, "color": "#A9281F66"}
]
}
},
"encoding": {
"x": {
"field": "calendar_month_year",
"type": "ordinal",
"sort": {"field": "__row__"},
"axis": {"title": null},
"scale": {"padding": 0}
},
"y": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"axis": {"title": null}
}
}
}
I'd expect the entire area to be green but it seems that the offsets are relative to the overall area and now there are still red areas even though all metrics are below .1:
Just a very minor tweak to Davide's answer.
{
"width": 600,
"data": {
"values": [
{
"__row__": 0,
"calendar_month_year": "Dec-23",
"concentration_risk_monthly": 0.8
},
{
"__row__": 1,
"calendar_month_year": "Jan-24",
"concentration_risk_monthly": 0.7
},
{
"__row__": 2,
"calendar_month_year": "Feb-24",
"concentration_risk_monthly": 0.7
},
{
"__row__": 3,
"calendar_month_year": "Mar-24",
"concentration_risk_monthly": 0.8
},
{
"__row__": 4,
"calendar_month_year": "Apr-24",
"concentration_risk_monthly": 0.8
},
{
"__row__": 5,
"calendar_month_year": "May-24",
"concentration_risk_monthly": 0.8
}
]
},
"transform": [{"extent": "concentration_risk_monthly", "param": "myExtent"}],
"mark": {
"type": "area",
"fill": {
"expr": "{'gradient': 'linear', 'x1':0,'y1':1,'x2':0,'y2':0, 'stops': [{'offset': 0, 'color': scale('stroke', 0)},{'offset': 0.3, 'color': scale('stroke', myExtent[1]>1?1:myExtent[1]>0.3?0.3:0)},{'offset': 1, 'color': scale('stroke', myExtent[1]>=1?1:myExtent[1]>0.3?1:0.3)}]}"
},
"strokeOpacity": 0
},
"encoding": {
"stroke": {
"field": "1",
"legend": null,
"scale": {
"type": "linear",
"range": ["#00800166", "#F7B50066", "#A9281F66"],
"domain": [0, 0.3, 1]
}
},
"x": {
"field": "calendar_month_year",
"type": "ordinal",
"sort": {"field": "__row__"},
"axis": {"title": null},
"scale": {"padding": 0}
},
"y": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"axis": {"title": null}
}
}
}
And just for fun here is a version with colored bands instead of gradient. Now it is much more clear where boundaries are.
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"width": 600,
"data": {
"values": [
{
"__row__": 0,
"calendar_month_year": "Dec-23",
"concentration_risk_monthly": 0.2
},
{
"__row__": 1,
"calendar_month_year": "Jan-24",
"concentration_risk_monthly": 0.1
},
{
"__row__": 2,
"calendar_month_year": "Feb-24",
"concentration_risk_monthly": 0.5
},
{
"__row__": 3,
"calendar_month_year": "Mar-24",
"concentration_risk_monthly": 0.04
},
{
"__row__": 4,
"calendar_month_year": "Apr-24",
"concentration_risk_monthly": 0.5
},
{
"__row__": 5,
"calendar_month_year": "May-24",
"concentration_risk_monthly": 0.2
}
]
},
"transform": [
{"extent": "concentration_risk_monthly", "param": "myExtent"},
{
"joinaggregate": [
{"op": "max", "field": "concentration_risk_monthly", "as": "maxVal"}
]
}
],
"encoding": {
"x": {
"field": "calendar_month_year",
"type": "ordinal",
"sort": {"field": "__row__"},
"axis": {
"title": null,
"domain": false,
"labelAngle": 0,
"labelFontSize": 12
},
"scale": {"padding": 0}
},
"y": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"stack": null,
"axis": {
"title": null,
"ticks": false,
"labels": false,
"domain": false,
"grid": false
}
}
},
"layer": [
{
"transform": [
{
"calculate": "datum.maxVal < 0.3 ? 0.3 : datum.maxVal >= 20 ? 1 : datum.maxVal",
"as": "redStart"
},
{"calculate": "0.3", "as": "redEnd"}
],
"mark": {"type": "area", "color": "#A9281F66", "clip": true},
"encoding": {
"y": {"type": "quantitative", "field": "redStart"},
"y2": {"field": "redEnd"}
}
},
{
"transform": [
{
"calculate": "datum.maxVal < 0.3 ? 0.3 : datum.maxVal >= 0.3 ? 0.3 : datum.maxVal",
"as": "yellowStart"
},
{"calculate": "0.1", "as": "yellowEnd"}
],
"mark": {"type": "area", "color": "#F7B50066", "clip": true},
"encoding": {
"y": {"type": "quantitative", "field": "yellowStart"},
"y2": {"field": "yellowEnd"}
}
},
{
"transform": [
{"calculate": "0.1", "as": "greenStart"},
{"calculate": "0", "as": "greenEnd"}
],
"mark": {
"type": "area",
"color": "#00800166",
"opacity": 1,
"clip": true
},
"encoding": {
"y": {"type": "quantitative", "field": "greenStart"},
"y2": {"field": "greenEnd"}
}
},
{
"mark": {"type": "line", "strokeOpacity": 1},
"encoding": {"color": {"value": "black"}}
},
{
"mark": {"type": "area"},
"encoding": {"color": {"value": "white"}, "y2": {"value": 0}}
},
{
"mark": {
"type": "circle",
"size": 70,
"stroke": "black",
"strokeWidth": 1,
"fill": {
"expr": "datum.concentration_risk_monthly <= 0.1 ? '#38B510' : datum.concentration_risk_monthly <= 0.3 ? '#F3CF26' : '#EC3B15'"
}
},
"encoding": {"opacity": {"value": 1}}
},
{
"mark": {
"type": "rect",
"cornerRadius": 6,
"xOffset": {"expr": "-0"},
"yOffset": {"expr": "-20"},
"height": 20,
"width": 35,
"fill": {
"expr": "datum.concentration_risk_monthly <= 0.1 ? '#38B510' : datum.concentration_risk_monthly <= 0.3 ? '#F3CF26' : '#EC3B15'"
}
}
},
{
"mark": {
"type": "text",
"align": "center",
"baseline": "middle",
"xOffset": {"expr": "1"},
"dy": -19,
"dx": -1
},
"encoding": {
"text": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"format": ".2f"
},
"color": {
"condition": {
"test": "datum.concentration_risk_monthly > 0.1 && datum.concentration_risk_monthly <= 0.3",
"value": "black"
},
"value": "white"
}
}
}
],
"config": {"view": {"stroke": "transparent"}}
}
And one more version with smooth lines and 3 separate gradients:)
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"usermeta": {"embedOptions": {"renderer": "svg"}},
"width": 600,
"data": {
"values": [
{
"__row__": 0,
"calendar_month_year": "Dec-23",
"concentration_risk_monthly": 0.2
},
{
"__row__": 1,
"calendar_month_year": "Jan-24",
"concentration_risk_monthly": 0.1
},
{
"__row__": 2,
"calendar_month_year": "Feb-24",
"concentration_risk_monthly": 0.5
},
{
"__row__": 3,
"calendar_month_year": "Mar-24",
"concentration_risk_monthly": 0.04
},
{
"__row__": 4,
"calendar_month_year": "Apr-24",
"concentration_risk_monthly": 0.5
},
{
"__row__": 5,
"calendar_month_year": "May-24",
"concentration_risk_monthly": 0.2
}
]
},
"transform": [
{"extent": "concentration_risk_monthly", "param": "myExtent"},
{
"joinaggregate": [
{"op": "max", "field": "concentration_risk_monthly", "as": "maxVal"}
]
}
],
"encoding": {
"x": {
"field": "calendar_month_year",
"type": "ordinal",
"sort": {"field": "__row__"},
"axis": {
"title": null,
"domain": false,
"labelAngle": 0,
"labelFontSize": 10
},
"scale": {"padding": 0}
},
"y": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"stack": null,
"axis": {
"title": null,
"ticks": false,
"labels": false,
"domain": false,
"grid": false,
"labelPadding": 5,
"labelFontSize": 10
}
}
},
"layer": [
{
"transform": [
{
"calculate": "datum.maxVal < 0.3 ? 0.3 : datum.maxVal >= 20 ? 1 : datum.maxVal",
"as": "redStart"
},
{"calculate": "0.3", "as": "redEnd"}
],
"mark": {
"type": "area",
"interpolate": "monotone",
"clip": true,
"color": {
"x1": 1,
"y1": 1,
"x2": 1,
"y2": 0.3,
"gradient": "linear",
"stops": [
{"offset": 0, "color": "#FFC600"},
{"offset": 1, "color": "#FE3800"}
]
}
},
"encoding": {
"y": {"type": "quantitative", "field": "redStart"},
"y2": {"field": "redEnd"}
}
},
{
"transform": [
{
"calculate": "datum.maxVal < 0.3 ? 0.3 : datum.maxVal >= 0.3 ? 0.3 : datum.maxVal",
"as": "yellowStart"
},
{"calculate": "0.1", "as": "yellowEnd"}
],
"mark": {
"type": "area",
"interpolate": "monotone",
"clip": true,
"color": {
"x1": 1,
"y1": 1,
"x2": 1,
"y2": 0.1,
"gradient": "linear",
"stops": [
{"offset": 0, "color": "#C9F047"},
{"offset": 1, "color": "#FFC600"}
]
}
},
"encoding": {
"y": {"type": "quantitative", "field": "yellowStart"},
"y2": {"field": "yellowEnd"}
}
},
{
"transform": [
{"calculate": "0.1", "as": "greenStart"},
{"calculate": "0", "as": "greenEnd"}
],
"mark": {
"type": "area",
"interpolate": "monotone",
"opacity": 1,
"clip": true,
"color": {
"x1": 1,
"y1": 1,
"x2": 1,
"y2": 0.1,
"gradient": "linear",
"stops": [
{"offset": 0, "color": "#1AA60C"},
{"offset": 1, "color": "#C9F047"}
]
}
},
"encoding": {
"y": {"type": "quantitative", "field": "greenStart"},
"y2": {"field": "greenEnd"}
}
},
{
"transform": [{"calculate": "0.1", "as": "yellowBorder"}],
"mark": {
"type": "line",
"strokeWidth": 1,
"strokeDash": [2, 2],
"color": "black"
},
"encoding": {"y": {"field": "yellowBorder"}}
},
{
"transform": [{"calculate": "0.3", "as": "redBorder"}],
"mark": {
"type": "line",
"strokeWidth": 1,
"strokeDash": [2, 2],
"color": "black"
},
"encoding": {"y": {"field": "redBorder"}}
},
{
"mark": {
"type": "area",
"interpolate": "monotone",
"stroke": "white",
"strokeWidth": 1
},
"encoding": {"color": {"value": "white"}, "y2": {"value": 0}}
},
{
"mark": {"type": "line", "strokeWidth": 1, "interpolate": "monotone"},
"encoding": {"color": {"value": "black"}}
},
{
"mark": {
"type": "circle",
"size": 70,
"stroke": "black",
"strokeWidth": 1,
"fill": {
"expr": "datum.concentration_risk_monthly <= 0.1 ? '#38B510' : datum.concentration_risk_monthly <= 0.3 ? '#F3CF26' : '#EC3B15'"
}
},
"encoding": {"opacity": {"value": 1}}
},
{
"mark": {
"type": "text",
"align": "left",
"baseline": "middle",
"xOffset": {"expr": "1"},
"dy": 0,
"dx": 12,
"color": "black",
"angle": 270
},
"encoding": {
"text": {
"field": "concentration_risk_monthly",
"type": "quantitative",
"format": ".2f"
}
}
}
],
"config": {"view": {"stroke": "white", "strokeWidth": 3}}
}