Multi-series bar chart in DC-js

后端 未结 4 572
隐瞒了意图╮
隐瞒了意图╮ 2020-12-10 07:07

I\'m using DC.js ( lib on top of D3 ) and have a great example of a single series bar chart:

\"enter

相关标签:
4条回答
  • 2020-12-10 07:28

    I was able to do this with a twist of the renderlet technique in the following link renderlet function for coloring

    My code goes as follows

    .renderlet(function (chart) {
        chart.selectAll('rect.bar').each(function(d){                           
        d3.select(this).attr("fill",                            
            (function(d){
                var colorcode ="grey"
                if(d.x[1] === "Zone1")   
                    colorcode ="#ff7373";
                else if(d.x[1] === "Zone2")
                    colorcode ="#b0e0e6";
                else if(d.x[1] === "Zone3")
                    colorcode ="#c0c0c0";
                else if(d.x[1] === "Zone4")
                    colorcode ="#003366";
                else if(d.x[1] === "Zone5")
                    colorcode ="#ffa500";
                else if(d.x[1] === "Zone6")
                    colorcode ="#468499";
                else if(d.x[1] === "Zone7")
                    colorcode ="#660066";   
                return colorcode;
            }))
        }); 
    
    });  
    

    Note : I was using a dimension with 2 values for the series .

    0 讨论(0)
  • 2020-12-10 07:32

    If you have a static number of groups to graph, you can achieve the desired effect with a composite chart.

    In the example below, I hard coded the gap between the bar charts - I can do this because I know there are 12 months being displayed.

                var actuals = dc.barChart(compositeChart)
                        .gap(65)
                        .group(group)
                        .valueAccessor(function (d) {
                            return d.value.Actual;
                        });
    
                var budgets = dc.barChart(compositeChart)
                        .gap(65)
                        .group(group)
                        .valueAccessor(function (d) {
                            return d.value.Budget;
                        });
    

    I pass these bar charts to the compose method of a composite chart:

                    compositeChart
                        .width(1000)
                        .height(300)
                        .dimension(monthDimension)
                        .group(group)
                        .elasticY(true)
                        .x(d3.time.scale().domain(timeExtent))
                        .xUnits(d3.time.months)
                        .round(d3.time.month.round)
                        .renderHorizontalGridLines(true)
                        .compose([budgets, actuals])
                        .brushOn(true);
    

    Finally, I add a renderlet to move one of the charts to the right a few pixels:

                  compositeChart
                        .renderlet(function (chart) {
                            chart.selectAll("g._1").attr("transform", "translate(" + 20 + ", 0)");
                            chart.selectAll("g._0").attr("transform", "translate(" + 1 + ", 0)");
                        });
    

    I know this isn't the cleanest approach but it can work in a pinch.

    I hope this helps.

    0 讨论(0)
  • 2020-12-10 07:43

    The closest thing to what you're asking for that comes to mind immediately in dc.js would be a stacked bar chart (example). But I think what you might prefer is a grouped bar chart. I'm not sure that this chart type is currently supported by dc.js. Maybe someone else knows.

    0 讨论(0)
  • 2020-12-10 07:46

    I know I’m late for this but it might help someone else.

    To create grouped-bar-chart in dc.js without overwrite the original dc code you can take advantage of ‘pretransition’ event and split the bars to create a group.

    I've created an example (jsfiddle)

    The magic happens here:

    let scaleSubChartBarWidth = chart => {
      let subs = chart.selectAll(".sub");
    
      if (typeof barPadding === 'undefined') { // first draw only
        // to percentage
        barPadding = BAR_PADDING / subs.size() * 100;
        // each bar gets half the padding
        barPadding = barPadding / 2;
      }
    
      let startAt, endAt,
          subScale = d3.scale.linear().domain([0, subs.size()]).range([0, 100]);
    
      subs.each(function (d, i) {
    
        startAt = subScale(i + 1) - subScale(1);
        endAt = subScale(i + 1);
    
        startAt += barPadding;
        endAt -= barPadding;
    
        // polygon(
        //  top-left-vertical top-left-horizontal,
        //  top-right-vertical top-right-horizontal,
        //  bottom-right-vertical bottom-right-horizontal,
        //  bottom-left-vertical bottom-left-horizontal,
        // )
    
        d3.select(this)
          .selectAll('rect')
          .attr("clip-path", `polygon(${startAt}% 0, ${endAt}% 0, ${endAt}% 100%, ${startAt}% 100%)`);
    
      });
    }; 
    

    ...

    .on("pretransition", chart => {
        scaleSubChartBarWidth(chart);
    })
    

    Complete code:

    markup

    <div id="chart-container"></div>
    

    Js

    //'use strict';
    let compositeChart = dc.compositeChart("#chart-container");
    
    const BAR_PADDING = .1; // percentage the padding will take from the bar
    const RANGE_BAND_PADDING = .5; // padding between 'groups'
    const OUTER_RANGE_BAND_PADDING = 0.5; // padding from each side of the chart
    
    let sizing = chart => {
      chart
        .width(window.innerWidth)
        .height(window.innerHeight)
        .redraw();
    };
    
    let resizing = chart => window.onresize = () => sizing(chart);
    
    let barPadding;
    let scaleSubChartBarWidth = chart => {
      let subs = chart.selectAll(".sub");
    
      if (typeof barPadding === 'undefined') { // first draw only
        // to percentage
        barPadding = BAR_PADDING / subs.size() * 100;
        // each bar gets half the padding
        barPadding = barPadding / 2;
      }
    
      let startAt, endAt,
          subScale = d3.scale.linear().domain([0, subs.size()]).range([0, 100]);
    
      subs.each(function (d, i) {
    
        startAt = subScale(i + 1) - subScale(1);
        endAt = subScale(i + 1);
    
        startAt += barPadding;
        endAt -= barPadding;
    
        // polygon(
        //  top-left-vertical top-left-horizontal,
        //  top-right-vertical top-right-horizontal,
        //  bottom-right-vertical bottom-right-horizontal,
        //  bottom-left-vertical bottom-left-horizontal,
        // )
    
        d3.select(this)
          .selectAll('rect')
          .attr("clip-path", `polygon(${startAt}% 0, ${endAt}% 0, ${endAt}% 100%, ${startAt}% 100%)`);
    
      });
    };
    
    let data = [
      {
        key: "First",
        value: [
          {key: 1, value: 0.18},
          {key: 2, value: 0.28},
          {key: 3, value: 0.68}
        ]
      },
      {
        key: "Second",
        value: [
          {key: 1, value: 0.72},
          {key: 2, value: 0.32},
          {key: 3, value: 0.82}
        ]
      },
      {
        key: "Third",
        value: [
          {key: 1, value: 0.3},
          {key: 2, value: 0.22},
          {key: 3, value: 0.7}
        ]
      },
      {
        key: "Fourth",
        value: [
          {key: 1, value: 0.18},
          {key: 2, value: 0.58},
          {key: 3, value: 0.48}
        ]
      }
    ];
    
    let ndx = crossfilter(data),
        dimension = ndx.dimension(d => d.key),
        group = {all: () => data}; // for simplicity sake (take a look at crossfilter group().reduce())
    
    let barChart1 = dc.barChart(compositeChart)
        .barPadding(0)
        .valueAccessor(d => d.value[0].value)
        .title(d => d.key + `[${d.value[0].key}]: ` + d.value[0].value)
        .colors(['#000']);
    
    let barChart2 = dc.barChart(compositeChart)
        .barPadding(0)
        .valueAccessor(d => d.value[1].value)
        .title(d => d.key + `[${d.value[1].key}]: ` + d.value[1].value)
        .colors(['#57B4F0']);
    
    let barChart3 = dc.barChart(compositeChart)
        .barPadding(0)
        .valueAccessor(d => d.value[2].value)
        .title(d => d.key + `[${d.value[2].key}]: ` + d.value[2].value)
        .colors(['#47a64a']);
    
    compositeChart
      .shareTitle(false)
      .dimension(dimension)
      .group(group)
      ._rangeBandPadding(RANGE_BAND_PADDING)
      ._outerRangeBandPadding(OUTER_RANGE_BAND_PADDING)
      .x(d3.scale.ordinal())
      .y(d3.scale.linear().domain([0, 1]))
      .xUnits(dc.units.ordinal)
      .compose([barChart1, barChart2, barChart3])
      .on("pretransition", chart => {
      scaleSubChartBarWidth(chart)
    })
      .on("filtered", (chart, filter) => {
      console.log(chart, filter);
    })
      .on("preRedraw", chart => {
      chart.rescale();
    })
      .on("preRender", chart => {
      chart.rescale();
    })
      .render();
    
    sizing(compositeChart);
    resizing(compositeChart);
    

    It's not perfect but it could give a starting point.

    0 讨论(0)
提交回复
热议问题