nvd3 multibarchart last bar is hidden not visible
Asked Answered
I

2

0

Based on this stackoverflow answer I was trying to copy essential parts to get proper scale for timeline. I use multibarcharts for multible graphs, from few records to hundreds with X-axis having data from 1930 to today.

I've copied it like this, but I have two issues:

  1. Last bar is always outside of graph
  2. bars overlap, which I can partly fix by altering numTicks, but isn't there a better way?

    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script>
    <script type="text/javascript">
        nv.addGraph(function() {
          var data = [{
            "values": [
    
                    { x: new Date("1999-12-01"), y: 42.27 } , 
    
                    { x: new Date("2000-12-01"), y: 41.73 } , 
    
                    { x: new Date("2001-12-01"), y: 41.34 } , 
    
                    { x: new Date("2002-12-01"), y: 41.84 } , 
    
                    { x: new Date("2003-12-01"), y: 43.93 } , 
    
                    { x: new Date("2004-12-01"), y: 42.18 } , 
    
                    { x: new Date("2005-12-01"), y: 42.31 } , 
    
                    { x: new Date("2006-12-01"), y: 43.14 } , 
    
                    { x: new Date("2007-12-01"), y: 43.24 } , 
    
                    { x: new Date("2008-12-01"), y: 39.30 } , 
    
                    { x: new Date("2009-12-01"), y: 43.80 } , 
    
                    { x: new Date("2010-12-01"), y: 44.10 } , 
    
                    { x: new Date("2011-12-01"), y: 54.10 } , 
    
                    { x: new Date("2012-12-01"), y: 62.10 } , 
    
                    { x: new Date("2013-12-01"), y: 56.70 } , 
    
                    { x: new Date("2014-12-01"), y: 45 } , 
    
                    { x: new Date("2015-12-01"), y: 55.60 } , 
    
                    { x: new Date("2026-12-01"), y: 54.40 } , 
    
                    { x: new Date("2027-12-01"), y: 57 } 
    
            ],
            "bar": true,
            "key": "Payout Ratio"
          }];
    
          var chart = nv.models.multiBarChart(),
              container = d3.select('#payout_ratio_chart svg'),
              availableWidth,
              numTicks = data[0].values.length,
              xScale = d3.time.scale();
    
          function updateAvailableWidth() {
              availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
          }
          updateAvailableWidth();
          nv.utils.windowResize(updateAvailableWidth);
    
          xScale.rangeBands = xScale.range;
          xScale.rangeBand = function() { return (1 - chart.groupSpacing()) * availableWidth / numTicks; };
    
          chart.multibar.xScale(xScale);
    
          var last_date = data[0].values[data[0].values.length-1].x;
          last_date.setMonth(last_date.getMonth() + 10);
          chart.xDomain([data[0].values[0].x, last_date]);
    
          chart.xAxis.tickFormat(function(d){ return d3.time.format('%b %Y')(new Date(d)); });
          chart.yAxis.tickFormat(d3.format(',f'));
    
    
          chart.showControls(false);
    
          container.datum(data).transition().duration(500).call(chart);
    
          nv.utils.windowResize(chart.update);
    
          return chart;
      });
    </script>
    

https://jsfiddle.net/lucas03/poamvfke/4/

Imperceptible answered 1/12, 2018 at 22:47 Comment(0)
N
1

Checkout the code below which was derived from your posted code:

nv.addGraph(function() {
  var data = [{
    "values": [

      {
        x: new Date("1999-12-01"),
        y: 42.27
      },

      {
        x: new Date("2000-12-01"),
        y: 41.73
      },

      {
        x: new Date("2001-12-01"),
        y: 41.34
      },

      {
        x: new Date("2002-12-01"),
        y: 41.84
      },

      {
        x: new Date("2003-12-01"),
        y: 43.93
      },

      {
        x: new Date("2004-12-01"),
        y: 42.18
      },

      {
        x: new Date("2005-12-01"),
        y: 42.31
      },

      {
        x: new Date("2006-12-01"),
        y: 43.14
      },

      {
        x: new Date("2007-12-01"),
        y: 43.24
      },

      {
        x: new Date("2008-12-01"),
        y: 39.30
      },

      {
        x: new Date("2009-12-01"),
        y: 43.80
      },

      {
        x: new Date("2010-12-01"),
        y: 44.10
      },

      {
        x: new Date("2011-12-01"),
        y: 54.10
      },

      {
        x: new Date("2012-12-01"),
        y: 62.10
      },

      {
        x: new Date("2013-12-01"),
        y: 56.70
      },

      {
        x: new Date("2014-12-01"),
        y: 45
      },

      {
        x: new Date("2015-12-01"),
        y: 55.60
      },

      {
        x: new Date("2026-12-01"),
        y: 54.40
      },

      {
        x: new Date("2027-12-01"),
        y: 57
      }

    ],
    "bar": true,
    "key": "Payout Ratio"
  }];


  var fDate = data[0].values[0].x,
  lDate = new Date(data[0].values[data[0].values.length - 1].x);
  lDate.setFullYear(lDate.getFullYear() + 1);
  
  var chart = nv.models.multiBarChart()
    .showControls(false)
    .reduceXTicks(false)
    .rotateLabels(-45),
    container = d3.select('#payout_ratio_chart svg'),
    availableWidth,
    numTicks = (lDate.getFullYear() - fDate.getFullYear()) + 1,
    xScale = d3.time.scale();

  function updateAvailableWidth() {
    availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;
  }
  updateAvailableWidth();

  xScale.rangeBands = xScale.range;
  xScale.rangeBand = function() {
    return (1 - chart.groupSpacing()) * availableWidth / numTicks;
  };

  chart.multibar.xScale(xScale);

  chart.xDomain([fDate, lDate]);

  chart.xAxis.tickFormat(function(d) {
    return d3.time.format('%b %Y')(new Date(d));
  });
  chart.yAxis.tickFormat(d3.format(',f'));

  container.datum(data).transition().duration(500).call(chart);

  nv.utils.windowResize(function() {
    updateAvailableWidth();
    chart.update();
  });

  return chart;
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.css" rel="stylesheet"/>
<div id="payout_ratio_chart">
  <svg style="width:100%;height:400px" />
</div>

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script>

Fixes:

Only difference between your code and fixed code is how fDate, lDate and numTicks are calculated.

fDate is your start date while lDate is last date in data for the following year. Lastly, numTicks is difference between years in fDate and lDate.

Find jsfiddle here.

Noh answered 6/12, 2018 at 3:45 Comment(4)
Hi, thanks for answer. But this was previous version of my graph, based on stackoverflow link in my question I added scale, as I want realistic timeline (x-axis). Notice how last two years are 10 years apart, I need some gaps there :)Imperceptible
@Imperceptible Oh sorry, I didn't realize that, I 'll try to fix it when I get a chance today.Noh
didn't you get a chance? :(Imperceptible
@Imperceptible Was out of town for couple of days, check out the updates.Noh
C
0

You have a bar chart so you have to insert data points for not existing dates.

Here I have hard coded but you can write some logic to add missing dates. For some other data serie you can have values for the missing dates of this serie.

nvd3 draws these bars with a minimal height of 1px, so add a style to hide these small bars

.nvd3 .nv-groups rect[height="1"] {
    opacity: 0;
}

nv.addGraph(function() {
  var data = [{
    "values": [
      { x: new Date("1999-12-01"), y: 42.27 },
      { x: new Date("2000-12-01"), y: 41.73 },
      { x: new Date("2001-12-01"), y: 41.34 },
      { x: new Date("2002-12-01"), y: 41.84 },
      { x: new Date("2003-12-01"), y: 43.93 },
      { x: new Date("2004-12-01"), y: 42.18 },
      { x: new Date("2005-12-01"), y: 42.31 },
      { x: new Date("2006-12-01"), y: 43.14 },
      { x: new Date("2007-12-01"), y: 43.24 },
      { x: new Date("2008-12-01"), y: 39.30 },
      { x: new Date("2009-12-01"), y: 43.80 },
      { x: new Date("2010-12-01"), y: 44.10 },
      { x: new Date("2011-12-01"), y: 54.10 },
      { x: new Date("2012-12-01"), y: 62.10 },
      { x: new Date("2013-12-01"), y: 56.70 },
      { x: new Date("2014-12-01"), y: 45 },
      { x: new Date("2015-12-01"), y: 55.60 },
      { x: new Date("2016-12-01"), y: 0 },
      { x: new Date("2017-12-01"), y: 0 },
      { x: new Date("2018-12-01"), y: 0 },
      { x: new Date("2019-12-01"), y: 0 },
      { x: new Date("2020-12-01"), y: 0 },
      { x: new Date("2021-12-01"), y: 0 },
      { x: new Date("2022-12-01"), y: 0 },
      { x: new Date("2023-12-01"), y: 0 },
      { x: new Date("2024-12-01"), y: 0 },
      { x: new Date("2025-12-01"), y: 0 },
      { x: new Date("2026-12-01"), y: 54.40 },
      { x: new Date("2027-12-01"), y: 57 }
    ],
    "bar": true,
    "key": "Payout Ratio"
  }];

  var chart = nv.models.multiBarChart(),
    container = d3.select('#payout_ratio_chart svg');

  chart.xAxis.tickFormat(function(d) {
    return d3.time.format('%b %Y')(new Date(d));
  });
  chart.yAxis.tickFormat(d3.format(',f'));

  chart.showControls(false);

  container.datum(data).transition().duration(500).call(chart);

  nv.utils.windowResize(chart.update);

  return chart;
});
.nvd3 .nv-groups rect[height="1"] {
    opacity: 0;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.css" rel="stylesheet"/>
<div id="payout_ratio_chart">
  <svg style="width:100%;height:400px" />
</div>

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.1/nv.d3.js"></script>
Cony answered 9/12, 2018 at 12:45 Comment(4)
This is what I was doing till now, but these graphs are dynamically generated and I thought chart library must support this. It's a bit silly, adding there 0 values for proper rendering :(Imperceptible
that is what I am doing, I was just expecting that this should be a responsibility of graph library. When I saw mentioned stackoverflow question with xScale, I thought just small part is missing.Imperceptible
@Lucas03: who knows best what the broken input data is? Do you expect the library to handle all kind of shit and still render the graph as intended? No the library only accepts clean data series. Based on what should it add some missing data elements, it is a BAR CHART!Cony
:) based on timeline, if key is date. Maybe nvd3 does not support this, I don't know. Thanks for replies!Imperceptible

© 2022 - 2024 — McMap. All rights reserved.