Charts: Stop axis labels duplicating.

Created on 24 Aug 2015  ·  51Comments  ·  Source: danielgindi/Charts

Within the App I have an issue where the labels on the left axis of either a BarChartView or a LineChartView repeats.

I first added the snippet below to ensure that the number of labels on the axis does not exceed the maximum value in the y data.

self.chartView.leftAxis.labelCount = self.chartView.data.yMax > YAxisMaxLabelCount ? YAxisMaxLabelCount : self.chartView.data.yMax;

This ensured that if the maximum value in the y data was 3 then there would only be 3 labels within the axis. Now this worked fine if the graph was not interactive, i.e. the user could not zoom the chart.

As soon as the user zooms obviously the count of the labels remains the same and the value formatter fills in the gaps allowing a value to repeat.

_Example of axis with no duplicated values_
screen shot 2015-08-24 at 5 07 29 pm

_Example of same chart but zoomed in_
screen shot 2015-08-24 at 5 07 54 pm

The screenshots show the left axis repeating the value 2. What is your best suggestion on solving this repeating label issue? Any help would be grateful. If you need any further information let me know.

Thanks

Most helpful comment

This is released in 2.2.4, as the granularity feature

All 51 comments

I guess it is not the label duplicated, but it contains decimal values and an integer formatter? You need to check what's the raw value for each x axis label and how you format it when it draw the xAxis labels in y axis renderer.

I was able to do this by adding a check to ensure the interval each label is always a whole value.
The change is within the computeAxisValues() function of the ChartYAxisRenderer class

var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval))
if(_yAxis.valueFormatter?.maximumFractionDigits == 0)
{
    interval = interval < 1 ? 1 : floor(interval)
}

If the value formatter request there to be no fractional digits then it floors the intervals and if it is less then the whole number 1 it sets it the interval to 1.

Would you like a pull request?

Thanks for your help.

If you are having decimal values, why maximumFractionDigits is 0? I think it's the formatter issue.

I don't want decimal values that is why I set maximumFractionDigits = 0

pal, your data is decimal, and you just want to use formatter to abandon the fraction digits, it does not make sense. Why not to convert your data to integers first?

The y values I give to the chart are

(1, 3, 2, 0, 0, 7, 0)

Once I create a ChartDataSet these values are then doubles.

ChartDataSet, label: , 7 entries:
ChartDataEntry, xIndex: 0, value 1.0
ChartDataEntry, xIndex: 1, value 3.0
ChartDataEntry, xIndex: 2, value 2.0
ChartDataEntry, xIndex: 3, value 0.0
ChartDataEntry, xIndex: 4, value 0.0
ChartDataEntry, xIndex: 5, value 7.0
ChartDataEntry, xIndex: 6, value 0.0

This is because your ChartDataEntry Class holds the value as a Double

/// the actual value (y axis)
public var value = Double(0.0)

Although the values have been changed to a double the charts do show the correct increment of labels at first but as soon a the chart is zoomed in, the ChartYAxisRenderer wishes to fill in the gaps and as my value formatter removed any fractional digits it shows as a duplicated value.

I do not want the chart to assume I want additional values in-between the whole numbers as only want to show a label for a whole number.

So this leads me to see that I am supplying the correct data to the chart, but the chart is making assumptions on what I want to do. This is fine for a default but should provide the option to only show whole numbers on the axis too.

By adding the check against the value formatter it allows this behaviour. You might know of a better way to provide this behaviour rather than checking the value formatter but it is a use case some people my want.

I have just noticed that I am using the code base at commit a7e30dd16fbfaecb8e44c01ad8618a3a745fd101 and when this change is implemented into the latest commit of Charts it does not act as expected.

Would you be able to advice how best to achieve the behaviour I want?

I have attached a gif of the expected behaviour.

Below you can see that no labels show the same value and the chart is not attempting to place any label in-between other labels as there is the requirement that the labels are whole values.

chart

Ah I see. If you zoom to let's say [3,4], the range would be [3,4] and the axis wants to display [3.0, 3.2, 3.4, 3.6, 3.8, 4.0], and you don't want fractional digits, is it?

It's actually nothing I can do... since your data is quite small, and we have to display 4-6 labels there... What if the range is [0,1], and you need to display 5 labels, what can you display other than fractional digits?

I think it's fine to display fractional digits if the range is too small, but if you truely don't want it, then you can do it like your did. Or you would accept only show the max and min label called showOnlyMinMaxEnabled

BTW, your screenshots works great, solved your problem

There is also some properties to help you limit the label count. You can try modify and fit for different scale levels.

    public func setLabelCount(count: Int, force: Bool)
    {
        _labelCount = count

        if (_labelCount > 25)
        {
            _labelCount = 25
        }
        if (_labelCount < 2)
        {
            _labelCount = 2
        }

        forceLabelsEnabled = force
    }

To have the same behaviour on the Y axis is necessary to set a Y zoom limit. #382

388

@steprescott, is #388 solved your problem?

From looking at the commit for issue #388 the only change is a new property on viewport.

How would I get the intended behaviour that is shown in the gif but with your changes?

Hi @steprescott, try with [_chartView.viewPortHandler setMaximumScaleY: 2.f]; for example, like this the granularity of the zoom isn't so big to show repeated values. A good example is when you have a horizontal char line without a limit all Y values are repeated when zoom is big. With MaximumScaleY this repetition doesn't happen.

Without limit:
screen shot 2015-09-22 at 23 29 16

With limit 2f:
screen shot 2015-09-22 at 23 30 07

Hey @noais

That works on a dataset that has a lager range of values but if you have a small range of values, example being an array of values 0 - 7, zooming in by a factor of 2 could potentially provide duplicate labels.

Although by limiting the zoom factor has solved your issue, it dose not solve the issue of repeating whole values.

I did get my expected results when editing the ChartYAxisRenderer class pointing at commit a7e30dd16fbfaecb8e44c01ad8618a3a745fd101.

This was done by flooring the value.

var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval))
if(_yAxis.valueFormatter?.maximumFractionDigits == 0)
{
    interval = interval < 1 ? 1 : floor(interval)
}

Although this worked (but not when using HEAD of this repo) I feel that you guys will know a better place within your framework to place this to provide the same result as shown in the gif.

Maybe a property allowRepeatingValues BOOL on a axis. You guys will know better.

Yes you right this was one work around. Let me try to see if is easy to create some method like allowRepeatingValues.

That would be most helpful, thank you.

Hey @noais and @liuxuan30. Have you had chance to investigate this? I was able to get the required effect using the framework but I don't know of where best this property should sit within the framework and if there is a better way to achieve the same result.

Thanks for your time.

I am still thinking it's because the formatter that produced duplicated labels with small digits, not the algorithm. If you delete the formatter, it will display the number correctly, right?
You can solve it with your code, but I am not sure if it's a good one for every one.
@danielgindi what do you think

Hi guys, voting for allowRepeatingValues, I have small numbers and don't want to display duplicated y-axis valuse

When all my values are zero, I get these repeating axis labels-
chart

Also, I've noticed that although the code snippet I have does what I need, it is not removing duplicate values simply only allowing whole numbers. So by checking against the number formatter isn't a horrible way of adding this.

What that snippet doesn't' allow is removing of duplicated values so if the values were 1.1, 1.2, 1.2, 1.3 and you wished to keep the single decimal digit it would not work. You guys will know the best place and implementation for this feature and for people to spend time looking at the current issues of the framework and for them to then up vote it shows it's an option others want too.

allowRepeatingValues :+1:

@noais @liuxuan30 Do you have a better solution to this than what I have suggested?

nope, except for allowRepeatingValues.. Because your original problem is about the formatters. you don't want decimal digits at a level but your data actually has the next level

Ideally graph should't show repeating values, ever! Can repeating values be hidden with some logic, thus resulting in only unique values?

@vigyanhoon it's not 'repeating' values behind, it's formatter that causes the value to be 'repeated'. If you don't want repeat, give a decimal formatter can solve it.

@liuxuan30 Can you explain what you mean by "_give a decimal formatter_"? With my data I gave a formatter that set the maximumFractionDigits = 0. I'm not sure how to get the expected results with what is currently there? Have you been able to produce a chart with the framework as it is that gives the same results as my gif I posted on the 28th August?

Did you mean to use numberFormatter.allowsFloats = NO, if yes, it didn't help me, I still see duplicated INTs?! The only way I managed NOT to display repetative values is to use 2 labels on xAxis (top/bottom)

@steprescott sorry if I'm not clear. MaximumFractionDigits should not be 0, otherwise it will only show the integer part. I did met similar issues, if the data range is small and 5~6 labels needed, giving maximumFractionDigits = 0 will have repeated values. There is no easy way to fix this I guess. think about if the data range is [0,2], and I have to display 5 labels: 0, 0.4,0.8,1.2,1.6, 2.0. If we apply maximumFractionDigits = 0, it maybe 0, 0, 1, 1, 2, 2

@injectios, how about MaximumFractionDigits = 2 or even 4?

Hey everyone!
I'm having this same issue.

@liuxuan30 depending on the type of data, it doesn't make sense to have decimals. Like a chart of humans, you will never have 1.5 humans, its either 1 or 2.

@jpros you got a point, but from the math point, we cannot have 4 labels without decimals in range [0,2].

So either turn on yAxis.showOnlyMinMaxEnabled to avoid repeated values as work around for now,
Or you implement your logic to calculate your own values in ChartYAxisRenderer.computeAxis to avoid,
Or we have to wait for feature 'allowRepeatValues'

@PhilJay do we have any solution for that currently? :-)

From an Android (and ofc iOS) POV, this line here: https://github.com/PhilJay/MPAndroidChart/blob/master/MPChartLib/src/com/github/mikephil/charting/renderer/YAxisRenderer.java#L108

Could be a location to tackle this problem. There have the float intervalof the axis labels calcualted, and could use it to prevent further changes to the labels if two labels would be formatted equally.
That's just what comes to my mind when quickly thinking about it. What do you think?

I got rid of duplication by setting a max value

leftAxis.customAxisMax = maxYLabelCount

@vigyanhoon it's totally different thing, values could be millions, while maxYLabelCount can't. customAxisMax is to define the max value the axis range could be.

+1 for solving the rendering issue. I have also a graph of people, and I'm having [0, 0, 1, 1, 2, 2]. Setting the numberFormatter.maximumFractionDigits to anything other than 0 doesn't make any sense.

The following code suggested by @steprescott fixed for me as well. I added it in my fork here:

https://github.com/natanrolnik/ios-charts/commit/01df4d9264e0adbef4db88763075376f4a0f255c

Would a PR be able to close this issue? What do you think @danielgindi @liuxuan30 @steprescott ? Are there cases this code doesn't fix? If so, what are better solutions?

@natanrolnik what if the user zoom in and range become [25.2, 25.3]? The interval would be 1.0 while it should be, say 0.02.
It should help if user specificly set max fraction number to 0, we limit the interval. I didn't test negative values though.
We don't have a better solution yet, but I'm guessing a feature like 'allowRepeatValues' is what we can think of?

I agree regarding allowRepeatedValues!

I think the @steprescott is a good solution. Only needs to adjust the value one to a granularity variable.

With this @liuxuan30 problem don't exists any more.

If we put this code on line 75 in ChartYAxisRenderer:

interval = interval < granuality ? granuality : interval       

https://github.com/noais/ios-charts/compare/master...Noais
What do you think?

Ok, so this is now closed but it isn't merged into master. When would this be likely to happen?

This is released in 2.2.4, as the granularity feature

I'm not sure if I should post this issue here but it makes sense to me.

I have a very similar problem with my xAxis labels. I have a custom IAxisValueFormatter that returns strings for time, it gives hours like this "22:00", "23:00" for some charts, week dayslike this "Mon.", "Thu." for others etc..

When the chart is zoomed in too much, the values start to duplicate like shown here:

Screenshot 2019-07-19 at 17 59 11

In this case the granularity feature doesn't work since it works with the difference between numbers.

Do you have any Idea of how I could stop those labels from repeating?

Thanks

Set granularityEnabled to false work for me
leftAxis.granularityEnabled = true

Hello guys , i want to make xAxis value not repeating when zoom in
my Formater :
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let date = Date(timeIntervalSince1970: value)
return date.formattedString(withDateFormat: .daymonth)
}
also i use granularity , but i still have the same issue
xAxis.granularity = 1.0
xAxis.granularityEnabled = true

can anyone know how to solve it plz ?

@Aaimek
i have the same issue , did you find the solution ?

I have the same issue when I force x-axis label count, and plots a few points.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BrandonShega picture BrandonShega  ·  4Comments

guoyutaog picture guoyutaog  ·  3Comments

heumn picture heumn  ·  3Comments

kwstasna picture kwstasna  ·  3Comments

anhltse03448 picture anhltse03448  ·  3Comments