Как интегрировать d c. js с диаграммой горизонта d3 для визуализации данных? - PullRequest
1 голос
/ 29 марта 2020

Этот образец c. js показывает линейный график ( источник ). Интересно, как можно заменить его один focusChart набором диаграмм горизонта, используя d3-horizon-chart ( пример источника )?

Здесь (JSFiddle пример кода) - это то место, где я застреваю - он не показывает данных на графиках d c ... Пожалуйста, помогите = (

class dcHorizonChart {
  constructor(parent, groupByKeyName, valueKeyName, group ) {
    this.data = null;
    this.height = 30
    this._groupByKeyIdx = groupByKeyName;
    this._valueKeyName = valueKeyName;
    
    this._root = d3.select(parent);
    dc.registerChart(this, group);    
  }
  
  groupAll(groupAll) {
  console.log("gac")
    if(!arguments.length)
      return this._groupAll;
    this._groupAll = groupAll;
    return this;
  }
  
  setData(data) {
  	this.data = data
  }
  
  render() {
		console.log("called once");
    this.redraw();
  }

  redraw() {
//  console.log(this.data.all())
   //console.log(this.data.all())
  
     var ndata = Enumerable.From(this.data.all()).Select("r=> { 'name': r.key["+this._groupByKeyIdx+"], 'val': r.key["+this._valueKeyName + "]}");
      ndata = ndata.GroupBy("$.name", "$.val" ).Select("{'id': $.Key(), 'values':$.ToArray()}")
      .ToArray();
    console.log(ndata)
 
   this._root.html(null)
 
     this._root.selectAll('.horizon')
    .data(ndata)
    .enter()
    .append('div')
    .attr('class', 'horizon')
    .each(function(d) {
      d3.horizonChart()
        .title(d.id)
        .call(this, d.values);
    });
    
    
    console.log("called");
  /*
    d3.select('body').selectAll('.horizon')
    .data(stocks)
    .enter()
    .append('div')
    .attr('class', 'horizon')
    .each(function(d) {
      d3.horizonChart()
        .title(d.stock)
        .call(this, d.values);
    });
  
    this._rect.transition()
      .duration(this._duration)
      .attr('fill', this._colors(this._groupAll.value()));
      */
  }
  
}

function loadStockData(stock, callback) {
  d3.csv('https://bost.ocks.org/mike/cubism/intro/stocks/' + stock + '.csv').then(function(rows) {
    rows = rows.map(function(d) {
      return [d3.timeParse(d.Date), +d.Open];
    }).filter(function(d) {
      return d[1];
    }).reverse();

    var date = rows[0][0],
      compare = rows[400][1],
      value = rows[0][1],
      values = [],
      indices = [];

    rows.forEach(function(d, i) {
      values.push(value = (d[1] - compare) / compare);
      indices.push(i);
    });

    
    callback({
      'stock': stock,
      'values': values,
      'indices': indices
    });
  });
}

var promises = [];
['AAPL', 'GOOG', 'MSFT'].forEach(function(stock) {
  promises.push(new Promise(function(resolve, reject) {
    var r = loadStockData(stock, resolve);
  }));
});

Promise.all(promises).then(function(stocks) {
  console.log(stocks);

  
  var data = [];
  data = Enumerable.From(stocks)
  .SelectMany( "val, index=>" +
      "Enumerable.From(val.values)" +
                ".Select(\"v,i => {'value': v, 'idx':i, 'name':'\" + val.stock + \"' } \")")
  .ToArray();
  /*
  for(var i = 0; i < stocks.length; i++) {
    for(var j= 0; j < stocks[i].indices.length; j++) {
        data.push({ 'idx':stocks[i].indices[j], 'name': stocks[i].stock, 'value': stocks[i].values[j] })
    }
  }
  */
  
  console.log(data);
  var ndx, runDimension, runGroup, overviewRunDimension, overviewRunGroup;
  ndx = crossfilter(data);
  var allDim = ndx.dimension(function(d){ return [d.idx, d.name, d.value] ;});
  
  runDimension = ndx.dimension(function(d) {return [d.name, d.idx]; });
  overviewRunDimension = ndx.dimension(function(d) {return [d.name, d.idx]; });
  runGroup = runDimension.group().reduceSum(function(d) { return d.value; });
  overviewRunGroup = overviewRunDimension.group().reduceSum(function(d) { return d.value; });

 var horizonChart = new dcHorizonChart("#test-hc", 1,2);
 horizonChart.setData(allDim.group());
 
 var overviewChart =  dc.seriesChart("#test-overview");
  overviewChart
    .width(768)
    .height(100)
    .chart(function(c) { return dc.lineChart(c).curve(d3.curveCardinal); })
    .x(d3.scaleLinear().domain([0,20]))
    .brushOn(true)
    .xAxisLabel("Run")
    .clipPadding(10)
    .dimension(runDimension)
    .group(runGroup)
    .seriesAccessor(function(d) {return "Expt: " + d.key[0];})
    .keyAccessor(function(d) {return +d.key[1];})
    .valueAccessor(function(d) {return +d.value;});



  dc.renderAll();
});
 body {
        margin: 0;
        padding: 0;
    }

    .horizon {
        border-top: solid 1px #000;
        border-bottom: solid 1px #000;
        overflow: hidden;
        position: relative;
    }

    .horizon + .horizon {
        border-top: none;
    }

    .horizon canvas {
        display: block;
        image-rendering: pixelated;
    }

  .horizon .title,
  .horizon .value {
      bottom: 0;
      line-height: 30px;
      margin: 0 6px;
      position: absolute;
      font-family: sans-serif;
      text-shadow: 0 1px 0 rgba(255,255,255,.5);
      white-space: nowrap;
  }

  .horizon .title {
      left: 0;
  }

  .horizon .value {
      right: 0;
  }
<!DOCTYPE html>
<html lang="en">

  <head>
    <title>dc.js - Custom Chart Example</title>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="//dc-js.github.io/dc.js/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="//unpkg.com/dc@4/dist/style/dc.css" />

    <script src="//d3js.org/d3.v5.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.4/crossfilter.min.js"></script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dc/3.2.1/dc.min.js"></script>


    <script src="//npmcdn.com/d3-horizon-chart/build/d3-horizon-chart.min.js"></script>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
  </head>

  <body>

    <div class="container">
      <div id="test-overview"></div>
      <div id="bar"></div>
      
      <br/>
      AAA
      <br>
      <div id="test-hc"></div>

    </div>
  </body>

</html>

1 Ответ

1 голос
/ 05 апреля 2020

Я создал пример диаграммы горизонта для d c. js.

horizon chart example

Основная проблема с Интеграция d3-horizon-chart с d c. js заключается в том, что он не имеет шкалы X - он просто отображает каждое значение в виде 1-пиксельного столбца цвета в порядке массива.

Это означает, что он не использует групповые ключи, и данные должны быть отсортированы в правильном порядке перед построением диаграммы горизонта.

Вот определение диаграммы:

  class HorizonChart {
      constructor(parent, group) {
          this._group = null;
          this._colors = null;
          this._seriesAccessor = null;
          this._root = d3.select(parent);
          dc.registerChart(this, group);
      }

      // initialization functions for user

      group(group) {
          if(!arguments.length)
              return this._group;
          this._group = group;
          return this;
      }

      // takes array of colors (not scale)
      colors(colors) {
          if(!arguments.length)
              return this._colors;
          this._colors = colors;
          return this;
      }

      seriesAccessor(seriesAccessor) {
          if(!arguments.length)
              return this._seriesAccessor;
          this._seriesAccessor = seriesAccessor;
          return this;
      }

      valueAccessor(valueAccessor) {
          if(!arguments.length)
              return this._valueAccessor;
          this._valueAccessor = valueAccessor;
          return this;
      }

      // interface for dc.js chart registry

      render() {
          this.redraw();
      }

      redraw() {
          const nester = d3.nest().key(this._seriesAccessor),
                nesting = nester.entries(this._group.all());
          let horizon = this._root.selectAll('.horizon')
              .data(nesting);
          horizon = horizon.enter()
              .append('div')
              .attr('class', 'horizon')
              .merge(horizon);
          const colors = this._colors,
                valueAccessor = this._valueAccessor;
          horizon
              .each(function(series) {
                  d3.select(this).selectAll('*').remove();
                  d3.horizonChart()
                      .colors(typeof colors === 'function' ? colors(series.key) : colors)
                      .title(series.key)
                      .call(this, series.values.map(valueAccessor));
            });
      }

Он реализует небольшую часть интерфейса диаграммы серии d c. js . Одно из отличий состоит в том, что d3-horizon-chart принимает массив цветов и решает, сколько положительных и отрицательных полос нарисовать, основываясь на длине этого массива. Так что HorizonChart.colors() принимает либо массив цветов, либо функцию, которая берет ключ серии и возвращает массив цветов.

Используется d3.nest () для разделения данных на серийный аксессор. Затем он добавляет или удаляет div для диаграмм горизонта, удаляет что-либо внутри каждого div и dr aws там горизонтальную диаграмму.

сортировка данных

Поскольку данные должны быть отсортированы, и то, что мы получаем из group.all() для мультиключа, будет неправильно отсортировано, мы можем использовать поддельную группу для упорядочения по двум элементам ключа:

  function sort_multikey_group(group) {
      return {
          all: () => {
              return group.all().slice().sort(
                  ({key: keyA}, {key: keyB}) => d3.ascending(keyA[0],keyB[0]) || d3.ascending(keyA[1],keyB[1]));
          }
      };
  }

Мы применяем поддельную группу при инициализации диаграммы горизонта :

  var horizonChart = new HorizonChart("#horizon"),
  horizonChart
      .group(sort_multikey_group(exptRunGroup))
      .colors(n => [d3.schemeBlues, d3.schemeOranges, d3.schemeGreens, d3.schemeReds, d3.schemePurples][n-1][6]) // levels * 2
      .seriesAccessor(d => d.key[0])
      .valueAccessor(d => d.value - 500)

Параметр функции .colors() выбирает одну из цветовых схем D3 для каждой из диаграмм горизонта. Он выбирает схему с 6 цветами для отображения 3 положительных и 3 отрицательных полос.

Он помещает данные в каждую из диаграмм серии на основе первого элемента ключа. В этом примере данные смещены ниже, чтобы использовать отрицательные цветные полосы.

примерные данные

Данные в этом примере - это данные эксперимента Майкельсона-Морли, использованные в d c. js Пример диаграммы серии , с интерполяцией 20 точек для каждого:

  const experiments2 = d3.range(experiments.length-1).flatMap(i => {
      if(experiments[i].Expt !== experiments[i+1].Expt)
          return [];
      let {Expt, Run, Speed: Speed0} = experiments[i],
            {Speed: Speed1} = experiments[i+1];
      Expt = +Expt; Run = +Run; Speed0 = +Speed0; Speed1 = +Speed1;
      const terp = d3.scaleLinear().range([Speed0, Speed1]);
      return d3.range(mult).map(j => ({Expt, Run: Run + j/mult, Speed: terp(j/mult)}));
  });

Это еще одно различие между d3-horizon-chart и встроенным d c. js диаграммы: нужны данные для каждого пикселя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...