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

Stop axis labels duplicating. #315

Closed
steprescott opened this issue Aug 24, 2015 · 52 comments
Closed

Stop axis labels duplicating. #315

steprescott opened this issue Aug 24, 2015 · 52 comments

Comments

@steprescott
Copy link

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

@liuxuan30
Copy link
Member

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.

@steprescott
Copy link
Author

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.

@liuxuan30
Copy link
Member

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

@steprescott
Copy link
Author

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

@liuxuan30
Copy link
Member

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?

@steprescott
Copy link
Author

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.

@steprescott
Copy link
Author

I have just noticed that I am using the code base at commit a7e30dd 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?

@steprescott
Copy link
Author

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

@liuxuan30
Copy link
Member

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

@liuxuan30
Copy link
Member

BTW, your screenshots works great, solved your problem

@liuxuan30
Copy link
Member

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
    }

@noais
Copy link
Contributor

noais commented Sep 18, 2015

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

@noais
Copy link
Contributor

noais commented Sep 18, 2015

#388

@liuxuan30
Copy link
Member

@steprescott, is #388 solved your problem?

@steprescott
Copy link
Author

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?

@noais
Copy link
Contributor

noais commented Sep 22, 2015

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

@steprescott
Copy link
Author

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 a7e30dd.

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.

@noais
Copy link
Contributor

noais commented Sep 25, 2015

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

@steprescott
Copy link
Author

That would be most helpful, thank you.

@steprescott
Copy link
Author

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.

@liuxuan30
Copy link
Member

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

@injectios
Copy link

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

@bcbeta
Copy link

bcbeta commented Oct 3, 2015

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

@steprescott
Copy link
Author

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.

@noeleont
Copy link

allowRepeatingValues 👍

@steprescott
Copy link
Author

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

@liuxuan30
Copy link
Member

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

@vigyanhoon
Copy link

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

@liuxuan30
Copy link
Member

@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.

@steprescott
Copy link
Author

@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?

@injectios
Copy link

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)

@liuxuan30
Copy link
Member

@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?

@jpros
Copy link

jpros commented Nov 24, 2015

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.

@liuxuan30
Copy link
Member

@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'

@danielgindi
Copy link
Collaborator

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

@PhilJay
Copy link
Collaborator

PhilJay commented Nov 25, 2015

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?

@vigyanhoon
Copy link

I got rid of duplication by setting a max value

leftAxis.customAxisMax = maxYLabelCount

@liuxuan30
Copy link
Member

@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.

@natanrolnik
Copy link

+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.

@natanrolnik
Copy link

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

natanrolnik@01df4d9

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?

@liuxuan30
Copy link
Member

@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?

@natanrolnik
Copy link

I agree regarding allowRepeatedValues!

@noais
Copy link
Contributor

noais commented Dec 30, 2015

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       

noais/ios-charts@master...Noais
What do you think?

@steprescott
Copy link
Author

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

@noais
Copy link
Contributor

noais commented Jan 4, 2016

#648

@danielgindi
Copy link
Collaborator

This is released in 2.2.4, as the granularity feature

@Aaimek
Copy link

Aaimek commented Jul 19, 2019

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

@mickaelhero
Copy link

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

@aymenbraham
Copy link

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 ?

@aymenbraham
Copy link

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

@adarshjayan7574
Copy link

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

@mksmanish
Copy link

having the same issue ,Is anyone having any solution for this?

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

No branches or pull requests