Datenvisualisierung/D3.js
Im Selfhtml-aktuell-Bereich stellte Lutz Tautenhahn mit dem JavaScript Diagram Builder 2002 eine Bibliothek zur Verfügung, mit der man einfache Diagramme erstellen konnte. Heute gibt es zahlreiche Frameworks, die mit wenigen Zeilen JavaScript attraktive und anschauliche Präsentationen ermöglichen.
Dieses Tutorial gibt eine Einführung in D3.js (Data-Driven Documents), das zur Zeit am weitesten verbreiteste Framework mit vielen Funktionen, die man mit wenigen Zeilen Code einbinden kann.
D3.js ist eine JavaScript-Bibliothek zur Manipulation datenbasierter Dokumente, die auf HTML, CSS und SVG aufbaut und so fortgeschrittene Animationen und Visualisierungen ermöglicht.[1][2] Auch renommierte Medienkonzerne wie die New York Times nutzen Visualisierungen mit D3.
Durch einen Klick auf die Sechsecke der Startseite kann man sich eine Vielzahl von Beispielen anschauen.
Download
- d3.v7.js (573kb)
- d3.v7.min.js (273 kB)
Man kann die jeweils neueste Version auch direkt per CDN einbinden, wenn man folgenden Code-Schnipsel in den Kopf Ihrer Webseite einfügt:
<script src="https://d3js.org/d3.v7.min.js"></script>
Inhaltsverzeichnis
Einführung
Es gibt auf d3js.org/getting-started eine englischsprachige Einführung. Ich möchte die Beispiele aus den vorhergehenden Kapiteln nehmen und zeigen, wie man diese mit D3.js aufwerten und anschaulicher darstellen kann.[3]
D3.js hat aber auch eine eigene Plattform, auf der du Code-Snippets live testen kannst: Observable
Data driven - Datengesteuert
In einem ersten Versuch wollen wir unser Balkendiagramm aus dem ersten Kapitel mit D3.js nachbauen. Bisher ging es dabei vorrangig aber mehr über die passenden SVG-Elemente als über die Daten an sich.
Das soll sich nun ändern, damit unser Diagramm vielseitig einsetzbar ist.
Ähnlich wie bei den aus jQuery übernommenen querySelector und querySelectorAll kann man mit d3.select
ein Element - nämlich das erste, dass diese Bedingung trifft - auswählen:
d3.select("#d3_p").style("color", "blue");
Die d3.selectAll
-Methode wählt alle Elemente, auf die die Bedingung zutrifft:
let fruits = ['Pflaumen', 'Kiwis', 'Zitronen', 'Orangen', 'Äpfel'];
d3.selectAll(".d3_fruit").data(fruits).text((d) => d);
<p class="d3_fruit"></p>
Alerdings wird nur der erste Datenpunkt Pflaumen
zugeordnet - die anderen werden „vergessen“. Eine schnelle Lösung für dieses Problem wäre, die anderen p-Elemente manuell zu erstellen und zu füllen. In den meisten Fällen weiss man jedoch nicht, wie viele Elemente sich im Array mit Daten befinden.
Deshalb soll unser Script für jeden Datenpunkt ein eigenes Element erstellen:
let fruits = ['Pflaumen', 'Kiwis', 'Zitronen', 'Orangen', 'Äpfel'];
d3.select(".d3_fruit")
.selectAll("p")
.data(fruits)
.join("p") // the join method
.attr("class", "d3_fruit")
.text((d) => d)
Die d3.join()
-Methode fügt Elemente hinzu, entfernt sie und ordnet sie neu an, um sie an die angegebenen Daten anzupassen.
-
d3.select(".d3_fruit")
wählt den Div-Wrapperd3_fruit
-
.selectAll("p")
wählt alle p-Elemente aus, auch wenn es keine p-Elemente im div gibt - dies gibt eine leere Auswahl zurück -
.data(fruits)
- Bindet dasfruits
-Array an die leere Auswahl -
.join("p")
- Diese Methode erstellt alle p-Elemente für jedes Element in unserem Array -
.attr("class", "d3_fruit")
- Wir setzen eine Klasse für jedes erstellte p-Element -
.text((d) => d)
- Setzt den Text für jedes erstellte p auf der Grundlage des Arrays "fruits".
Laden von Daten
In der Praxis befinden sich Daten nicht in einem Array im HTML-Dokument, sondern werden aus einer Datenbank oder einer externen Datei geladen.
Dafür gibt es in D3 jeweils eigene Methoden:
-
d3.json
-
d3.csv
-
d3.xml
-
d3.tsv
-
d3.text
// async await
const data = await d3.csv("/path/to/fruits.csv");
console.log(data);
// or
d3.json("/path/to/file.json").then((data) => { console.log(data); })
Wir verwenden diese JSON-Datei:
{
"fruits": [
{
"name": "Pflaumen",
"amount": 18,
"color": "purple"
},
{
"name": "Kiwis",
"amount": 21,
"color": "green"
},
{
"name": "Zitronen",
"amount": 15,
"color": "yellow"
},
{
"name": "Orangen",
"amount": 16,
"color": "orange"
},
{
"name": "Äpfel",
"amount": 23,
"color": "red"
}
]
}
Sie wird nun geladen und aus den Werten ein Balkendiagramm erstellt:
const url = '.../D3-fruits.json';
d3.json(url).then(data => {
const svg = d3.select('#barChart');
const width = 600;
const height = 400;
const margin = { top: 20, right: 20, bottom: 20, left: 20 };
data.fruits.forEach((d, i) => {
const x = 10;
const barHeight = 25;
const y = height - ( margin.bottom + barHeight) * (i + 1);
// Draw bar
svg.append('rect')
.attr('class', 'bar')
.attr('x', x)
.attr('y', y)
.attr('width', d.amount*10)
.attr('height', barHeight)
.attr('fill', d.color);
// Add label
svg.append('text')
.classed('label', true)
.attr('x', x + 15)
.attr('y', y +18)
.text(d.amount);
});
}).catch(error => {
console.error('Error loading JSON:', error);
});
Mit Array.forEach fügen wir nun für jeden Datensatz zwei Elemente hinzu:
Die D3-Methoden sind kürzer als ihre JavaScript-Pendants:
-
.attr()
: Aktualisiert das Attribut des ausgewählten Elements.attr('width', d.amount*10)
ALs Wert für das width-Attribut wird der d.amount verzehnfacht. -
.classed()
: Weist den ausgewählten Elementen die angegebenen CSS-Klassennamen zu bzw. hebt die Zuweisung auf.classed('label', true)
-
.text()
: Aktualisiert den Textinhalt des ausgewählten Elements.text(d.amount);
Das Anlegen eines neuen Elements inklusive Einhängen in das DOM erfolgt einfach mit:
.append()
Hängt ein neues Element als letztes Kind des ausgewählten Elements ansvg.append('rect')
.insert()
Funktioniert wie die Methode .append(), außer dass man ein anderes Element angeben kann, das vor dem ausgewählten Element eingefügt werden solld3.select('div').insert('p', 'h1')
.remove()
Entfernt das ausgewählte Element aus dem DOM
Skalen in D3
Das letzte Beispiel hat ein Problem: In guten Jahren wird die Anzahl der Pflaumen (oder Kirschen) viel höher werden und aus unserer „nur“ 600 Einheiten breiten SVG-Leinwand ausbrechen. Im nächsten Beispiel wollen wir die Einwohnerzahlen der 16 Bundesländer vergleichen. Anstelle einer viewBox mit ca. 20 Millionen Einheiten sollen die Werte skaliert werden, wofür SVG ja prädestiniert ist.
Dafür können wir das eingebaute d3.scale
verwenden. Es nimmt Daten als Eingabe entgegen und gibt einen visuellen Wert in Pixeln zurück. d3.scale
muss mit einer Domain und einem Bereich festgelegt werden. Der Bereich setzt eine Grenze für die Daten, die wir visuell darstellen wollen.
const xScale = d3.scaleBand()
.domain(states.map(d => d.name))
.range([margin.left, width - margin.right])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(states, d => d.population)])
.nice()
.range([height - margin.bottom, margin.top]);
// Axes
const xAxis = d3.axisBottom(xScale);
const yAxis = d3.axisLeft(yScale);
Achsen
Die Achsenkomponente stellt für Menschen lesbare Referenzmarken für Positionsskalen dar. Der Aufruf der Achsen-Komponente auf einer Auswahl von SVG-Containern (normalerweise ein einzelnes g-Element) füllt die Achsen auf. Die Achsen werden am Ursprung gerendert. Um die Position der Achse in Bezug auf das Diagramm zu ändern, geben Sie ein Transformationsattribut für das enthaltende Element an.
const xScale = d3.scaleBand()
.domain(states.map(d => d.name))
.range([margin.left, width - margin.right])
.padding(0.1);
const xAxis = d3.axisBottom(xScale);
Die Achsen erhalten ihren Text von den Skalen, die mit den Daten konfiguriert werden.
- Die Domain() von
xScale
definiert die Zustandsnamen (d.name
) als Kategorien. - Die
xAxis
wird dann ausxScale
erzeugt und verwendet automatisch die Werte im Bereich der Skala für die Beschriftungen.
Siehe auch
Drag und Drop mit d3.js
Das Framework d3.js ist eine der weit verbreiteten Ergänzungen zur Datenvisualisierung. Mit seiner Hilfe kannst du mit wenigen Zeilen Code SVG-Elemente ziehen und ablegen.[4]
let drag = d3.behavior.drag();
Mit dieser Zuweisung initialisierst du eine neue Instanz des Drag-Verhaltens.
let drag = d3.behavior.drag();
d3.selectAll(".draggable").call(drag);
Jetzt werden alle Elemente, die das Klassenattribut draggable
haben, mit der Drag- & Drop-Funktionalität versehen.
let drag = d3.behavior.drag()
.on("dragstart", function(){
//ziehen beginnt
})
.on("drag", function(){
//es wird gezogen
})
.on("dragend", function(){
//ablegen
});
Man kann an die einzelnen Drag-Events auch weitere Funktionsaufrufe anhängen, die dann zusätzliche Anweisungen ausführen.
Ein weiteres Livebeispiel findest du auf jsfiddle, das auf dieser Frage in Stackoverflow basiert.[5]
Streudiagramm
Ein Streudiagramm (engl. scatter plot) ist die grafische Darstellung von beobachteten Wertepaaren zweier statistischer Merkmale. Diese Wertepaare werden in ein kartesisches Koordinatensystem eingetragen, wodurch sich eine Punktwolke ergibt.
Das Muster der Punkte lässt oft Informationen über die Abhängigkeitsstruktur der beiden Merkmale erkennen. Häufig auftretende Korrelationen sind Ballungen (cluster) und lineare Strukturen.
https://codepen.io/sandeepguggu/pen/DLBVVK
https://gist.github.com/ctufts/298bfe4b11989960eeeecc9394e9f118
Streudiagramme:
- alignedleft.com: Making a scatterplot
- matthewgladney.com: No Nonsense Guide To Getting Started with Scatter Plots, d3.js and d3.csv
- OReilly: Making a scatterplot with D3.js
weitere Frameworks
snap.svg
Da Raphael.js aus dem Jahre 2008 sogar den IE5.5 unterstützte, war eine Weiterentwicklung nur noch schwer möglich und so entschloss sich Dimitri Baranovskiy zu einer Neuentwicklung, die besonders die Manipulation von SVG-Elementen ermöglichte. Der Nachfolger Snap.svg ermöglicht interaktive und responsive Animationen für SVG und nutzt dabei SVG-Features wie Maskierungen, Muster, Verläufe und Filter.[6]
Auf tutsplus findest du ein umfangreiches Tutorial in englischer Sprache.[7]
- Download (1.66 MB)
Chartist
Das Ziel von Chartist ist es, eine einfache, leichtgewichtige und unaufdringliche Bibliothek zur Verfügung zu stellen, um Diagramme auf Ihrer Website responsiv zu gestalten. Es ist wichtig zu verstehen, dass eine der Hauptabsichten von Chartist darin besteht, sich auf Standards zu verlassen, anstatt eigene Lösungen für Probleme zu bieten, die bereits durch diese Standards gelöst werden.
Weblinks
- ↑ D3.js
- ↑ The D3.js Graph Gallery
- ↑ D3.js Tutorial – Data Visualization for Beginners von Spruce Emmanuel
- ↑ collarobadev: D3 Drag And Drop von Jason Graves
- ↑ stackoverflow: How can I click to add or drag in D3?
- ↑ Snap.svg
- ↑ tutsplus: How to Manipulate and Animate SVG With Snap.svg
- alignedleft: Making a bar chart (mit d3.js) - viele interessante Ideen zur Struktur und Gestaltung
- sitepoint: Creating Simple Line and Bar Charts Using D3.js
Video-Tutorial:
- dashingd3js: Basic Chart - Bar Chart
fruits
.
Mit derd3.selectAll
-Methode werden die Datenpunkte dem p-Element im HTML zugeordnet: