2017-03-03 8 views
0

Ich habe eine Reihe von Daten, die eine von sechs Kategorien ist, jedes Stück Daten hat eine Zeit damit verbunden. Ich muss ein Histogramm verwenden, um diese Daten in Monatsbins zu sortieren, was einfach ist, aber ich muss auch jede Kategorie stapeln. Ich habe nach einem Beispiel für gestapelte Histogramme gesucht, aber die einzigen, die ich finden kann, stammen von d3 v3, das sich in seiner Stapel-API offensichtlich sehr unterscheidet. Im Moment stecke ich darin fest, dass ich nach dem Aufruf stack() unsinnige Daten zurückbekomme, die ich nicht verwenden kann, um ein gestapeltes Balkendiagramm zu erzeugen.d3 v4: Stapel mit Histogrammdaten verwenden?

   var data = this.data; 
       var margin = {top: 20, right: 20, bottom: 30, left: 50}, 
        width = this.width - margin.left - margin.right, 
        height = this.height - margin.top - margin.bottom; 


       data.forEach(function(d) { 
        d.date = d3.isoParse(d.createdDate); 
       }); 

       // set the ranges 
       var x = d3.scaleTime() 
        .domain(d3.extent(data, function(d) { return d.date; })) 
        .rangeRound([0, width]); 
       var y = d3.scaleLinear() 
        .range([height, 0]); 
       var colours = d3.scaleOrdinal(d3.schemeCategory10); 

       var svg = d3.select(this.$.chart); 
       var svg2 = svg.select("#canvas"); 

       var histogram = d3.histogram() 
        .value(function(d) { return d.date; }) 
        .domain(x.domain()) 
        .thresholds(x.ticks(d3.timeMonth)); 

       var dataGroupedByType = d3.nest() 
        .key(function(d) { 
         return d.type; 
        }) 
        .object(data, d3.map); 

       var histDataByType = []; 
       for (var key in dataGroupedByType) { 
        var histData = histogram(dataGroupedByType[key]); 
        histDataByType.push({type: key, values: histData}); 
       } 

       var stack = d3.stack() 
        .keys(["A","B","C","D","E","F"]) 
        .value(function(d, key) { 
         return d.values; 
        }); 

       var stackedHistData = stack(histDataByType); 

dataGroupedByType ist ein Objekt mit sechs keyed Objekten (A bis F), die jeweils ein Array von Datenobjekten enthalten. Dann mache ich histDataByType was zu einem Array von 6 Objekten führt, von denen jedes eine type Eigenschaft (A bis F) und ein values Array hat, was immer die gleiche Länge ist (91 in meinem Fall, da meine Daten 91 Monate umfassen). Innerhalb dieses Arrays befindet sich ein weiteres Array mit bin-Daten (falls vorhanden) und den Werten x0 und x1. An diesem Punkt wurde das Binning erledigt, alles, was ich brauche, ist alles zu stapeln und die Werte y0 und y1 zu bekommen.

Also, ich rufe stack, aber es gibt mir Müll raus; stackedHistData ist ein Array von 6, jedes Array hat eine 0-Eigenschaft, die 0 entspricht, eine 1-Eigenschaft, die 'NaN' entspricht, und eine data-Eigenschaft, die dieses 91-lange Array, den Index und den Schlüssel (A bis F) hat. Ich sehe nicht einmal die Werte y0 und y1, die vom Stapelaufruf erzeugt werden sollen. Wie soll es mit dieser Art von Histogrammdaten verwendet werden?

Antwort

0

Ich habe das irgendwann herausgefunden. Ich habe im Grunde versucht, die Datenstruktur found here zu emulieren.

Zuerst habe ich die Schlüssel von den Daten erworben sowie die Zeiten analysiert.

var keys = []; 
data.forEach(function(d) { 
    d.date = d3.isoParse(d.relevantDate); 
    keys.push(d.type); 
}); 

keys = _.uniq(keys); 

Hier verwende ich die lodash-Bibliothek, um meine Reihe von Schlüsseln einzigartig zu machen. Der nächste Schritt besteht darin, die Behälter so zu erstellen, wie Sie es normalerweise für ein Histogramm tun würden:

var histogram = d3.histogram() 
    .value(function(d) { return d.date; }) 
    .domain(x.domain()) 
    .thresholds(x.ticks(d3.timeMonth)); 

var bins = histogram(data); 
y.domain([0, d3.max(bins, function(d) { return d.length; })]); 

Die Domäne kann auch hier deklariert werden. Jetzt kommt der Spaßteil:

var stackData = []; 
for (var bin in bins) { 
    //console.log(bins[bin].x0, bins[bin].x1) 
    var pushableObject = {}; 
    // add the time boundaries. 
    pushableObject.x0 = bins[bin].x0; 
    pushableObject.x1 = bins[bin].x1; 
    // for each bin, split the data into the different keys. 
    bins[bin].forEach(function(d) { 
     //console.log(d); 
     if (!pushableObject[d.type]) { pushableObject[d.type] = [d]} 
     else pushableObject[d.type].push(d); 
    }) 
    // if any of the keys didn't get represented in this bin, give them empty arrays for the stack function. 
    keys.forEach(function(key) { 
     if (!pushableObject[key]) { 
      pushableObject[key] = []; 
     } 
    }) 

    stackData.push(pushableObject); 
} 

Ich mache einen leeren stackData var, und Schleife durch die Behälter. Für jedes Bin befülle ich ein Objekt mit x0 und x1, da diese für das Zeichnen des Diagramms benötigt werden. Dann mache ich eine foreach Schleife auf der Bin, die über jedes darin gespeicherte Datenelement schleift. Das Speicherobjekt erhält in dieser Schleife ein Array pro Typ (auch bekannt als Schlüssel). Dann gibt es eine Backup-Schleife, um alle Typen abzufangen, die in diesem Bin nicht dargestellt wurden, so dass die stack-Funktion korrekt funktionieren kann. Apropos, hier ist es:

var realStack = d3.stack() 
    .keys(keys) 
    .value(function(d, key) { 
     return d[key].length; 
    }); 

Es ist ziemlich einfach, jetzt, da wir alle Daten richtig massiert haben. Es muss nur die Länge der Daten Buckets anstelle der Daten selbst erhalten. Dann verwenden Sie einfach diese Stack-Funktion, wenn Sie die Rectures anhängen und die stackData Variable übergeben, und alles wird funktionieren.