Forum Discussion
Dynamic y-Axis calculation with Deneb
- 1 year ago
Hi distra,
The area mark is designed to have a zero baseline, so even if you cut the domain, it is trying to plot the remainder of the shape outside the domain (as the second y-value is zero). It would work with a line mark as there is only a single y-coordinate to worry about.
So, what is happening here is that your y-scale and axis are being correctly set to the desired range, but the size of your chart is condensing the vertical scale into the equivalent amount of space. You can see it "kind of" working if you make the visual container larger, e.g.:
(ignore the 'undefined'; that's a copy/paste issue with your sample data at my end)
One way to resolve this is to instruct Vega-Lite to clip the area mark. You should be able to make the following changes to your spec:
1. Update the scale as follows (removing "zero": false), e.g.:
{ ... "encoding": { "y": { "field": "Avg Rate dynamic Currency", "type": "quantitative", "scale": { "domain": [80, 235] // or whatever you need min/max to be }, ... }, ... } ... }2. Update the area mark definition to include a clip as follows:
{ ... "layer": [ { "description": "sparkline definition", "mark": { "type":"area", "clip": true }, ... }, ... } ... }This will now cut the baseline of the plotted area, e.g.:
Hopefully this should be all you need to resolve. Good luck!
(and thanks for using Deneb!)
Daniel
Hi distra,
The area mark is designed to have a zero baseline, so even if you cut the domain, it is trying to plot the remainder of the shape outside the domain (as the second y-value is zero). It would work with a line mark as there is only a single y-coordinate to worry about.
So, what is happening here is that your y-scale and axis are being correctly set to the desired range, but the size of your chart is condensing the vertical scale into the equivalent amount of space. You can see it "kind of" working if you make the visual container larger, e.g.:
(ignore the 'undefined'; that's a copy/paste issue with your sample data at my end)
One way to resolve this is to instruct Vega-Lite to clip the area mark. You should be able to make the following changes to your spec:
1. Update the scale as follows (removing "zero": false), e.g.:
{
...
"encoding": {
"y": {
"field": "Avg Rate dynamic Currency",
"type": "quantitative",
"scale": {
"domain": [80, 235] // or whatever you need min/max to be
},
...
},
...
}
...
}
2. Update the area mark definition to include a clip as follows:
{
...
"layer": [
{
"description": "sparkline definition",
"mark": {
"type":"area",
"clip": true
},
...
},
...
}
...
}
This will now cut the baseline of the plotted area, e.g.:
Hopefully this should be all you need to resolve. Good luck!
(and thanks for using Deneb!)
Daniel
Hi Daniel,
thank you for reaching out, this works like a charm! I used it with the same boundaries I had provided for the example chart with native Power BI visuals and it looks great!
I was expecting it to be a simple task to replace the static values by my pre-calculated values, but it seems like Vega-Lite won't let me use the calculations in the domain directly.
I have made the following update to my transform section. Is there a way to directly reference my calculated upper and lower boundaries in the domain?
"transform": [
{
"joinaggregate": [
{
"op": "max",
"field": "Month",
"as": "max_period"
},
{
"op": "max",
"field": "Avg Rate dynamic Currency",
"as": "max_avg_rate"
},
{
"op": "min",
"field": "Avg Rate dynamic Currency",
"as": "min_avg_rate"
}
]
},
{
"calculate": "datum.max_avg_rate - datum.min_avg_rate",
"as": "delta_max_min"
},
{
"calculate": "datum.max_avg_rate + datum.delta_max_min",
"as": "upper_limit_yAxis"
},
{
"calculate": "datum.min_avg_rate - datum.delta_max_min",
"as": "lower_limit_yAxis"
}
],
Thanks a lot!
Dirk
- dm-p1 year agoSuper User
I'm a bit short on time as it's later here, but you can do this with the extent transform, which will let you hoist your min/max to a top-level signal (called params in Vega-Lite). These are easier to access in expressions, which you can use in the scale domain.
Here's a quick, minimal example that you can use as a reference. This clips the area to the extents of the values (in conjunction with the clip approach discussed previously):
{ "$schema": "https://vega.github.io/schema/vega-lite/v6.json", "data": { "url": "data/stocks.csv" }, "transform": [ { "filter": "datum.symbol==='GOOG'" }, { "extent": "price", "param": "y_extents" } ], "mark": { "type": "area", "clip": true }, "encoding": { "x": { "field": "date", "type": "temporal" }, "y": { "field": "price", "type": "quantitative", "scale": { "domain": [{"expr": "y_extents[0]"}, {"expr": "y_extents[1]"}] } } } }You can explore this in the Vega Editor here.
The pertinent parts are as follows:
{ ... "transform": [ ... { "extent": "price", "param": "y_extents" } ], ... }This is a calculation that will take the min/max of a specified field and store them as a variable. You can see this in the Signal pane (in either Deneb or the Vega Editor), e.g.:
This is an array that can then be referenced in the domain evaluation, e.g.:
{ "encoding": { ... "y": { ... "scale": { "domain": [ {"expr": "y_extents[0]"}, {"expr": "y_extents[1]"} ] } } } }This is an example of extraction of the direction values, but you could also use downstream params to manipulate these values (such as adding a padding factor). this is similar to a transform in a data stream.
Anyway, I hope this is clear and provides you with some ideas on how to apply it to your design. If you need me to implement your specification exactly, it would be helpful if you could supply an example .pbix file, and I can review it when I'm next back online.
Good luck!
Daniel
- distra1 year agoFrequent Visitor
Daniel, you're my hero!
By replacing my existing min and max calculations with the proposed y_extents of my field
Avg Rate dynamic Currency and recreating my calculations for upper and lower boundaries directly in the expression for the domain, it works exactly as intended.I already tested it, I can toggle through all currencies used in our organization and the chart will perfectly adapt the axis to the right values in that currency, zoomed into the desired area!
Thank you so much, have a good rest!- dm-p1 year agoSuper User
Glad it's working for you! It occurred to me (after some sleep) that you might be able to set the y2 encoding channel for the area mark to your derived minimum value from the join aggregate, which would minimize the number of moving parts. You would still need to set the y-scale's zero to false, but it works just as well without needing to apply the extents and expressions to the domain. Here's a quick revision using the above example:
{ "$schema": "https://vega.github.io/schema/vega-lite/v6.json", "data": { "url": "data/stocks.csv" }, "transform": [ { "filter": "datum.symbol==='GOOG'" }, { "joinaggregate": [ { "op": "min", "field": "price", "as": "min_price" } ] } ], "mark": { "type": "area" }, "encoding": { "x": { "field": "date", "type": "temporal" }, "y": { "field": "price", "type": "quantitative", "scale": { "zero": false } }, "y2": { "field": "min_price" } } }And here it is in the Vega Editor.
Cheers,
Daniel