2012-07-26 6 views
12

Alle d3 Tutorials, die ich gefunden habe, verwenden Daten, die in Arrays von Objekten angeordnet sind, aus denen sie einen Punkt für jedes Objekt im Array zeichnen. Gegebene Daten in der folgenden Struktur:Kann d3.js zwei Streudiagramme auf demselben Graphen mit Daten aus derselben Quelle zeichnen?

data = [ 
    {id: 1, x: 4, y: 10, type: 1}, 
    {id: 2, x: 5, y: 20, type: 2} 
    ... 
] 

Die X- und Y-Werte werden verwendet, um ein Streudiagramm zu erstellen. Der type-Parameter wird verwendet, um die Farbe jedes Punktes zu ändern. Sehen Sie diese jsfiddle für ein Beispiel: durch Ziehen zwei Datenpunkte für jedes Objekt http://jsfiddle.net/uxbHv/

Unfortuately, ich habe eine andere Datenstruktur, und ich kann nicht herausfinden, wie man das gleiche Diagramm erstellen. Hier einige Beispieldaten:

dataSet = [ 
    {xVar: 5, yVar1: 90, yVar2: 22}, 
    {xVar: 25, yVar1: 30, yVar2: 25}, 
    {xVar: 45, yVar1: 50, yVar2: 80}, 
    {xVar: 65, yVar1: 55, yVar2: 9}, 
    {xVar: 85, yVar1: 25, yVar2: 95} 
] 

I XVAR einzeln gegen yVar1 oder yVar2 grafisch darstellen kann, aber ich kann nicht herausfinden, wie beide auf derselben Grafik erhalten: http://jsfiddle.net/634QG/

Antwort

30

Die allgemeine Regel bei Verwendung einer data-join ist, dass Sie eine eins-zu-eins-Zuordnung von Daten zu Elementen möchten. Wenn Sie also zwei Reihen in Ihrem Streudiagramm haben, möchten Sie, dass zwei Containerelemente (z. B. G elements) die Serie darstellen. Da Sie derzeit nur ein data Array haben, möchten Sie auch array.map verwenden, um die Datendarstellung in zwei parallele Arrays mit der gleichen Darstellung zu konvertieren. Auf diese Weise müssen Sie den Code nicht für jede Serie duplizieren.

Sagen Sie Ihre Daten in einer CSV-Datei dargestellt wurden für das x -Werten mit einer Spalte-Datei und mehr anderen Spalten für die y -Werten jede Serie:

x,y1,y2 
5,90,22 
25,30,25 
45,50,80 
65,55,9 
85,25,95 

Wenn Sie das wollen, Code, um vollständig generisch zu sein, müssen Sie zuerst die Namen der Reihe, wie ["y1", "y2"] berechnen. (Wenn Sie der CSV-Datei eine dritte Spalte hinzugefügt haben, könnte dies ["y1", "y2", "y3"] sein.) Sie können die Namen mit d3.keys berechnen, wodurch die benannten Eigenschaften aus einem Objekt extrahiert werden. Zum Beispiel gibt d3.keys({foo: 1, bar: 2})["foo", "bar"] zurück.

// Compute the series names ("y1", "y2", etc.) from the loaded CSV. 
var seriesNames = d3.keys(data[0]) 
    .filter(function(d) { return d !== "x"; }) 
    .sort(); 

Jetzt, da Sie die Seriennamen haben, können Sie ein Array von Punktarrays erstellen. Das äußere Array repräsentiert die Reihe (von denen es zwei gibt) und die inneren Arrays speichern die Datenpunkte. Sie können die Punkte gleichzeitig in eine konsistente Repräsentation konvertieren (Objekte mit den Eigenschaften x und y), sodass Sie den Code seriellübergreifend wiederverwenden können.

// Map the data to an array of arrays of {x, y} tuples. 
var series = seriesNames.map(function(series) { 
    return data.map(function(d) { 
    return {x: +d.x, y: +d[series]}; 
    }); 
}); 

Hinweis: dieser Code verwendet den + Operator die CSV-Werte in Zahlen zu zwingen. (CSV-Dateien sind nicht typisiert, daher sind sie anfänglich Zeichenfolgen.)

Nachdem Sie Ihre Daten nun einem regulären Format zugeordnet haben, können Sie G-Elemente für jede Serie erstellen und dann die Elemente für jeden Punkt einkreisen. Die resultierende SVG-Struktur wird wie folgt aussehen:

<g class="series"> 
    <circle class="point" r="4.5" cx="1" cy="2"/> 
    <circle class="point" r="4.5" cx="3" cy="2"/> 
    … 
</g> 
<g class="series"> 
    <circle class="point" r="4.5" cx="5" cy="4"/> 
    <circle class="point" r="4.5" cx="7" cy="6"/> 
    … 
</g> 

und den entsprechenden D3 Code:

// Add the points! 
svg.selectAll(".series") 
    .data(series) 
    .enter().append("g") 
    .attr("class", "series") 
    .style("fill", function(d, i) { return z(i); }) 
    .selectAll(".point") 
    .data(function(d) { return d; }) 
    .enter().append("circle") 
    .attr("class", "point") 
    .attr("r", 4.5) 
    .attr("cx", function(d) { return x(d.x); }) 
    .attr("cy", function(d) { return y(d.y); }); 

Ich habe auch ein Stück Code hinzugefügt jede Serie eine einzigartige Farbe zuweisen, indem eine Füllstil Hinzufügen zu dem enthaltenden G-Element. Es gibt natürlich viele verschiedene Möglichkeiten, dies zu tun. (Vielleicht möchten Sie zum Beispiel die Farbe für jede Serie präzisieren.) Ich habe auch den Code weggelassen, der die Domänen Ihrer x und y Skalen berechnet (und auch die Achsen rendert), aber wenn Sie möchten, das gesamte Arbeitsbeispiel sehen:

+0

fast ein Tutorial ... diese und die anderen ähnlichen Fragen, die durch so lange Antworten von Mike Bostock beantwortet wurden, sollten in die FAQ gestellt werden – paxRoman

4

Platz die beiden Kreise für alle Daten zeigen in ein einzelnes svg:g Element. Dadurch wird eine Eins-zu-eins-Zuordnung für die Daten zu Elementen erstellt, Sie können jedoch weiterhin zwei verschiedene Punkte anzeigen.

var nodeEnter = vis1.selectAll("circle") 
     .data(dataSet) 
     .enter() 
     .insert("svg:g"); 

nodeEnter.insert("svg:circle") 
      .attr("cx", function (d) { return 100 - d.xVar}) 
      .attr("cy", function (d) { return 100 - d.yVar1}) 
      .attr("r", 2) 
      .style("fill", "green"); 

nodeEnter.insert("svg:circle") 
      .attr("cx", function (d) { return 100 - d.xVar}) 
      .attr("cy", function (d) { return 100 - d.yVar2}) 
      .attr("r", 2) 
      .style("fill", "blue"); 

Arbeiten JSFiddle.

+0

Danke für die Lösung! Ich sehe, wie dies mit statischen Daten funktioniert, aber jetzt frage ich mich, ob es schwierig sein wird, Übergänge und Ausgänge hinzuzufügen .... zurück zu jsfiddle ... –

+0

Dies könnte funktionieren (jetzt), aber ich würde dieses Muster stark abraten- Die API ist nicht so konzipiert, dass sie so funktioniert. Wenn Sie einen Datenjoin verwenden, sollten Sie immer eine Eins-zu-Eins-Zuordnung zwischen Daten und Elementen haben. Das zweimalige Einfügen oder Anfügen an die Eingabeauswahl ist ein Missbrauch der API. Die korrekte Lösung in diesem Fall besteht darin, zwei "vis" -Elemente zu erstellen und die Daten auf zwei parallele Arrays mit einer konsistenten Darstellung abzubilden. – mbostock

+0

@mbostock Ist das ein besserer Ansatz? http://jsfiddle.net/634QG/3/ –

Verwandte Themen