13

在 d3.js 中,您可以设置要使用的 x 轴,d3.time.scale()然后设置x.domain([start_date, end_date]),它将“填充”任何不在数据中的缺失日期,值为 0。我想对 nvd3.js 做同样的事情mulitBarChart

此代码(可以直接粘贴到http://nvd3.org/livecode/#codemirrorNav中)显示了按年份统计的条形图,2002 年和 2003 年有缺失值。我想将比例设置d3.time.scale()为域到数据集的第一年和最后一年,因此缺失的年份会自动添加 0 值。我怎么做?

nv.addGraph(function() {
    var chart = nv.models.multiBarChart();

    chart.xAxis
        .tickFormat(function(d){ return d3.time.format('%y')(new Date(d)); });

    chart.yAxis
        .tickFormat(d3.format(',f'));

    chart.reduceXTicks(false);
    chart.showControls(false);

    var data = [{
      'key': 'GB by year',
      'values': [
        {x: new Date().setFullYear('2001'), y: 0.12},
        {x: new Date().setFullYear('2004'), y: 0.03},
        {x: new Date().setFullYear('2005'), y: 0.53},
        {x: new Date().setFullYear('2006'), y: 0.43},
        {x: new Date().setFullYear('2007'), y: 5.5},
        {x: new Date().setFullYear('2008'), y: 9.9},
        {x: new Date().setFullYear('2009'), y: 26.85},
        {x: new Date().setFullYear('2010'), y: 0.03},
        {x: new Date().setFullYear('2011'), y: 0.12}
      ]
    }];        

    d3.select('#chart svg')
        .datum(data)
      .transition().duration(500).call(chart);

    nv.utils.windowResize(chart.update);

    return chart;
});
4

3 回答 3

12

根据上述答案,您可以使用数字 x 值(不是 Date 对象)以及强制 X 范围和指定的 tickValues.... 对于某些类型的图表来执行此操作。

条形图似乎没有这种能力,但是 nvd3.lineCharts 可以满足您的需求。multiBarChart 模型不允许使用 forceX 函数(现在,曾经吗?)。

解决您的问题的方法是填写 0 或使用顺序图表类型(例如 lineChart)

nv.addGraph(function() {
  var chart = nv.models.lineChart()
      .forceX(2001,2011);

  var tickMarks = [2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011]

  chart.xAxis
      .tickValues(tickMarks)
      .tickFormat(function(d){ return d });

  chart.yAxis
      .tickFormat(d3.format(',f'));

  var data = [{
    'key': 'GB by year',
    'values': [
      {x: 2001, y: 0.12},
      {x: 2004, y: 0.03},
      {x: 2005, y: 0.53},
      {x: 2006, y: 0.43},
      {x: 2007, y: 5.5},
      {x: 2008, y: 9.9},
      {x: 2009, y: 26.85},
      {x: 2010, y: 0.03},
      {x: 2011, y: 0.12}
    ]
  }];        

  d3.select('#chart svg')
    .datum(data)
    .transition().duration(500).call(chart);

  nv.utils.windowResize(chart1.update);

  return chart;
});   
于 2013-04-02T22:07:22.907 回答
5

您可以通过 2 种方式执行此操作:

A)您要么重写 nvd3 的轴组件以使用 d3.time.scale() /为此用例制作另一个轴组件...

或者最简单的方法:

B)您使用轴的自定义值。首先,您使用 + 运算符 ( +(date) ) 以毫秒为单位获取值。d3 中有一个 tickValues 函数,它允许您为刻度传递自定义值。要强制 X 刻度,您可以使用 scatter 中的 forceX() 方法(我假设您已经知道这一点)并且您编写了一个简单的函数为刻度采用自定义值....因此,如果您强制刻度具有 2002 年 1 月 1 日至 2012 年 12 月 31 日之间的值,然后决定有 4 个刻度,您可以直接使用刻度或刻度值...

所以它是这样的(添加类似于 multiBarChart.js 文件的内容):

  lines.forceX(minValue, maxValue) //where minValue and maxValue are the values
  //converted to ms already after you did +(date)

  //then you just rewrite the ticks - if you want a custom number of ticks you can do it like this

  //numberOfTicks is a method I added to the axis component (axis.js) to give the number of ticks the user would like to have

  //x.domain() now contains the forced values instead of the values you initially used..
  var maxTicks = xAxis.numberOfTicks()-1, xMin = x.domain()[0], xMax = x.domain()[1], 
      xDiff = (xMax - xMin)/maxTicks, tickInterval = [];

  tickInterval[0] = xMin;

  for(i=1; i<maxTicks; i++){
    var current = xMin + i*xDiff;
    tickInterval[i] = current;
  }

  tickInterval[maxTicks] = xMax;

  //tickInterval already contains the values you want to pass to the tickValues function
  xAxis.tickValues(tickInterval);

希望这会有所帮助...我知道这是 hack,但它适用于我的情况 :) 当然,如果您已经将日期格式化为显示为年份,您将在显示刻度时获得年份的值 :)

这就是我为线条所做的。对于 multiBarChart,您需要添加一个额外的步骤:您需要处理 reduceTicks 功能(将其设置为 false,删除那部分代码,随心所欲地使用它......)

于 2013-01-24T11:54:39.673 回答
5

确实没有必要插入您的值。您实际上可以修改大多数 nvd3 图表的比例,包括 multiBarCharts,尽管需要做一些额外的工作才能使其正常工作。

您需要做的基本事情是:

var xScale = d3.time.scale();
chart.multibar.xScale(xScale);

那应该就行了!除非它没有,因为 multiBarChart 假定 xScale 是d3.scale.ordinal(). 因此,您需要通过设置xScale.rangeBandsand来伪装自己是那种类型xScale.rangeBand

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

现在的问题是获取 SOME_VALUE。这需要等于单个条的宽度,这取决于两件事:整个图表的宽度和可能存在的刻度数,包括数据中缺少的零值。

以下是 nvd3 如何在内部获取可用宽度:

var container = d3.select('#chart svg'),
    availableWidth = (chart.width() || parseInt(container.style('width')) || 960) - chart.margin().left - chart.margin().right;

但是,如果窗口调整大小,您将需要刷新此值:

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

至于获取滴答数,这完全取决于您的数据。在您的情况下,将有 11 个刻度:从 2001 年到 2011 年每年。所以我们将继续这样做。因此,整个比例定义如下所示:

var container = d3.select('#chart svg'),
    availableWidth,
    numTicks = 11,
    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);

最后,您需要手动设置 xDomain。如果你用它以前的序数尺度来做这个,它会失败,但是使用线性时间尺度它会很好地工作:

chart.xDomain([new Date().setFullYear('2001'), new Date().setFullYear('2011')]);

综上所述,这是您的示例代码(可粘贴到http://nvd3.org/livecode/#codemirrorNav中):

nv.addGraph(function() {
    var chart = nv.models.multiBarChart(),
        container = d3.select('#chart svg'),
        availableWidth,
        numTicks = 11,
        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);
    chart.xDomain([new Date().setFullYear('2001'), new Date().setFullYear('2011')]);

    chart.xAxis
        .tickFormat(function(d){ return d3.time.format('%y')(new Date(d)); });

    chart.yAxis
        .tickFormat(d3.format(',f'));

    chart.reduceXTicks(false);
    chart.showControls(false);

    var data = [{
      'key': 'GB by year',
      'values': [
        {x: new Date().setFullYear('2001'), y: 0.12},
        {x: new Date().setFullYear('2004'), y: 0.03},
        {x: new Date().setFullYear('2005'), y: 0.53},
        {x: new Date().setFullYear('2006'), y: 0.43},
        {x: new Date().setFullYear('2007'), y: 5.5},
        {x: new Date().setFullYear('2008'), y: 9.9},
        {x: new Date().setFullYear('2009'), y: 26.85},
        {x: new Date().setFullYear('2010'), y: 0.03},
        {x: new Date().setFullYear('2011'), y: 0.12}
      ]
    }];        

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

    nv.utils.windowResize(chart.update);

    return chart;
});
于 2015-12-07T20:17:56.197 回答