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

Using stepSize and maxTicksLimit together doesn't work. #2539

Closed
2 tasks done
gitaarik opened this issue May 13, 2016 · 26 comments · Fixed by #5938
Closed
2 tasks done

Using stepSize and maxTicksLimit together doesn't work. #2539

gitaarik opened this issue May 13, 2016 · 26 comments · Fixed by #5938

Comments

@gitaarik
Copy link

gitaarik commented May 13, 2016

I want to have a chart with a minimal stepSize of 1, basically I don't want the steps to have decimals like 0.5 or something. I can set stepSize to 1, but then it will show a label for each step regardless of maxTicksLimit.

You can see the problem here:
http://codepen.io/anon/pen/pyBPME

If you disable the stepSize option, the labels are fine, but if you then disable "dataset 1" and it only shows "dataset 2", it will have steps like 0.5 and 1.5 which I don't want.

Am I missing configuration or is this an issue?

Many thanks!

@etimberg
Copy link
Member

@gitaarik to filter the labels, you could change the callback function that generates the tick strings. For every other label just return an empty string. If you return null or undefined the grid line will also be hidden

@gitaarik
Copy link
Author

Ok thanks, that is a way to solve my problem:
http://codepen.io/anon/pen/MyRopv

Though I think it would be nicer if the options would work together. But it does the job for now.

@gitaarik
Copy link
Author

gitaarik commented May 17, 2016

Unfortunately setting stepSize to 1 has a effect I don't like. When you look at my fiddle:
http://codepen.io/anon/pen/MyRopv

You see the yAxis goes to 19. Without the stepSize option it will go to 20:
http://codepen.io/anon/pen/ONYzwg

I like this behaviour better because it's easier to read when it goes in steps of 5 up to 20, instead of steps of 4 up to 19.

So if the minStepSize option would be implemented it would solve these two problems. Do you think it will be hard to implement? I wouldn't mind giving it a try myself.

@Streusel
Copy link

Streusel commented Jun 20, 2016

I would love to see minStepSize implemented, I'm working with small numbers occasionally and seeing my dataset show 0.5 when that's not even a possible value (for the y-axis) is a bit odd...

Example: I can't have half a person show up to an event. I may have events in which only ~20 people show up and if I log when they arrive and when they leave I can see halfsies. If I now work with numbers of in the triple digits I don't get this, however, using stepSize: 1 will give me an awful y-axis compression. (http://codepen.io/anon/pen/RRGMbj)

Large dataset: http://i.imgur.com/5yEXd7r.png
Small dataset: http://i.imgur.com/LRBn0jP.png

@etimberg
Copy link
Member

I think we can add a minStepSize option that works with the auto tick generation algorithm.

@Shivang44
Copy link

@etimberg I think that would simplify what I'm (and I'm sure others) are trying to achieve, which is integer-only ticks.

@gitaarik
Copy link
Author

@etimberg So do you think it's easy to implement? Would like to give it a try.

@etimberg
Copy link
Member

@gitaarik I think it could be easy to implement. If you'd like to try, i"d say to go for it. Feel free to drop any questions here or on Slack

@georgeu2000
Copy link

I recently ran into this issue as well as #3092.

The solution I found is to create a custom formatter. This gives you much finer control over which labels are rendered. In my experience, often the only way to tweak charts effectively is to have very fine control.

ticks: {
    callback: function( tick, index, ticks ){
        if( tick.toString().startsWith( '1' ))
            return tick.toLocaleString();

        return null;
      }
  }

image

@chris-wood-dynmark
Copy link

chris-wood-dynmark commented Oct 28, 2016

Thanks georgeu2000, using the callback enabled me to filter out fractions using

`ticks: {
beginAtZero: true,
maxTicksLimit: 10,
callback: function (tick, index, ticks) {
if (tick.toString().indexOf('.') !== -1)
return null;

    return tick.toLocaleString();
}

}`

This achieved the minStepSize = 1 that I required

@micheledallatorre
Copy link

If it may help, I ran into a similar problem, that is I wanted to have in my charts:

  • start at 0
  • no "half values" (e.g. 0.5, 1.5, 2.5, etc.) but only integers (e.g. 0, 1, 2)
  • minimum step of at least 1, that is no half values, but at the same time not each steps=1 when I have big values (e.g. if I have to display a value of 100, I do not want to see 100 ticks, I want to group the ticks by e.g. 10 so that they becomes e.g. 10, 20, 30, etc.)

I somehow accepted the output given by using chart options as follows:

ticks: {
    beginAtZero: true,   // minimum value will be 0.,
    stepSize: 1,
    maxTicksLimit: 11
}

The problem is that in this way, ticks are grouped in a weird way (e.g. 0, 6, 12, 18, 24, 30, 36, 42, 48, 55) rather than 0, 5, 10, 15, or 0, 10, 20, etc.

@chris-wood-dynmark
Copy link

@micheledallatorre , if you modify the callback in my example to only show the tick when the value is divisible by 5 you can achieve what you are after

@micheledallatorre
Copy link

micheledallatorre commented Nov 14, 2016

@chris-wood-dynmark thanks a lot, I'll try that.
The thing is that it should not be fixed to e.g. 5 but dynamic, according to the max value of my dataset.
For example, if my maxvalue = 100, tickValue should be 10, if maxvalue = 50, tickValue should be 5, etc.

I'll try if I can achieve this anyway, thanks for your piece of advice!

UPDATE:
Tried as follows, but when using also maxTicksLimit, the code is not working well.
For example, with maxTicksLimit=11 and the following callback, only values 30 and 55 are displayed as ticks in my X axis.
I guess this happens because the default output is (not sure why!) 0, 6, 12, 18, 24, 30, 36, 42, 48, 55 and only 30 and 55 divide 5 in the ouptut above...

    callback: function( tick, index, ticks ){
        if( tick % 5 == 0)
            return tick;
        return null;
      }

@etimberg
Copy link
Member

etimberg commented Jan 10, 2017

I'm trying to gather requirements so that we can implement this. Based on my reading of the comments, it sounds like the following features are requested

  1. Allow a minimum step size. Something like ticks.minStepSize which ensures that spacing = Math.max(spacing, autoGeneratedSpacing)
  2. Add a tick count property. Something like ticks.count which forces the number of ticks on the axis. This does not try to force the spacing to a nice value. For example, if the min (specified or calculated) is 0 and the max (specified or calculated) is 1 and ticks.count = 4 the following ticks would be generated: [0, 0.333, 0.666, 1]
  3. Include maxTicksLimit when using stepSize and minStepSize. Ie, if the number of ticks is generated to be 100 but we are limiting to 10, then only show 10.

@chris-wood-dynmark
Copy link

In my case I wanted 10 ticks but when I had values that were less than 10 I did not require fractional ticks. i.e a max value of 5 in the chart would have 5 ticks, a max value of 600 would have 10 ticks.

Would the proposed solution support that? It is not a fixed number of ticks every time it's the maximum number of ticks that the maxPointValue/minStepSize allows capped at the maxTicksLimit.

@etimberg
Copy link
Member

@chris-wood-dynmark I think that would be supported with the minStepSize set to 1 but I've edited my list above to include that requirement.

I wrote that we should support it for both minStepSize and stepSize though I kind of feel that it is more applicable to stepSize only. Since it forces the stepSize to be a certain value, while minStepSize still runs the auto generate algorithm (which takes into account maxTicksLimit) it might be odd to have it apply in that case since there are 2 variables for adjusting the step size.

@micheledallatorre
Copy link

@etimberg would your proposed solution take into account also the generation of the tick values?

For example, I had (see comments above) these values 0, 6, 12, 18, 24, 30, 36, 42, 48, 55, not sure why they were all +6 apart from the last one (48 +7 = 55).
Was this generated because of the number of ticks, set to 11 (so 10 values)?
In that case, would I be able to generate the ticks at specific increments (e.g. +5)?

Thanks!

@etimberg
Copy link
Member

@micheledallatorre yes, that should be fixed. Instead of using maxTicks: 11 it would be ticks.count: 11. The max ticks doesn't guarantee the number of ticks, only that the count is <= to that value. The proposed count property would explicitly set the number of ticks. I don't know which setting would take priority of ticks.count, ticks.minStepSize, and ticks.stepSize

I haven't investigated too far with that specific case yet, but with maxTicks of 11, there should be 11 values. I'm not sure why they're 6 apart either. AFAIK the auto generate algorithm should never pick 6 🤔 it only works in 1,2,5,10,...

@micheledallatorre
Copy link

@etimberg exactly, I'm not sure about which setting would take priority either, but we can investigate that afterwards.

Regarding the ticks value, I can give you other examples, e.g. 0, 8, 16, 24, 32, 40, 48, 56, 64, 77 (sic)! It seems like the difference between two values (in this case 8) is not taken into consideration w.r.t. the last value (in this case 77, which differs 13 from the previous value 64). Let me know if you want to check that in a separate issue or investigate that after the changes you are going to make.
Thanks!

@hexx870206helen
Copy link

@etimberg
Hey..i stuck on a small issue for 3 days..My problem is how to pass a variable to ticks.max? or the ticks.max is only allowed to be a fixed value? for example.. my dataset is [1.2,2.5,3.9] now the ticks.max is default to be 4, now I want to change the ticks.max to be 5 (the max value in dataset plus 1) or something which makes the chart looks more reasonable. because 3.9 is quite near to 4.0 so that the line seems like being cut by the gird line. I will be appreciate if you can provide any suggestions about my case.

@etimberg
Copy link
Member

@hexx870206helen you can't pass a variable or function to ticks.max. I would use one of the lifecycle callbacks to change it. See https://github.com/chartjs/Chart.js/blob/master/docs/axes/README.md#callbacks

@maurop123
Copy link

Is minStepSize still going to be added? It would perfectly fit my use case!

@engelby
Copy link

engelby commented Nov 1, 2017

Until this is implemented, I am using this code to enforce a minimum step size.

options: {
    scales: {
        xAxes: [{
            type: 'linear',
            position: 'bottom',
            min: 0,  // My use case requires starting at zero
            beginAtZero: true,
            ticks: {
                callback: function(value, index, values) {
                    // Make sure a tick is not a fractional value
                    if (Math.floor(value) === value) {
                        return value;
                    }
                }
            }
        }]
    }
}

@maurop123
Copy link

maurop123 commented Nov 2, 2017

Thanks @engelby. Unfortunately, this doesn't suit my use case because the ticks become unevenly spaced out along the axis. It may be because my minStepSize is 5 or 10, depending on the graph.

In case it helps anyone, this is the solution I found for my case. I'm using vue-chartjs.

export default Line.extend({
  mixins: [mixins.reactiveProp],
  props: {
    ticks: {  // this prop contains my ticks options, including stepSize
      type: Object,
      default: () => ({})
    },
  },
  data: () => ({
    useStepSize: true,
  }),
  methods: {
    make() {
      this.renderChart(this.chartData, {
        maintainAspectRatio: false,
        scales: {
          yAxes: [{
            gridLines: {
              display: true,
              drawTicks: false
            },
            ticks: {
              ...this.ticks,
              beginAtZero: false,
              callback: (value, index, values) => {
                const tooManyTicks = values.length > 11
                const stepSizeTooSmall = (values[0] - values[1]) < this.ticks.stepSize
                if (this.useStepSize && (tooManyTicks || stepSizeTooSmall)) {
                  this.useStepSize = false
                }
                return Math.floor(value * 100) / 100 + '  '
              }
            }
          }]
        }
      })
    },
  },
  mounted () {
    this.make()

    if (!this.useStepSize) {
      const ticks = _.omit(this._chart.config.options.scales.yAxes[0].ticks, ['stepSize'])
      this._chart.config.options.scales.yAxes[0].ticks = ticks
      this._chart.update()
    }
  }
})

Essentially, I'm checking in the y-axis callback if there are too many ticks (more than 11), or if the step size is smaller than the one specified by my ticks prop, which defines the stepSize option. If so, then I update the chart without the stepSize option.

Now that I review it, the stepSizeTooSmall check may be extraneous and this logic may work without it. Anyway, hope this helps someone, and that this feature gets to be officially supported :-)

@amitavmohanty
Copy link

Hi,
Can we define a fixed distance between ticks, but where the value difference between 2 ticks to be not same.

example: suppose we need to show 10 ticks or scale labels on x-axis.
But the tick values : 1, 10, 100, 200, 500, 1000, 5000, 20000, 100000, 500000

Is it possible?

Thanks a regards
Amitav

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.