leeoniya / uPlot

đŸ“ˆ A small, fast chart for time series, lines, areas, ohlc & bars
MIT License
8.51k stars 371 forks source link

Synchronizing Y-axis #433

Closed hugovoerman closed 3 years ago

hugovoerman commented 3 years ago

I must be overlooking some issue in which this is described, but I can't find it.

I have several similar graphs with just one y-axis. What I would like is to sync the y-axis over these graphs so that they are all the same. Is there a way to do that, like the way to sync the cursor over graphs? Or should I loop through all relevant values, picking the highest value and then manually set the y-axis maximum in the options?

Clipboard01

In these two graphs the red and blue lines both represent some series. Red in the left graph is the same series (other dates) as in the right graph, same goes for the blue line. As you can see the y-axis have automatically gotten reasonable values from the single graph perspective. I would like to have these synchronized, so that the mutual relationship is more visible.

leeoniya commented 3 years ago

there is no built-in way to do this, so some form of external code will be needed.

are you looking to simply match other graphs to the same y range as the one you zoom, or do you want to evaluate all data across all charts within the given x range and then rescale all charts (including the one you just zoomed) to the global min/max values?

hugovoerman commented 3 years ago

Hi Leon,

Thanks for your very quick response to my issue, really impressive!

I primarily look for a way to force different graphs with the same series to use the same y-axis. That’s for a start.

Of course it is wonderful to have the charts synced in such a way that when zooming one, the others zoom with it. But the latter is already working with the sync key.

It’s my idea that if we have a way to tell uPlot to use the same y-axis range for certain graphs (perhaps left, top, right and bottom separately?), that the rest will work via the sync key.

The picture I posted for instance is of measured and expected consumption of energy (let’s say gas and electricity). Use case: As a customer it would be helpful to have both using the same y-axis, as I can then see the difference easier instead of having to look carefully at the values on the y-axis. Of course another way to get this done it to just put both in one graph, I know, but that’s not really an option for other reasons.

I can also just look for max and min values in all relevant arrays with series data and use these as a minimum and maximum (forced in the options), but I think it would be nicer to have some way of telling uPlot to sync the y-axis.

Thanks again for your response and I look forward to your ideas on this issue.

Best regards,

Hugo

From: Leon Sorokin notifications@github.com Sent: Thursday, January 14, 2021 22:31 To: leeoniya/uPlot uPlot@noreply.github.com Cc: Hugo Voerman hugo.voerman@priva.com; Author author@noreply.github.com Subject: Re: [leeoniya/uPlot] Synchronizing Y-axis (#433)

there is no built-in way to do this, so some form of external code will be needed.

are you looking to simply match other graphs to the same y range as the one you zoom, or do you want to evaluate all data across all charts within the given x range and then rescale all charts (including the one you just zoomed) to the global min/max values?

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/leeoniya/uPlot/issues/433#issuecomment-760488802, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADECCFKX5KLESA76JGHX5BDSZ5PARANCNFSM4WC3LM6Q.

leeoniya commented 3 years ago

i think what you'll want to do is keep the sync'd selection behavior, but disable native zooming, and implement your own (where you can scan data and determine your own scale ranges). it's pretty easy. take a look at https://leeoniya.github.io/uPlot/demos/zoom-ranger.html

hugovoerman commented 3 years ago

I have seen this sample. This one uses the same series for both graphs. Functionality that I have successfully used in another visualization.

In this case I want to sync the y-axis of different graphs using similar (but different on the time axis) series of data.

From: Leon Sorokin notifications@github.com Sent: Thursday, January 14, 2021 23:43 To: leeoniya/uPlot uPlot@noreply.github.com Cc: Hugo Voerman hugo.voerman@priva.com; Author author@noreply.github.com Subject: Re: [leeoniya/uPlot] Synchronizing Y-axis (#433)

i think what you'll want to do is keep the sync'd selection behavior, but disable native zooming, and implement your own (where you can scan data and determine your own scale ranges). it's pretty easy. take a look at https://leeoniya.github.io/uPlot/demos/zoom-ranger.html

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/leeoniya/uPlot/issues/433#issuecomment-760523411, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADECCFPFLSX7TI5UBT3K3C3SZ5XQXANCNFSM4WC3LM6Q.

leeoniya commented 3 years ago

it would be helpful if you can provide the data (or representative data) and a jsfiddle that we can start to work from.

hugovoerman commented 3 years ago

Does: https://jsfiddle.net/hugovoerman/3wxy8aqd/8/ help?

So what I want is that these two graphs sync their y-axis to use the same range.

From: Leon Sorokin notifications@github.com Sent: Friday, January 15, 2021 16:59 To: leeoniya/uPlot uPlot@noreply.github.com Cc: Hugo Voerman hugo.voerman@priva.com; Author author@noreply.github.com Subject: Re: [leeoniya/uPlot] Synchronizing Y-axis (#433)

it would be helpful if you can provide the data (or representative data) and a jsfiddle that we can start to work from.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://github.com/leeoniya/uPlot/issues/433#issuecomment-761025990, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADECCFPKRHPOXMOWKLN4PYDS2BQ3DANCNFSM4WC3LM6Q.

leeoniya commented 3 years ago

yes, this helps.

now, i have several questions.

are you looking for the y ranges to be synchronized on:

  1. initial render
  2. zooming
  3. data updates (setData())

if you're only looking to sync ranges only during initial render, you can pre-scan your data (for both charts) and then provide the range to each during initialization.

if you want to sync range on zooming, while considering both charts, you will somehow have to match up the data range you intend to evaluate in the "other" chart that you're not interacting with, since your x values are not aligned. once you know which indices to scan in the other chart, you can re-scan the data and trigger a re-range on the other chart. how would the x-zoom interact on the other chart in this case? if you assume that you have the start and end indices from chart A (via selection), how do you convert that to start/end indices in chart B? or are you looking to simply do y-zooming as in this demo? https://leeoniya.github.io/uPlot/demos/zoom-variations.html, or do you just need to match up the y range of chart B to chart A, instead of two-way evaluation?

for data updates, the strategy is pretty much the same as for chart initialization, scan both datasets and update the data, with u.setData(newData, false) and then manually call setScale() to the range you want.

leeoniya commented 3 years ago

this is how you would do it on initial render: https://jsfiddle.net/n9cwjz58/

leeoniya commented 3 years ago

Of course another way to get this done it to just put both in one graph, I know, but that’s not really an option for other reasons.

btw, it's a real shame you cannot do both in one graph :D

there are plenty of ways to indicate that some of it is forecasted data. you can do a horizontal stroke gradient to color the second half of the line differently (like with a more pale or transparent color), or shade the forecast region: e.g. https://github.com/leeoniya/uPlot/issues/416#issuecomment-751371011. or just as separate different-colored series padded with nulls on opposite ends.

hugovoerman commented 3 years ago

Perhaps if I could change the background color of the graph from a certain time onwards, I can use this as a proposal to make it one graph. Is there a way to do that? I can't use fills for graph data as in the real graphs there are multiple lines. So just changing the background color to make past and future (and perhaps a line marking the present) could be an idea. Is this possible with uPlot?

leeoniya commented 3 years ago

yes, you simply draw it on the canvas: https://jsfiddle.net/fwgq73xk/1/

let data = [
    [0, 1, 2, 3, 4, 5],
    [0, 1, 2, 3, 4, 5],
];

const opts = {
    width: 600,
    height: 400,
    hooks: {
        drawAxes: [
            u => {
                let left   = u.valToPos(3, 'x', true);
                let right  = u.valToPos(5, 'x', true);
                let top    = u.bbox.top;
                let height = u.bbox.height;

                u.ctx.fillStyle = "rgba(0,0,0,0.1)";
                u.ctx.fillRect(left, top, right - left, height);
            }
        ]
    },
    scales: {
        x: {
            time: false
        },
    },
    series: [
        {},
        {
            stroke: "red"
        },
    ],
};

let u = new uPlot(opts, data, document.body);
hugovoerman commented 3 years ago

Right now I'll settle for two options:

  1. precalc the min/max over separate data sets and use these for the graphs
  2. use the technique above to combine both datasets and use a visual effect to differentiate between the data over the time axis.

Thanks for all the assistance.

leeoniya commented 3 years ago

closing for now, but let me know how the pitch goes :D