dc.js动态瀑布图

问题描述 投票:1回答:1

Vistas在github上有一个很好的例子,其中设置了如何在dc.js中制作瀑布。它使用第二个数据集实际创建堆积条形图的底部。但是,如果您在第一个数据集中进行过滤,由于堆叠图表的底部值是固定的,因此它将无法正常工作。

因此,我的问题是可以根据此公式计算d.value,因此不需要第二个数据集(dummy_data):

Dummy value of current column = previous dummy value + previous real data value

从而将第一列和最后一列的值设置为0

JSFiddle

代码

<!DOCTYPE html>
<html lang='en'>
<head>
  <meta charset='utf-8'>
  <meta content='width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0' name='viewport'>

  <title>Waterfall Chart with DC.js</title>

  <script src='js/d3.js' type='text/javascript'></script>
  <script src='js/crossfilter.js' type='text/javascript'></script>
  <script src='js/reductio.js' type='text/javascript'></script>
  <script src='js/dc.js' type='text/javascript'></script>
  <link href='css/dc.css' rel='stylesheet' type='text/css'>
</head>
<body>
  <div class='pie-graph span6' id='dc-waterfall-chart'></div>
<script>
  var waterfallChart = dc.barChart("#dc-waterfall-chart");
  var original_data = [];
  var dummy_data = [];

  //creating example data - could easily be any data reading process from sources like CSV or JSON
  original_data.push({item: "x0", value: 10});
  original_data.push({item: "x1", value: 2});
  original_data.push({item: "x2", value: -1});
  original_data.push({item: "x3", value: -3});
  original_data.push({item: "x4", value: 8});

  //creating the dummy data, the invisible columns supporting the waterfall chart. 
  //This is going to be the first stack whereas the real data (original_data) is the 
  //second stack
  dummy_data.push({item: "x0", value: 0});
  dummy_data.push({item: "x1", value: 10});
  dummy_data.push({item: "x2", value: 12});
  dummy_data.push({item: "x3", value: 11});
  dummy_data.push({item: "x4", value: 0});

  //creating crossfilter based off of the real data. Again, you can have your own crossfilter creation process here.
  var ndx = crossfilter(original_data);
  var itemDimension = ndx.dimension(function (d) { return d.item; });
  var reducerValue = reductio().count(true).sum(function(d) { return d.value; }).avg(true); 
  var itemGroup = itemDimension.group();
  var grp = reducerValue(itemGroup);

  // we should also have a similar cross filter on the dummy data
  var ndx_dummy = crossfilter(dummy_data);
  var itemDimension_dummy = ndx_dummy.dimension(function (d) { return d.item; });
  var reducerValue_dummy = reductio().count(true).sum(function(d) { return d.value; }).avg(true); 
  var itemGroup_dummy = itemDimension_dummy.group();
  var dummy_grp = reducerValue_dummy(itemGroup_dummy);

  waterfallChart.width(600)
  .height(400)
  .margins({top: 5, right: 40, bottom: 80, left: 40})
  .dimension(itemDimension)
  .group(dummy_grp)
  .valueAccessor(function (d) { // specific to reductio
    return d.value.sum; 
    })
  .title(function(d){ 
    return (d.key + "  (" + d.value.sum+ ")" );
  })
  .transitionDuration(1000)
  .centerBar(false) 
  .gap(7)                    
  .x(d3.scaleBand())
  .xUnits(dc.units.ordinal)
    .controlsUseVisibility(true)
    .addFilterHandler(function(filters, filter) {return [filter];})
  .elasticY(true)
  .xAxis().tickFormat(function(v) {return v;});

  waterfallChart.stack(grp,"x")

  waterfallChart.on("pretransition",function (chart) {
    //coloring the bars
    chart.selectAll("rect.bar").style("fill", function(d){return "white";});
    chart.selectAll("rect.bar").style("stroke", "#ccc");//change the color to white if you want a clean waterfall without dashed boundaries
    chart.selectAll("rect.bar").style("stroke-dasharray", "1,0,2,0,1");

    // stack._1 is your real data, whereas stack._0 is the dummy data. You want to treat the styling of these stacks differently
    chart.selectAll("svg g g.chart-body g.stack._1 rect.bar").style("fill", function(d){console.log(d.data.value.sum);if (d.data.value.sum >0) return '#ff7c19'; else return '#7c7c7c';});
    chart.selectAll("svg g g.chart-body g.stack._1 rect.bar").style("stroke", "white");
    chart.selectAll("svg g g.chart-body g.stack._1 rect.bar").style("stroke-dasharray", "1");
    // chose the color of deselected bars, but only for the real data.
    chart.selectAll("svg g g.chart-body g.stack._1 rect.deselected").style("fill", function (d) {return '#ccc';});
    chart.selectAll('g.x text').style('fill', '#8e8e8e');
    chart.selectAll('g.y text').style('fill', '#777777');
    chart.selectAll('g.x text').style('font-size', '10.5px');
    chart.selectAll('g.y.axis g.tick line').style("stroke", "#f46542");
    chart.selectAll('.domain').style("stroke","#c6c6c6");
    chart.selectAll('rect.bar').on("contextmenu",function(d){d3.event.preventDefault();});
  });
  dc.renderAll();
</script>
</body>
</html>
dc.js crossfilter
1个回答
0
投票
function waterfall_group(group, endkey, acc) { acc = acc || (x => x); return { all: () => { let cumulate = 0; let all = group.all().map(({key,value}) => { value = acc(value) const kv = { key, value: { baseline: cumulate, data: value } }; cumulate += value; return kv; }); return all.concat([{key: endkey, value: {baseline: 0, data: cumulate}}]); } }; }

此函数是最后一个“ sum total”栏的键和访问器函数,在这里需要此函数,因为reductionio将值包装在一个额外的对象中。

[它返回一个值为{baseline,data}的组,其中baseline是不可见堆栈所需的虚拟值,data是条形图的值。

构建类似的伪造组

var waterfall_group = waterfall_group(grp, 'x5', x => x.sum);

并通过访问器将其传递到.group().stack()以获取子值:

waterfallChart
  .group(waterfall_group, 'baseline', kv => { return kv.value.baseline; })
  .stack(waterfall_group, 'data', kv => kv.value.data)

我还更改了着色代码以获取新的数据格式:

chart.selectAll("svg g g.chart-body g.stack._1 rect.bar")
  .style("fill", function(d){if (d.data.value.data >0) return '#ff7c19'; else return '#7c7c7c';});

为了进行测试,我添加了另一个“类别”字段和一个饼图。请注意,瀑布图可能会进入带有负值和零值的某些怪异状态(例如,单击“ C”),但看起来正确。

waterfall shot

Fork of your fiddle

© www.soinside.com 2019 - 2024. All rights reserved.