Seeking help to troubleshoot Vega Lite area chart.I need to get the intersection area between "activity" and "stop" line correctly colored.
I did use transfrom to create two parts of the area to be calculated, with some logic to for choosing which line will be up or down. However, in the area chart, the interpolation of the area doesn't reflect the desired area to be colored.
Below is the vega lite code so far
{
"data":
{
"values": [{"Date":"2024-04-01","Activity":3,"Stops":10},{"Date":"2024-04-02","Activity":6,"Stops":13},{"Date":"2024-04-03","Activity":13,"Stops":11},{"Date":"2024-04-04","Activity":13,"Stops":14},{"Date":"2024-04-05","Activity":12,"Stops":16},{"Date":"2024-04-06","Activity":6,"Stops":11},{"Date":"2024-04-07","Activity":9,"Stops":8},{"Date":"2024-04-08","Activity":10,"Stops":9},{"Date":"2024-04-09","Activity":11,"Stops":5},{"Date":"2024-04-10","Activity":20,"Stops":10},{"Date":"2024-04-11","Activity":9,"Stops":6},{"Date":"2024-04-12","Activity":15,"Stops":8},{"Date":"2024-04-13","Activity":17,"Stops":4},{"Date":"2024-04-14","Activity":7,"Stops":5},{"Date":"2024-04-15","Activity":5,"Stops":8},{"Date":"2024-04-16","Activity":14,"Stops":4},{"Date":"2024-04-17","Activity":18,"Stops":3},{"Date":"2024-04-18","Activity":13,"Stops":13},{"Date":"2024-04-19","Activity":13,"Stops":5},{"Date":"2024-04-20","Activity":14,"Stops":3},{"Date":"2024-04-21","Activity":10,"Stops":8},{"Date":"2024-04-22","Activity":15,"Stops":12},{"Date":"2024-04-23","Activity":15,"Stops":6},{"Date":"2024-04-24","Activity":19,"Stops":9},{"Date":"2024-04-25","Activity":6,"Stops":9},{"Date":"2024-04-26","Activity":16,"Stops":6},{"Date":"2024-04-27","Activity":11,"Stops":3},{"Date":"2024-04-28","Activity":9,"Stops":10},{"Date":"2024-04-29","Activity":14,"Stops":6},{"Date":"2024-04-30","Activity":19,"Stops":9}]
},
"width":500,
"height":200,
"layer": [
{
"mark": {
"type": "line",
"point":{
"filled":true,
"fill":"#20419A"
},
"color":"#20419A",
"opacity": 1,
"tooltip": true
},
"encoding": {
"y": {
"field": "Activity"
}
}
},
{
"mark": {
"type": "line",
"point":{
"filled":true,
"fill":"#00A19C"
},
"color":"#00A19C",
"opacity": 1,
"tooltip": true
},
"encoding": {
"y": {
"field": "Stops"
}
}
},
{
"transform":[
{
"calculate":"datum['Stops']<=datum['Activity']?datum['Stops']:datum['Activity']", "as":"MinY"
}
],
"mark": {
"type": "area",
"line":true,
"point":true,
"color":"red", "opacity": 1,
"tooltip":true
},
"encoding": {
"y": {
"field": "Activity"
},
"y2": {
"field": "MinY"
}
}
},
{
"transform":[
{
"calculate":"datum['Activity']<=datum['Stops']?datum['Activity']:datum['Stops']", "as":"MinY1"
}
],
"mark": {
"type": "area",
"line":true,
"point":true,
"interpolate":"linear",
"color":"#00A19C", "opacity": 1,
"tooltip":true
},
"encoding": {
"y": {
"field": "Stops"
},
"y2": {
"field": "MinY1"
}
}
}
],
"encoding": {
"x": {
"field": "Date",
"type": "temporal",
"axis": {
"labelFontSize": 8,
"labelFont": "sans-serif",
"labelFontWeight": "bold",
"labelAngle": 0,
"title":"Date",
"format":"%_d %b"
}
},
"y": {
"type": "quantitative",
"axis": {
"labelFontSize": 12,
"labelFont": "sans-serif",
"labelFontWeight": "bold",
"title": null,
"domain":false,
"grid":false,
"ticks":false,
"labels":false
}
}
},
"title": {
"text": [
"XXXXX"
],
"subtitle": "Operations",
"font": "sans-serif",
"align": "left",
"anchor": "start",
"dx": 0,
"fontSize": 18,
"subtitleFontSize": 12
}
}
[text]
This is the code in vega lite with the transform calculation to create red and green area Link to editor Note that, the colored areas not matching as the expected "intersection" as in the following area chart (without transfrom calculation used). Link to editor
Area between "blue" and "green" line will be "red". Area between "green" and "blue" line will be green Desired Outcome
Update. Solved following material from @APB Report. The following is the final code:
{
"config": {
"view": {"stroke": "transparent"},
"font": "Segoe UI",
"axis": {
"title": null,
"grid": false,
"ticks": false,
"labelPadding": 10,
"labelFontSize": 12
},
"area": {
"opacity": 1.0 },
"axisY": {"domain": false},
"style": {
"delta_negative": {
"color": "#FF0000"
},
"delta_positive": {
"color": "#00A19C"
},
"mask_foreground": {
"color": "#ffffff",
"stroke": "#ffffff"
},
"Activity_line": {
"color": "#763F98",
"strokeWidth": 2
},
"stops_line": {
"color": "#00A19C",
"strokeWidth": 3
}
}
},
"width":500,
"height":250,
"data": {
"values": [
{"Date": "2024-04-01", "Activity": 3, "Stops": 10},
{"Date": "2024-04-02", "Activity": 6, "Stops": 13},
{"Date": "2024-04-03", "Activity": 13, "Stops": 11},
{"Date": "2024-04-04", "Activity": 13, "Stops": 14},
{"Date": "2024-04-05", "Activity": 12, "Stops": 16},
{"Date": "2024-04-06", "Activity": 6, "Stops": 11},
{"Date": "2024-04-07", "Activity": 9, "Stops": 8},
{"Date": "2024-04-08", "Activity": 10, "Stops": 9},
{"Date": "2024-04-09", "Activity": 11, "Stops": 5},
{"Date": "2024-04-10", "Activity": 20, "Stops": 10},
{"Date": "2024-04-11", "Activity": 9, "Stops": 6},
{"Date": "2024-04-12", "Activity": 15, "Stops": 8},
{"Date": "2024-04-13", "Activity": 17, "Stops": 4},
{"Date": "2024-04-14", "Activity": 7, "Stops": 5},
{"Date": "2024-04-15", "Activity": 5, "Stops": 8},
{"Date": "2024-04-16", "Activity": 14, "Stops": 4},
{"Date": "2024-04-17", "Activity": 18, "Stops": 3},
{"Date": "2024-04-18", "Activity": 13, "Stops": 13},
{"Date": "2024-04-19", "Activity": 13, "Stops": 5},
{"Date": "2024-04-20", "Activity": 14, "Stops": 3},
{"Date": "2024-04-21", "Activity": 10, "Stops": 8},
{"Date": "2024-04-22", "Activity": 15, "Stops": 12},
{"Date": "2024-04-23", "Activity": 15, "Stops": 6},
{"Date": "2024-04-24", "Activity": 19, "Stops": 9},
{"Date": "2024-04-25", "Activity": 6, "Stops": 9},
{"Date": "2024-04-26", "Activity": 16, "Stops": 6},
{"Date": "2024-04-27", "Activity": 11, "Stops": 3},
{"Date": "2024-04-28", "Activity": 9, "Stops": 10},
{"Date": "2024-04-29", "Activity": 14, "Stops": 6},
{"Date": "2024-04-30", "Activity": 19, "Stops": 9}
]
},
"encoding": {
"x": {
"field": "Date",
"type": "temporal",
"axis": {
"zindex": 1,
"format": "%_d-%b"
}
},
"y": {
"type": "quantitative",
"axis": {"tickCount": 5}
}
},
"layer": [
{
"description": "Target area - background",
"mark": {
"type": "area",
"style": "delta_negative",
"tooltip":true
},
"encoding": {
"y": {"field": "Activity"}
}
},
{
"description": "Actual area - masks out target where necessary",
"mark": {
"type": "area",
"style": "delta_positive",
"tooltip":true
},
"encoding": {
"y": {"field": "Stops"}
}
},
{
"description": "Masking layer (with interpolated points)",
"transform": [
{
"calculate": "min(datum['Activity'], datum['Stops'])",
"as": "lowest_value"
},
{
"window": [
{
"op": "lead",
"field": "Date",
"as": "date_following"
},
{
"op": "lead",
"field": "Stops",
"as": "stops_following"
},
{
"op": "lead",
"field": "Activity",
"as": "activity_following"
}
]
},
{
"calculate": "(datum['stops_following'] - datum['Stops']) / (datum['date_following'] - datum['Date'])",
"as": "stops_slope"
},
{
"calculate": "(datum['activity_following'] - datum['Activity']) / (datum['date_following'] - datum['Date'])",
"as": "activity_slope"
},
{
"calculate": "datum['Stops'] - (datum['stops_slope'] * datum['Date'])",
"as": "stops_y_intercept"
},
{
"calculate": "datum['Activity'] - (datum['activity_slope'] * datum['Date'])",
"as": "activity_y_intercept"
},
{
"calculate": "(datum['activity_y_intercept'] - datum['stops_y_intercept']) / (datum['stops_slope'] - datum['activity_slope'])",
"as": "intersect_base"
},
{
"calculate": "datetime(datum['intersect_base']) > datum['Date'] && datum['intersect_base'] < datum['date_following']",
"as": "intersect_before_following"
},
{
"calculate": "datum['intersect_before_following'] ? datetime(datum['intersect_base']) : null",
"as": "intersect_x"
},
{
"calculate": "datum['intersect_before_following'] ? (datum['stops_slope'] * datum['intersect_base']) + datum['stops_y_intercept'] : null",
"as": "intersect_y"
},
{
"fold": [
"Date",
"intersect_x"
]
},
{
"filter": "datum['value'] !== null"
},
{
"calculate": "datum['key'] === 'Date' ? datum['Date'] : datum['intersect_x']",
"as": "x"
},
{
"calculate": "datum['key'] === 'Date' ? datum['lowest_value'] : datum['intersect_y']",
"as": "y"
}
],
"mark": {
"type": "area",
"style": "mask_foreground"
},
"encoding": {
"x": {"field": "x"},
"y": {"field": "y"}
}
},
{
"description": "Activity line",
"mark": {
"type": "line",
"style": "Activity_line",
"tooltip":true
},
"encoding": {
"y": {"field": "Activity"}
}
},
{
"description": "Stops line",
"mark": {
"type": "line",
"style": "stops_line",
"tooltip":true
},
"encoding": {
"y": {"field": "Stops"}
}
}
],
"title": {
"text": [
"XXXXX"
],
"subtitle": "Operations",
"font": "sans-serif",
"align": "left",
"anchor": "start",
"dx": 0,
"fontSize": 18,
"subtitleFontSize": 12
}
}
I recommend you have a read of Daniel Marsh-Patrick's very insightful analysis here:
A Study on Hills and Valleys in Power BI with Deneb:
https://coacervo.co/deneb_hill_valley
You need to calculate where the lines intersect in a better way. I have provided a similar answer a while back how to do this with Javascript.