Google Charts vertical axis in whole numbers
Asked Answered
H

4

15

I am trying to configure a Google column chart to display the vertical axis in whole numbers but when it is displaying a small number such as 1 it displaying 0.25, 0.50 etc as well.

Is there anyway to change this? I have been through the documentation and can't find anything that seems relevant.

Thanks

Hanser answered 21/1, 2013 at 13:57 Comment(0)
D
28

Simple answer: yes, but it's complicated.

If you just want numbers to display as whole numbers, then it's easy:

function drawVisualization() {
  // Create and populate the data table.
  var data = google.visualization.arrayToDataTable([
    ['x', 'Cats', 'Blanket 1', 'Blanket 2'],
    ['A',   1,       1,           0.5],
    ['B',   2,       0.5,         1],
    ['C',   4,       1,           0.5],
    ['D',   8,       0.5,         1],
    ['E',   7,       1,           0.5],
    ['F',   7,       0.5,         1],
    ['G',   8,       1,           0.5],
    ['H',   4,       0.5,         1],
    ['I',   2,       1,           0.5],
    ['J',   3.5,     0.5,         1],
    ['K',   3,       1,           0.5],
    ['L',   3.5,     0.5,         1],
    ['M',   1,       1,           0.5],
    ['N',   1,       0.5,         1]
  ]);

  // Create and draw the visualization.
  new google.visualization.LineChart(document.getElementById('visualization')).
      draw(data, {curveType: "function",
                  width: 500, height: 400,
                  vAxis: {maxValue: 10, format: '0'}}
          );
}

If you go to google playground you will note that the axis labels are 0, 2.5, 5, 7.5, 10. By adding the format: '0' to the vAxis, it will display only whole numbers, so my labels become 0, 3, 5, 8, 10. But obviously this is not ideal since 8 displays as being halfway between 5 and 10, which it isn't, because the number is actually 7.5 and just being rounded.

Your ability to change the axis scale/labels is restricted. And to do what you're asking would take a special bit of javascript to create an appropriate scale and number of gridlines to prevent breaking things down in to funky numbers.

Basically, you want to make sure that your maximum and minimum values, and number of gridlines, allow for easy division such that you will only get whole numbers. To do that, you need to create some funky new logic. Here is some sample code that will allow you to get an appropriate min/max axis value:

// Take the Max/Min of all data values in all graphs
var totalMax = 345;
var totalMin = -123;

// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));

// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);

// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;

// Determine the range of your values
var range = newMax - newMin;

// Define the number of gridlines (default 5)
var gridlines = 5;

// Determine an appropriate gap between gridlines
var interval = range / (gridlines - 1);

// Round that interval up to the exponent of 10
var newInterval = Math.ceil(interval/roundingDec)*roundingDec;

// Re-round your max and min to the new interval
var finalMax = Math.ceil(totalMax/newInterval)*newInterval;
var finalMin = Math.floor(totalMin/newInterval)*newInterval;

There are a couple issues here (unfortunately). The first is that I am using the number of gridlines to determine the min/max values -- you want to figure out how many gridlines you should have to use nice whole numbers. The easiest way to do this, I think, would be as follows (pseudo-code only):

// Take the Max/Min of all data values in all graphs
var totalMax = 3;
var totalMin = -1;

// Figure out the largest number (positive or negative)
var biggestNumber = Math.max(Math.abs(totalMax),Math.abs(totalMin));

// Round to an exponent of 10 appropriate for the biggest number
var roundingExp = Math.floor(Math.log(biggestNumber) / Math.LN10);
var roundingDec = Math.pow(10,roundingExp);

// Round your max and min to the nearest exponent of 10
var newMax = Math.ceil(totalMax/roundingDec)*roundingDec;
var newMin = Math.floor(totalMin/roundingDec)*roundingDec;

// Determine the range of your values
var range = newMax - newMin;

// Calculate the best factor for number of gridlines (2-5 gridlines)
// If the range of numbers divided by 2 or 5 is a whole number, use it
for (var i = 2; i <= 5; ++i) {
    if (Math.round(range/i) = range/i) {
        var gridlines = i
    }
}

Then you set the gridlines option (vAxis.gridlines) to the above variable, and your max to newMax, and your min to newMin. That should give you whole number axis labels.

Note: if your numbers are really small then the above function may not work. The function is also not tested so please check it against some examples on your own time and let me know if it doesn't work properly.

Debera answered 23/1, 2013 at 4:13 Comment(3)
Glad I could help. Sorry I can't help with a bit more specific code -- I hope your javascript is better than mine!Debera
I am having integer data only, but still the chart automatically shows the the grids with decimal. On y-axis having data max 0 then getting two lines of 0.5 and 1. Which I dont needThew
format: '0' - never would've guessed that! Thanks a bunch!Benzoic
W
7

Just to expand on jmac's answer. If you know the maximum value of the chart, the solution is simple providing maxvalue ensures you always use the same height for the chart.

If your content is very dynamic however his solution seems to be the only one.

Wrand answered 23/8, 2013 at 10:5 Comment(0)
B
1

After struggling with the same Problem I came to very simple solutions. Right-click your chart, select "advanced editing" and:

1) Set the amount of vertical lines for the chart to "auto" (or manually equal to the number of data columns) - subsequently google doesen't have to calculate non-integer breakpoints. Did the trick for me.

2) Or select "Treat the Labels as Text", which will leave them untouched.

Bookrest answered 17/8, 2016 at 10:5 Comment(0)
P
0

I am programming in Java to generate the html files based on dynamic data and reusing the class. I use an ArrayList() for both the data and the options part of the BarChart html generation.

My data comes to me in a Guava Multiset which is initialized as a TreeMultiset.

Multiset<String> items = TreeMultiset.create();

options = new ArrayList<String>();

I need to make sure my items are in order, so I make a copy of the items list.

Multiset<String> occurrences = Multisets.copyHighestCountFirst(items);

For each option that I need to be dyanmic, I'll add a string. For example key is the title of my graph.

options.add("\n      title : '"+key+"'");

The first thing I do is get the highest number of occurrences in the sorted Multiset.

int max = occurrences.count(Iterables.get(occurrences,  0));

I then want to add some number of ticks to the hAxis. I have about a dozen datasets. When the max numbers are under 100, then it looks nice, but when the numbers are in the hundreds or thousands then I need to filter the "mod" function based on the value.

The other annoyance is that I wanted at least one line beyond the largest value. I'll need to adjust that too.

So adding the string into my options String array:

options.add("\n         hAxis : { title : 'Count', ticks: <<"+      
          IntStream.range(0, max+5)
                   .filter(x -> x % 4 == 0)
                   .boxed()
                   .collect(Collectors.toList())
         +">> }");      

IntStream generates all the numbers in between the values. max+5 allows one last gridline to the right of the longest bar, aka one greater than the size of the interval. Filter only shows numbers that can be mod'ed by 4, my interval that I'll adjust later. .boxed() prevents the integers from being interpreted incorrectly for printing. .collect just wraps them up into a String array.

I can then just use the options.toString() to print the strong, except, for my printing, I need to strip the array brackets of my options list, but not the ticks. e.g. [ and ]. If you look above, that's why I used "<<" and ">>".

  String optionstr = stripBrackets(options.toString())
                          .replaceAll("<<", "[")
                          .replaceAll(">>", "]");

The reason I'm posting this solution is that it gives you great control over the actual axis labeling. Other than making the interval dynamic, which I'll do next, this works pretty well for all my datasets.

Here is the final html that was printed out. Not the bracketed ticks param.

<html>
  <head>
    <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript">
      google.charts.load('current', {'packages':['corechart']});
      google.charts.setOnLoadCallback(drawVisualization);

      function drawVisualization() {
        var data = google.visualization.arrayToDataTable([
          [ 'Signals', 'Count', { role: 'style' }], 
          [ 'Agreements' , 6, 'blue' ], 
          [ 'ProductsMarkets' , 3, 'blue' ], 
          [ 'DebtEquity' , 1, 'blue' ], 
          [ 'Executive' , 1, 'blue' ], 
          [ 'Securities' , 1, 'blue' ]]);

    var options = {
         title : 'Signals', 
         vAxis : { title : 'Signals' }, 
         hAxis : { title : 'Count', ticks: [0, 4, 8] }, 
         bars: 'horizontal', 
         bar : { groupWidth : "95%" }, 
         legend : { position : "none" }
    };

    var chart = new google.visualization.BarChart(document.getElementById('signals'));
    chart.draw(data, options);
    }
    </script>
  </head>
  <body>
    <div id="signals" style="width: 900px; height: 500px;"></div>
  </body>
</html>
Powel answered 6/5, 2018 at 22:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.