D3.js переходы между графиками

Я пытаюсь создать переход в своем коде гистограммы, который позволил бы пользователю щелкнуть конкретное и показать другой набор данных, связанных с этой панелью.

Это примерный набор данных:

module_category,component_category,date_repair,actual,predicted
M1,P06,2009/01,39,63
M1,P06,2009/10,3,4
M1,P06,2009/11,4,3
M1,P06,2009/12,4,2
M1,P06,2009/02,29,45
M1,P06,2009/03,29,32
M1,P06,2009/04,10,22
M1,P06,2009/05,13,15
M1,P06,2009/06,9,16
M1,P06,2009/07,7,12

Полный набор данных можно найти здесь: полный набор данных

На основании моего текущего кода я могу создать эту гистограмму:

гистограмма

но теперь я хочу добавить интерактивность, которая позволит пользователю после нажатия на панели, например, "M2", график обновляется, чтобы показать компоненты из "component_category", связанные с этим модулем, с соответствующими "фактическими" и "прогнозируемыми" Значения также отображаются в виде гистограммы.

Это мой текущий код:

var margin = {top: 20, right: 90, bottom: 30, left: 60},
    width = 980 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
        .rangeRoundBands([0, width], .1);

var x1 = d3.scale.ordinal();

var y = d3.scale.linear()
    .range([height, 0]);

var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(x0)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(d3.format(".2s"));

var svg = d3.select("#maincontent").append("svg")
    .attr('id','chart')
    .attr('viewBox', '0 0 980 500')
    .attr('perserveAspectRatio', 'xMinYMid')
    .attr('width', width + margin.right + margin.left)
    .attr('height', height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var tip=d3.tip()
            .attr("class","d3-tip")
            .offset([-10, 0])
            .html(function(d) { return "No. of repairs: " + d.value; });

d3.csv("data/Consolidated_result.csv", function(error, data) {
  if (error) throw error;

  data = d3.nest()
    .key(function(d) { return d.module_category;}).sortKeys(d3.ascending)
    .rollup(function(values){
        var counts = {}, keys = ['actual', 'predicted']
        keys.forEach(function(key){
            counts[key] = d3.sum(values, function(d){ return d[key]})
        })
        return counts
    })
    .entries(data);

    console.log(data);

  x0.domain(data.map(function(d) { return d.key; }));
  x1.domain(['actual','predicted']).rangeRoundBands([0, x0.rangeBand()]);
  // store all the values in an array
  var yval = [];
    data.forEach(function(d){
        yval.push(d.values.actual);
        yval.push(d.values.predicted);
    });
  y.domain([0, d3.max(yval)]);

  svg.call(tip);

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 0 - margin.left)
      .attr("x", 0 - (height / 2))
      .attr("dy", "1em")
      .style("text-anchor", "middle")
      .text("Number of Repairs");

  var module = svg.selectAll(".module")
      .data(data)
    .enter().append("g")
      .attr("class", "g")
      .attr("transform", function(d) { return "translate(" + x0(d.key) + ",0)"; });

  module.selectAll("rect")
    .data(function(d){
        var ary = [];
        ary.push({name:"actual", value:d.values.actual});
        ary.push({name:'predicted', value: d.values.predicted});
        return ary;
      })
    .enter().append("rect")
        .on('mouseover', tip.show)
        .on('mouseout', tip.hide)
      .on("click", function(d){
        d3.select("svg")
            .style("opacity",0)
            .remove()
            tip.hide()
            setTimeout(componentgroupedchart, 1000);
      })
        /*
      .on("click", function(d){
        d3.select(this)
            setTimeout(updateChart(name), 500);
        })*/
      .attr("width", x1.rangeBand())
      .attr("x", function(d) { return x1(d.name); })
      .attr("y", function(d) { return y(d.value); })
      .attr("height", function(d) { return height - y(d.value); })
      .style("fill", function(d) { return color(d.name); });

  var legend = svg.selectAll(".legend")
      .data(['actual','predicted'])
    .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

  legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", function(d){
        return color(d)
      });

  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) { return d; });


});

Что я хочу реализовать, так это создать функцию, в которой я обновляю диаграмму, перезагружаю данные и вкладываю их в это:

 data = d3.nest()
    .key(function(d) { return d.module_category;}).sortKeys(d3.ascending)
    .key(function(d) { return d.component_category;}).sortKeys(d3.ascending)
    .rollup(function(values){
    var counts = {}, keys = ['actual', 'predicted']
    keys.forEach(function(key){
       counts[key] = d3.sum(values, function(d){ return d[key]})
          })
       return counts
    })
    .entries(data);

Это так, что я могу получить доступ для каждого модуля:

  • количество компонентов, связанных с этим модулем
  • фактические и прогнозируемые значения ремонта

Полученные данные затем становятся:

var data = [{
   key: "M1"
   values: {
      key: "P06"
      values: {
        actual: 156 ,
        predicted: 228
      },
      key: "P09"
      values: {
        actual: 31,
        predicted: 20
      },
      key: "P12"
      values: {
        actual: 140,
        predicted: 176
      },
      key: "P15"
      values: {
        actual: 38,
        predicted: 40
      },
      key: "P16"
      values: {
        actual: 112,
        predicted:113
      },
      key: "P17"
      values: {
        actual: 20 ,
        predicted: 7
      },
      key: "P20"
      values: {
        actual: 98,
        predicted: 127
      },
      key: "P28"
      values: {
        actual: 143 ,
        predicted: 149
      },
      key: "P30"
      values: {
        actual: 16,
        predicted: 38
      }
  },
  key: "M5"
  values: {
      key: "P06"
      values: {
        actual: 61 ,
        predicted: 65
      },
      key: "P09"
      values: {
        actual: 83,
        predicted: 82
      },
      key: "P12"
      values: {
        actual: 45,
        predicted: 58
      },
      key: "P15"
      values: {
        actual: 26,
        predicted: 31
      },
      key: "P16"
      values: {
        actual: 152,
        predicted:174
      },
      key: "P21"
      values: {
        actual: 74 ,
        predicted: 120
      }
   }
}]

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

1 ответ

Решение

Это может быть достигнуто путем создания одной функции для построения графа модуля, другой для создания графа категории детализации. Определите домен с помощью функций, так как домен оси Y домен оси X будет меняться в зависимости от графика модуля / категории.

Я добавил комментарии в коде; Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать.

  var margin = {
      top: 20,
      right: 90,
      bottom: 30,
      left: 60
    },
    width = 980 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;
  var x0 = d3.scale.ordinal()
    .rangeRoundBands([0, width], .1);

  var x1 = d3.scale.ordinal();

  var y = d3.scale.linear()
    .range([height, 0]);

  var color = d3.scale.category10();

  var xAxis = d3.svg.axis()
    .scale(x0)
    .orient("bottom");

  var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(d3.format(".2s"));

  var svg = d3.select("body").append("svg")
    .attr('width', width + margin.right + margin.left)
    .attr('height', height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var tip = d3.tip()
    .attr("class", "d3-tip")
    .offset([-10, 0])
    .html(function(d) {
      return "No. of repairs: " + d.value;
    });

  d3.csv("my.csv", function(error, data) {
    if (error) throw error;
    fullData = data;
    data = d3.nest()
      .key(function(d) {
        return d.module_category;
      })
      .rollup(function(values) {
        var counts = {},
          keys = ['actual', 'predicted']
        keys.forEach(function(key) {
          counts[key] = d3.sum(values, function(d) {
            return d[key];
          })
        })
        return counts
      })
      .entries(data);
      //make the x axis
    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);
      //make the y axis
    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
      .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 0 - margin.left)
      .attr("x", 0 - (height / 2))
      .attr("dy", "1em")
      .style("text-anchor", "middle")
      .text("Number of Repairs");

    makeModuleGraph(data)

    var legend = svg.selectAll(".legend")
      .data(['actual', 'predicted'])
      .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) {
        return "translate(0," + i * 20 + ")";
      });

    legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", function(d) {
        return color(d);
      });

    legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) {
        return d;
      });
  });

  function makeModuleGraph(data) {
    var yval = [];
    data.forEach(function(d) {
      yval.push(d.values.actual);
      yval.push(d.values.predicted);
    });
    x0.domain(data.map(function(d) {
      return d.key;
    }));
    x1.domain(['actual', 'predicted']).rangeRoundBands([0, x0.rangeBand()]);

    y.domain([0, d3.max(yval)]);

    svg.call(tip);

    svg.selectAll("g .x")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.selectAll("g .y")
      .attr("class", "y axis")
      .call(yAxis);

    var module = svg.selectAll(".module")
      .data(data)
      .enter().append("g")
      .attr("class", "module")
      .attr("transform", function(d) {
        return "translate(" + x0(d.key) + ",0)";
      });

    module.selectAll("rect")
      .data(function(d) {
        var ary = [];
        ary.push({
          name: "actual",
          value: d.values.actual,
          key: d.key
        });
        ary.push({
          name: "predicted",
          value: d.values.predicted,
          key: d.key
        });
        return ary;
      })
      .enter().append("rect")
      .attr("width", x1.rangeBand())
      .attr("x", function(d) {
        return x1(d.name);
      })
      .attr("y", function(d) {
        return y(d.value);
      })
      .attr("height", function(d) {
        return height - y(d.value);
      })
      .style("fill", function(d) {
        return color(d.name);
      }).on("click", function(d) {
        makeComponentCategoryGraph(d);//make the graph for category
      });

  }

  function makeComponentCategoryGraph(d){
    var filtered = fullData.filter(function(k){ if(d.key == k.module_category){return true;}else {return false;}})
    var data = d3.nest()
      .key(function(d) {
        return d.component_category;
      })
      .rollup(function(values) {
        var counts = {},
          keys = ['actual', 'predicted']
        keys.forEach(function(key) {
          counts[key] = d3.sum(values, function(d) {
            return d[key];
          })
        })
        return counts
      })
      .entries(filtered);
          var yval = [];
    data.forEach(function(d) {
      yval.push(d.values.actual);
      yval.push(d.values.predicted);
    });
    x0.domain(data.map(function(d) {
      return d.key;
    }));
    x1.domain(['actual', 'predicted']).rangeRoundBands([0, x0.rangeBand()]);

    y.domain([0, d3.max(yval)]);

    svg.call(tip);

    svg.selectAll("g .x")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis);

    svg.selectAll("g .y")
      .attr("class", "y axis")
      .call(yAxis);
    svg.selectAll(".module").remove();//remove alll the bar graphs
    var module = svg.selectAll(".module")
      .data(data)
      .enter().append("g")
      .attr("class", "module")
      .attr("transform", function(d) {
        return "translate(" + x0(d.key) + ",0)";
      });

    module.selectAll("rect")
      .data(function(d) {
        var ary = [];
        ary.push({
          name: "actual",
          value: d.values.actual,
          key: d.key
        });
        ary.push({
          name: "predicted",
          value: d.values.predicted,
          key: d.key
        });
        return ary;
      })
      .enter().append("rect")
      .attr("width", x1.rangeBand())
      .attr("x", function(d) {
        return x1(d.name);
      })
      .attr("y", function(d) {
        return y(d.value);
      })
      .attr("height", function(d) {
        return height - y(d.value);
      })
      .style("fill", function(d) {
        return color(d.name);
      })
  }

Рабочий код здесь.

Другие вопросы по тегам