Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

log scale step size too large #7332

Closed
benmccann opened this issue May 8, 2020 · 11 comments · Fixed by #9166
Closed

log scale step size too large #7332

benmccann opened this issue May 8, 2020 · 11 comments · Fixed by #9166
Milestone

Comments

@benmccann
Copy link
Contributor

On https://www.chartjs.org/chartjs-chart-financial/ it's choosing increments of 10 which results in lots of whitespace (you need to change the dropdown to logarithmic)

Screenshot from 2020-05-08 13-58-42

@benmccann benmccann added this to the Version 3.0 milestone May 8, 2020
@benmccann
Copy link
Contributor Author

benmccann commented May 9, 2020

I have a branch that works significantly better: https://github.com/benmccann/Chart.js/tree/log-scale-ticks

Unfortunately it fails more than a hundred tests that expect very specific ticks to be generated. We'd probably have to change the log scale tests to image tests in order to make any improvements to tick generation

@benmccann benmccann removed this from the Version 3.0 milestone May 9, 2020
@benmccann
Copy link
Contributor Author

The overall tick generation strategy for the log scale is not very well thought out. I believe that the wider range of data you have the more ticks it will generate. Instead we should do something like use LinearBase.getTickLimit to determine how many ticks can fit on the scale.

I'm removing this from the 3.0 milestone though because these are longstanding issues and shouldn't be a blocker for the release

@etimberg
Copy link
Member

etimberg commented May 9, 2020

I'm not sure what the most expected behaviour is. I believe the log scale expands the range down to the start of the power-of-10, i.e. the range is expanded down to start at 10 even if the data min is 30. I think the reason I wrote it that way was that usually log charts are used when the data range is very large and so expanding a bit isn't a problem. It generates so many ticks to out of the box generate something that looks like https://i.stack.imgur.com/zxKqo.jpg

We didn't have minor ticks as a concept when the log scale was introduced. Perhaps we could use that? Declare major ticks at 1, 10, 100, etc and then minor ticks in between. Before switching the generation options too much, I think its worthwhile to do a bit of research and see what other libs do

@benmccann
Copy link
Contributor Author

If you do a Google image search for logarithmic chart you can find lots of examples of how other charts are drawn. Very few begin at powers of 10 and for those that do it's typically because the data is near a power of 10, so you don't end up with much white space at the bottom of the chart. For finance chart examples you can try Yahoo Finance or TradingView as two popular sites that both offer log scales with interactive charts.

Here's an image where the data is far apart. We end up with probably too many ticks.

Screenshot from 2020-05-09 13-01-54

Here's an image where the data is close. We end up with not enough gridlines. I think it'd be easier to tell what the data points are if we had ticks at 10, 12, 15, 20 for example.

Screenshot from 2020-05-09 13-02-26

The two charts are the same number of pixels, so should show roughly the same number of ticks in my opinion. This is how the other scales we have today like the linear and time scale work.

@etimberg
Copy link
Member

etimberg commented May 9, 2020

Right, my comment was more around the first chart. If the data min is 20, I think starting the axis at 10 looks a lot better and is clearer to understand. That being said, there are too many ticks in the top chart and I think that's because the filtering is done in the label callback.

Perhaps we can apply the linear algorithm to the powers of 10. In the top chart, its equivalent to linear ticks at 1, 4, 7, ... which are easier to control. I'm not sure how to best control when to add minor grid lines. In the top chart, it would be cluttered, but if the range was smaller, I'd want to see grid lines at 10, 20, 30, etc.

@benmccann
Copy link
Contributor Author

Here's what it looks like starting at the nearest power of 10 vs not. I'm not sure I see any issues starting with 20. Doing so would be more consistent with the other chart types to have the tick range be close to the data range. I think starting at a power of 10 can make it look kind of squished with a lot of whitespace.

master

Screenshot from 2020-05-09 14-42-57

my branch

Screenshot from 2020-05-09 14-47-40

I like how the current code chooses numbers starting with 1, 2, 5. I had tried generating every number and letting the auto-skipper do its thing before, which would sometimes choose other numbers. That didn't look as nice because the numbers wouldn't be spread out appropriately when plotted

@etimberg
Copy link
Member

etimberg commented May 9, 2020

I like the results on your branch. How are you keeping the grid lines at 30 and 40 but not generating the labels?

@benmccann
Copy link
Contributor Author

It generates the ticks and then the tick formatter formats them as empty string. That's what happens in master and in 2.9. I didn't change that part

@etimberg
Copy link
Member

etimberg commented May 9, 2020

Is there anything we could do to make the test changes easier? I'm sure there are lots of old tests in the log scale that could be removed / simplified

@benmccann
Copy link
Contributor Author

I haven't looked too closely. I think a lot of tests probably test duplicate functionality and we could get away with removing most of them and replacing the rest with image tests. I don't really have more time to work on this though.

@jaredgibb
Copy link

jaredgibb commented Feb 10, 2021

i just want to share how I have done these. I use a callback to create the ticks i want. im sure some JS magic could make this dynamic.

var ctx = document.getElementById('myChart').getContext('2d');
        var myChart = new Chart(ctx, {
            type: 'line',
            data: {
                labels: ['January', 'February', 'March', 'April', 'May','house','home','apartment','trailer'],
                datasets: [{
                    label: 'Corrects',
                    data: [10, 60, 70, 180, 190, 250, 350, 450, 550],
                    fill: false,
                    borderColor: 'green',
                    pointStyle: 'triangle',
                    pointRadius: 10,
                    pointHitRadius: 20,
                    lineWidth: 10,
                },
                {
                    label: 'Incorrects',
                    data: [.10, 20, 30, 40, 50, .05, .5, 3, 8],
                    fill: false,
                    borderColor: 'red',
                    pointStyle: 'star',
                    pointRadius: 10,
                    pointHitRadius: 20,
                    lineWidth: 10,
                }
                ]
            },
            options: {
              //  tooltips: {
               //     titleFontSize: 30,
               //     bodyFontSize: 30
               // },
                responsive: true,
                maintainAspectRatio: false,
                title: {
                    display: true,
                    text: 'Chart Title',
                    fontColor: 'Black',
                    fontSize: 30,
                },
                legend: {
                    position: 'bottom',
                },
                scales: {
                    xAxes: [{
                        ticks: {
                            fontSize: 30,
                            max: 90,
                            maxTicksLimit: 6,
                        }
                    }],
                    yAxes: [{
                        type: 'logarithmic',

                        ticks: {
                            fontSize: 30,
                            autoSkip: false,
                            min: .001,
                            max: 1000,
                            callback: function (value, index, values) {
                                if (value == .001 || value == .01 || value == .1 || value == 1 || value == 10 || value == 100 || value == 1000) {
                                    return value + '/min';
                                }
                            },
                        }
                    }],
                },
            },
        });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants