2014-02-22 3 views
8

Ich verwende DC.js (lib auf der D3) und haben ein großartiges Beispiel für eine einzige Serie Balkendiagramm:Multi-Serie Balkendiagramm in DC-js

enter image description here

var xf = crossfilter(data); 
var dim = xf.dimension(function (d) { return d["EmployeeName"]; }); 
var group = dim.group().reduceSum(function (d) { return d["AverageSale"]; }); 

var chart = dc.barChart(elm); 
chart.barPadding(0.1) 
chart.outerPadding(0.05) 
chart.brushOn(false) 
chart.x(d3.scale.ordinal()); 
chart.xUnits(dc.units.ordinal); 
chart.elasticY(true); 

chart.dimension(dim).group(group); 
chart.render(); 

aber ich Ich frage mich, ob es möglich ist, mit dieser Bibliothek ein mehrdimensionales Balkendiagramm zu erstellen. Beispiel: Gruppieren nach Geschäftsname, dann Gruppieren nach Mitarbeiter, dann zeigt die y-Achse den durchschnittlichen Verkaufswert an (bereits von meinem Backend berechnet).

Die Daten wie folgt aussieht:

[{ 
    "EmployeeName": "Heather", 
    "StoreName" : "Plaza", 
    "AverageSaleValue": 200 
}{ 
    "EmployeeName": "Mellisa", 
    "StoreName" : "Plaza", 
    "AverageSaleValue": 240 
}, { 
    "EmployeeName": "Sarah", 
    "StoreName" : "Oak Park", 
    "AverageSaleValue": 300 
} ... ] 

Antwort

9

Wenn Sie eine statische Anzahl von zu zeichnenden Gruppen haben, können Sie mit einem zusammengesetzten Diagramm den gewünschten Effekt erzielen.

Im Beispiel unten habe ich die Lücke zwischen den Balkendiagrammen fest codiert - ich kann das tun, weil ich weiß, dass 12 Monate angezeigt werden.

  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 geben diese Balkendiagramme zum compose Verfahren eines Verbunddiagramm:

   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); 

Schließlich fügt man einen renderlet eines der Diagramme nach rechts zu bewegen, um ein paar Pixel:

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

Ich weiß, das ist nicht der sauberste Ansatz, aber es kann in einer Prise funktionieren.

Ich hoffe, das hilft.

+0

Danke, Problem ist meine Diagramme sind total dynamisch ... Ich habe die Menge von 'Gruppen' so vielleicht könnte ich die Breite basierend auf dem berechnen. hmm ... – amcdnl

2

Die nächste Sache, was Sie für das fragen sofort in dc.js in dem Sinn kommt ein gestapeltes Balkendiagramm (example) wäre. Aber ich denke, was Sie bevorzugen könnten, ist ein grouped bar chart. Ich bin mir nicht sicher, ob dieser Diagrammtyp derzeit von dc.js unterstützt wird. Vielleicht weiß es jemand anderes.

+0

Gruppierte Balkendiagramm ist was ich suchte, kann aber nicht glauben, dc hat dies nicht! – amcdnl

+0

Sie können erreichen, dass mit Hilfe der Transformationsfunktion von d3 –

+3

@Goutam bitte erklären –

0

Ich konnte dies mit einer Drehung der renderlet Technik in dem folgenden Link geht renderlet function for coloring

Mein Code tun, wie

.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; 
     })) 
    }); 

}); 

Hinweis folgt: Ich war für eine Dimension mit 2 Werten die Serie .

1

Ich weiß, dass ich zu spät bin, aber es könnte jemand anderem helfen.

Um ein gruppiertes Balkendiagramm in dc.js zu erstellen, ohne den ursprünglichen DC-Code zu überschreiben, können Sie das Ereignis 'pretransition' nutzen und die Balken teilen, um eine Gruppe zu erstellen.

I've created an example (jsfiddle)

Die Magie geschieht hier:

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); 
}) 

komplette 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); 

Es ist nicht perfekt, aber es könnte einen Anfangspunkt geben.