Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display values in Stacked Multi-bar chart - nvd3 Graphs

I got a scenario where in I need to display value for every stack in stacked multi-bar chart - nvd3 graph as we can display value in discrete value - nvd3 graph.

I understand, 'showvalue' is used in discrete bar controller, can we use showvalue in stacked graph, if not please suggest with an alternative solution.

Thanks in advance

like image 657
Karthik Avatar asked Jun 03 '15 13:06

Karthik


People also ask

How many data attributes are mapped to a stacked bar chart?

A stacked bar chart shows two categorical variables. The first (and primary) variable is shown along the entire length of the bar, and the second variable is represented as stacks within each categorical bar.

How to explain stacked bar graph?

The stacked bar chart (aka stacked bar graph) extends the standard bar chart from looking at numeric values across one categorical variable to two. Each bar in a standard bar chart is divided into a number of sub-bars stacked end to end, each one corresponding to a level of the second categorical variable.

When to use a stacked bar chart?

Stacked Chart. Bar charts are best used when showing comparisons between categories. Typically, the bars are proportional to the values they represent and can be plotted either horizontally or vertically. One axis of the chart shows the specific categories being compared, and the other axis represents discrete values.


1 Answers

Currently isn't possible. That option is available only in the discrete bar chart.

From the maintainer:

We don't have this ability. Stacked/Grouped charts also have complex animations making this a tricky thing to solve. We use tooltips instead.

Source https://github.com/novus/nvd3/issues/150

If you want to do this you need to make your own implementation using d3. There is a good example here http://plnkr.co/edit/BNpAlFalKz0zkkSszHh1?p=preview. It's using the angular wrapper but it's a good starting point.

var app = angular.module('app', ['nvd3ChartDirectives']);

app.controller('chartCtrl', function($scope, $timeout) {

  var ANIMATION_TIME = 1500,
    countSeriesDisplayed = 2,
    promise,
    labels = ["label1", "label2", "label3", "label4", "label5"];

  $scope.isStacked = false;

  // Example data
  $scope.chartData = [{
    "key": "Series 1",
    "values": [
      [0, 10],
      [1, 20],
      [2, 30],
      [3, 40],
      [4, 50]
    ]
  }, {
    "key": "Series 2",
    "values": [
      [0, 10],
      [1, 40],
      [2, 60],
      [3, 20],
      [4, 40]
    ]
  }];

  /* To add labels, images, or other nodes on the created SVG, we need to wait
   *  for the chart to be rendered with a callback.
   *  Once the chart is rendered, a timeout is set to wait for the animation to
   *  finish.
   *
   *  Then, we need to find the position of the labels and set it with the
   *  transform attribute in SVG.
   *  To do so, we have to get the width and height of each bar/group of bar 
   *  which changes if stacked or not
   *
   */

  // Callback called when the chartData is assigned
  $scope.initLabels = function() {
    return function(graph) {
      promise = $timeout(function() {
        var svg = d3.select("svg"),
          lastRects, rectWidth,
          heightForXvalue = []; // Used for grouped mode

        // We get one positive rect of each serie from the svg (here the last serie)
        lastRects = svg.selectAll("g.nv-group").filter(
          function(d, i) {
            return i == countSeriesDisplayed - 1;
          }).selectAll("rect.positive");

        if ($scope.isStacked) {
          // If stacked, we get the width of one rect
          rectWidth = lastRects.filter(
            function(d, i) {
              return i == countSeriesDisplayed - 1;
            }).attr("width");
        } else {
          // If grouped, we need to get the greatest height of each bar
          var nvGroups = svg.selectAll("g.nv-group").selectAll("rect.positive");
          nvGroups.each(
            function(d, i) {
              // Get the Min height space for each group (Max height for each group)
              var rectHeight = parseFloat(d3.select(this).attr("y"));
              if (angular.isUndefined(heightForXvalue[i])) {
                heightForXvalue[i] = rectHeight;
              } else {
                if (rectHeight < heightForXvalue[i]) {
                  heightForXvalue[i] = rectHeight;
                }
              }
            }
          );

          // We get the width of one rect multiplied by the number of series displayed
          rectWidth = lastRects.filter(
            function(d, i) {
              return i == countSeriesDisplayed - 1;
            }).attr("width") * countSeriesDisplayed;
        }

        // We choose a width equals to 70% of the group width
        var labelWidth = rectWidth * 70 / 100;

        var groupLabels = svg.select("g.nv-barsWrap").append("g");

        lastRects.each(
          function(d, index) {
            var transformAttr = d3.select(this).attr("transform");
            var yPos = parseFloat(d3.select(this).attr("y"));
            groupLabels.append("text")
              .attr("x", (rectWidth / 2) - (labelWidth /2)) // We center the label
              // We add a padding of 5 above the highest rect
              .attr("y", (angular.isUndefined(heightForXvalue[index]) ? yPos : heightForXvalue[index]) - 5)
              // We set the text
              .text(labels[index])
              .attr("transform", transformAttr)
              .attr("class", "bar-chart-label");
          });

      }, ANIMATION_TIME);
    }
  };

  // Tooltips
  $scope.toolTipContentFunction = function () {
    return function (key, x, y, e, graph) {
      return labels[x];
    }
};

  $scope.$on('$destroy', function () {
    // Cancel timeout if still active
    $timeout.cancel(promise);
  });

});

UPDATE:

I've created a gist that could help you to implement this by yourself.

https://gist.github.com/topicus/217444acb4204f364e46

like image 146
Topicus Avatar answered Nov 01 '22 19:11

Topicus