Подтвердить что ты не робот

Как установить домен и масштаб по оси на nvd3.js multiBarChart

В 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;
});
4b9b3361

Ответ 1

Нет необходимости интерполировать ваши значения. Фактически вы можете изменить масштаб большинства диаграмм nvd3, включая multiBarCharts, хотя есть некоторые дополнительные работы, которые необходимо выполнить, чтобы заставить его работать.

Основное, что вам нужно сделать, это следующее:

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

Тогда это должно сработать! Кроме этого нет, потому что multiBarChart предполагает, что xScale d3.scale.ordinal(). Поэтому вам нужно подделать этот тип, установив xScale.rangeBands и 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;
});

Ответ 2

Основываясь на приведенном выше ответе, вы можете сделать это с числовыми значениями 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;
});   

Ответ 3

Вы можете сделать это двумя способами:

A) Вы переписываете компонент оси nvd3 для использования d3.time.scale()/создаете другой компонент оси для этого случая использования...

Или самый простой способ:

B) Вы используете пользовательские значения для оси. Прежде всего, вы используете оператор + (+ (дата)), чтобы иметь значения в мс. В d3 есть функция tickValues, которая позволяет передавать пользовательские значения для тиков. Чтобы заставить масштаб X вы используете метод forceX() из разброса (я предполагаю, что вы уже знаете об этом), и вы пишете простую функцию, которая принимает пользовательские значения для тиков.... Итак, если вы заставляете свой масштаб иметь значения между 1 января 2002 года и 31 декабря 2012 года, а затем решили иметь 4 тика, вы можете использовать либо тики напрямую, либо tickValues ​​...

Итак, это похоже на это (добавьте что-то похожее на файл 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);

Надеюсь, это поможет... Я знаю, что это взломало, но это сработало в моем случае:) И, конечно, если вы уже отформатировали дату для отображения в год, вы получите значения за годы, когда показываете тики:)

Так я сделал это для строк. Для multiBarChart вам нужно добавить дополнительный шаг: вам нужно иметь дело с функциональностью reduceTicks (установить его в false, удалить часть кода, делать с ней все, что вам нравится)