Multi values graph do not share same ZERO
Hi all, I have strange problem when using graph with multiple value entity's They are not using same ZERO horizontal line
Plotly was used in Home assistance to draw graph visualisations, developer of integration @dbuezas , recommend to write here
More information can be seen here https://github.com/dbuezas/lovelace-plotly-graph-card/issues/428
Any update over this bug?
I don't see this as a bug. It's perhaps a feature request, to say "these axes should all put zero at the same place," but it's certainly not a requirement that overlaid axes must put zero at the same place. For example on your graph some of the traces have a lot of negative data so zero is appropriately drawn near the top of the graph, and you've got the left axis going from 200-240 so zero on that axis should always be far off-screen.
We have quite a few options for controlling autorange behavior, because there are a lot of use cases here. "match the zero location of another axis" is one we don't support today but it would fit well in that framework. Others you can use today include rangemode: 'tozero' which is nice in that if you don't have any negative data it'll put zero exactly at the bottom, but then as soon as some data goes negative it will expand to show that data, pushing zero off the bottom for that axis.
If we do decide to consider this a feature request, I'll also note that the implementation is ambiguous and we'll need to decide how to handle that. If yaxis has a zero that's lower than yaxis2, do you match them by making yaxis.range[0] more negative, making yaxis2.range[1] more positive, or some of each?
What about doing both? Assuming the plotly uses d3 scales, it could be something like this:
function alignScales(scale1, scale2) {
const range = scale1.range(); // range in pixels for the y axis
const y1_zero = scale1(0);
const y2_zero = scale2(0);
const [d1_min, d1_max] = scale1.domain(); // assuming the domain is the extent of the trace in the y axis
const [d2_min, d2_max] = scale2.domain();
// Find how much to shift each domain so that scale1(0) === scale2(0)
const shift = y2_zero - y1_zero;
const shift1 = shift * (d1_max - d1_min) / (range[1] - range[0]);
const shift2 = shift * (d2_max - d2_min) / (range[1] - range[0]);
// Expand the domains symmetrically
const new_d1_min = Math.min(d1_min - shift1, d1_min);
const new_d1_max = Math.max(d1_max - shift1, d1_max);
const new_d2_min = Math.min(d2_min + shift2, d2_min);
const new_d2_max = Math.max(d2_max + shift2, d2_max);
scale1.domain([new_d1_min, new_d1_max]);
scale2.domain([new_d2_min, new_d2_max]);
}
It would make both scales map to the same y pixel coordinate at zero, while also scaling both so that no data points in either fall outside the plot area.
I bet this gets messier with more zero aligned yaxes, and even more so when zooming and scrolling around.
We don’t use d3 scales but yes, it would need to be something like that and yes more than two axes adds complexity as does interactivity. Also I suspect that precise algorithm isn’t quite what we would want, since if you use symmetric expansion to try and push a zero toward the end it’s already close to it will barely move. In that case you’d be better off only expanding the other axis to push its zero closer to the center.