Datenvisualisierung/D3.js

Aus SELFHTML-Wiki
Datenvisualisierung(Weitergeleitet von D3.js)
Wechseln zu: Navigation, Suche

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.

Screenshot der Seite d3.js

Download

Man kann die jeweils neueste Version auch direkt per CDN einbinden, wenn man folgenden Code-Schnipsel in den Kopf Ihrer Webseite einfügt:

Einbindung D3.js
<script src="https://d3js.org/d3.v7.min.js"></script>

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:

Array für D3.js ansehen …
let fruits = ['Pflaumen', 'Kiwis', 'Zitronen', 'Orangen', 'Äpfel'];

d3.selectAll(".d3_fruit").data(fruits).text((d) => d);
Die aus dem Balkendiagramm bekannten Obstsorten unserer Ernte befinden sich als Datenpunkte in einem Array fruits.


Mit der d3.selectAll-Methode werden die Datenpunkte dem p-Element im HTML zugeordnet:

<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:

Diagramm mit D3.js - 2 ansehen …
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.

  1. d3.select(".d3_fruit") wählt den Div-Wrapper d3_fruit
  2. .selectAll("p") wählt alle p-Elemente aus, auch wenn es keine p-Elemente im div gibt - dies gibt eine leere Auswahl zurück
  3. .data(fruits) - Bindet das fruits-Array an die leere Auswahl
  4. .join("p") - Diese Methode erstellt alle p-Elemente für jedes Element in unserem Array
  5. .attr("class", "d3_fruit") - Wir setzen eine Klasse für jedes erstellte p-Element
  6. .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 als JSON-Datei ansehen …
{
  "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:

Balkendiagramm mit D3.js ansehen …
	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:

  • ein rect-Element für den Balken und
  • ein text-Element für das label

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 an
    svg.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 soll d3.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.

Bundesländer im Vergleich mit D3.js ansehen …
    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.

Achsenbeschriftung mit D3.js ansehen …
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 aus xScale 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]

Beispiel
let drag = d3.behavior.drag();

Mit dieser Zuweisung initialisierst du eine neue Instanz des Drag-Verhaltens.


Beispiel
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.


Beispiel
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:

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]

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

  1. D3.js
  2. The D3.js Graph Gallery
  3. D3.js Tutorial – Data Visualization for Beginners von Spruce Emmanuel
  4. collarobadev: D3 Drag And Drop von Jason Graves
  5. stackoverflow: How can I click to add or drag in D3?
  6. Snap.svg
  7. tutsplus: How to Manipulate and Animate SVG With Snap.svg

Video-Tutorial: