5

I am trying to plot a categorical multi axis column chart of rankings. The number 1 ranking should be the tallest column and lowest ranking the shortest.

Essentially I would like the height of the bar to be it's reciprocal.

It is very close to:

var player_name_array = ["Aaron Rodgers", "Andrew Luck", "Drew Brees", "Russell Wilson", "Peyton Manning", "Ryan Tannehill", "Tony Romo", "Matt Ryan", "Cam Newton", "Ben Roethlisberger", "Eli Manning", "Philip Rivers", "Colin Kaepernick", "Teddy Bridgewater", "Marcus Mariota", "Matthew Stafford", "Robert Griffin III", "Joe Flacco", "Jay Cutler", "Sam Bradford"];

var series_array = [{"name":"espn_ranking","data":[38,33,63,64,67,95,75,85,96,76,999,999,999,999,999,999,999,999,999,999]}];

rankings_chart = new Highcharts.Chart({
        chart: {
            renderTo:'rankings_chart',
            type: 'column'
        },
        title: {
            text: 'Draft Rankings'
        },
        subtitle: {
            text: 'Source: The Internet'
        },
        xAxis: {
            categories: player_name_array,
            crosshair: true
        },
        yAxis: {
            type: 'logarithmic',
            //reversed: true,
            title: {
                text: 'Draft Rankings'
            }
        },
        tooltip: {
            headerFormat: '<span style="font-size:14px"><b>{point.key}</b></span><table>',
            pointFormat: '<tr><td style="color:{series.color};padding:0">{series.name}: </td>' +
                '<td style="padding:0"><b>{point.y}</b></td></tr>',
            footerFormat: '</table>',
            shared: true,
            useHTML: true
        },
        plotOptions: {
            series: {
                stacking:'normal',
            },
            column: {
                pointPadding: 0.2,
                borderWidth: 0
            }
        },
        rangeSelector: {
          selected: 1
        },
        series: series_array
  });
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="rankings_chart" ></div>

The problem with this is the columns come down from the top and ranking of 1 is still the smallest column.

Is there any way to add a function for the height of each column?

Bryan
  • 15,409
  • 22
  • 92
  • 119
  • Hi Bryan ! can you please provide jsfillde or jsbin for your chart ? – Sagar R Jul 15 '16 at 06:40
  • Sure @SagarR! See the update. – Bryan Jul 15 '16 at 09:01
  • It would be also nice if you will be able to add some drawing showing what you would like to achieve. For example would you like to get something similar to this chart? http://jsfiddle.net/8yohn998/ – Grzegorz Blachliński Jul 15 '16 at 09:10
  • Here you go @SagarR See the code snippet. – Bryan Jul 15 '16 at 09:13
  • Here is a demo of the app I built. https://www.youtube.com/watch?v=vqtlBKLxedI The functionality I am speaking of is in the first 30 seconds of the video. Now I am porting it over to html this year. – Bryan Jul 15 '16 at 09:16
  • @Bryan, can you provide some example of what you would like the graph to look like after the change? can really help. – Dekel Jul 18 '16 at 12:31

3 Answers3

3

Set up your data to be the inverse of the player ranking:

var rankings = [38,33,63,64,67,95,75,85,96,76,999,999,999,999,999,999,999,999,999,999]
var inv_rankings = [];
for (i = 0; i < rankings.length; i++) {
    inv_rankings[i] = 1 / rankings[i]
}

Set your Highcharts data to be your inverse ranking:

series: {
    name: "espn_ranking",
    data: inv_rankings
}

Use a formatter for the data labels and tooltip to return the reciprocal of the reciprocal (i.e. the original value):

plotOptions: {
    series: {
        dataLabels: {
            enabled: true,
            formatter: function() {
                return 1 / this.y;
            }
        }
    },
    tooltip: {
        pointFormatter: function() {
            return 1 / this.y;
        }
    }
}

Working fiddle

nagyben
  • 901
  • 1
  • 9
  • 18
2

This was an interesting and fun puzzle to work out!

I thought the answer by nagyben was an excellent one, and I based my code on their method of inversing the scores to come up with a relative rank.

Here's a working fiddle I created based on that concept, along with several enhancements, which I've shared below: https://jsfiddle.net/brightmatrix/j4ug40qm/

  • I added a sort function to rank the data in descending order, no matter how the data were arranged in the original two arrays. In order to do this, I first combined both arrays into a single Javascript object.
  • I changed the format to a column (vertical) chart to a bar (horizontal) chart. This will make the player names more readable for your users.
  • I updated the tooltip to show the player's rank whenever a user hovers their cursor over a particular bar.
  • I removed certain chart elements that aren't needed in this kind of chart, such as the legend, gridlines, and axis labels (since you're simply doing a rank, the true value of each bar isn't relevant to the user).

Here's the sort code that I built into this:

// combine both arrays into a single object so we can then sort them by rank
var rankArray = [];
$.each(player_name_array, function(index) {
  tempArray = {};
  tempArray['name'] = player_name_array[index];
  tempArray['y'] = 1 / series_array[index];
  rankArray.push(tempArray);
});

// sort the objects by rank (the "y" value) in descending order (due to the inverse)
// see accepted answer by Stobor at:
// http://stackoverflow.com/questions/979256/sorting-an-array-of-javascript-objects
rankArray.sort(function(a, b) {
  return parseFloat(b.y) - parseFloat(a.y);
});

And here's the updated chart options:

rankings_chart = new Highcharts.Chart({
  chart: {
    renderTo:'rankings_chart',
    type: 'bar'  // chose a bar vs. column chart so the player names are easier to read
  },
  title: { text: 'Draft Rankings' },
  subtitle: { text: 'Source: The Internet' },
  legend: { enabled: false },  // there is no legend needed for this chart
  xAxis: {
    type: 'category',
    tickmarkPlacement: 'on'  // place the lines directly on the player's name
  },
  yAxis: {
    // we're measuring by rank, so there's no need for labels, gridlines, or a title
    labels: { enabled: false },
    title: { enabled: false },
    gridLineWidth: 0
  },
  tooltip: {
    pointFormatter: function() {
        // show the rank of the player, based on their sort order
      return 'ranked #' + parseInt(this.x+1);
    }
  },
  plotOptions: {
    bar: {
      groupPadding: 0.1,
      pointPadding: 0.2,
      borderWidth: 0
    }
  },
  series : [{
    name : 'Draft Data',
    data : rankArray  // our array will look like this: [{name: 'NAME', y: 0}, ...]
  }]
});

Here's the result:

enter image description here

I hope this has been helpful to you in your chart conversion.

Mike Zavarello
  • 3,386
  • 4
  • 26
  • 41
1

Since you seem to already have set an upper limit of 999 for your data, i would suggest simply plotting 1000-x.

One way to do that is to let highcharts do the math in a function for the series:

series : [{
        name : 'Draft Data',
        data : (function () {
            // generate an array of random data
            var data = [];

            for (var x = 0;x <= series_array.length; x += 1) {
                data.push([
                    1000-series_array[x];
                ]);
            }
            return data;
        }())
    }]

JSFiddle: http://jsfiddle.net/hmjnz/4/

In this case i would hide the yaxis label and instead put annotations on the columns with the rank.

Alternatively i'd debate whether a column chart is an adequate representation for this type of data.

Max Uppenkamp
  • 906
  • 4
  • 16
  • The problem with 1000-x is that the number 1 ranked player looks almost the same as number 100, and their values are very different. – Bryan Jul 19 '16 at 15:56
  • While that is true it is also very much a problem with your data. If i was to imlpement this i wouldn't display the 999 players at all, since there is no information there, and it just kills your resolution. My advice is take them out of the chart, clip to a lower number and display the unranked (i assume) players in a separate list. – Max Uppenkamp Jul 20 '16 at 07:27