Una introducció suau a D3: Com fer un gràfic de bombolles reutilitzables

Comença amb D3

Quan vaig començar a aprendre D3, res tenia cap sentit per mi. Les coses només van quedar més clares quan vaig començar a tractar esquemes reutilitzables.

En aquest article, us mostraré com crear un gràfic de bombolles reutilitzables alhora que us donarà una suau introducció a D3. El conjunt de dades que utilitzem consisteix en històries que es van publicar al freeCodeCamp el gener del 2017.

Aquest és el diagrama que creareu

Sobre D3

D3 és una biblioteca JavaScript per a la visualització de dades. Dóna vida a les dades mitjançant HTML, SVG i CSS.

Sovint hem de reutilitzar un gràfic en un altre projecte o compartir el gràfic amb altres. Per això, Mike Bostock (el creador de D3) va suggerir un model anomenat diagrames reutilitzables. Utilitzarem el seu plantejament amb algunes petites modificacions, com les que presenta Pablo Navarro Castillo al llibre Mastering D3.js.

Utilitzem la versió 4.6.0 de D3 aquí.

Rts Gràfics reutilitzables

Els gràfics que segueixen el patró de gràfics reutilitzables tenen dues característiques:

  • Configurabilitat. Volem canviar l’aspecte i el comportament del diagrama sense haver de canviar el codi en si.
  • Capacitat de construir de forma independent. Volem que cada element del gràfic s’assigni de manera independent a un punt de dades del nostre conjunt de dades. Això està relacionat amb la manera en què D3 enllaça les instàncies de les dades amb elements DOM. Això quedarà més clar en un minut.
"Conclusió: implementeu esquemes com a tancaments amb mètodes getter-setter." - Mike Bostock

El gràfic de bombolles

Primer heu de definir quins elements del diagrama es poden ajustar:

  • La mida del gràfic
  • El conjunt de dades d’entrada

Definiu la mida del diagrama

Comencem per crear una funció que encapsuli totes les variables del gràfic i estableixi els valors per defecte. Aquesta estructura s’anomena conclusió.

// bubble_graph.js
var bubbleChart = function () {amplària var = 600, alçada = 400; Pla de funcions (selecció) {// arribareu aquí} retorn de la taula; }

Voleu crear diagrames de diferents mides sense haver de canviar el codi. Per fer-ho, creeu diagrames de la següent manera:

// bubble_graph.html
diagrama var = bubbleChart (). Amplada (300). Alçada (200);

Per fer-ho, ara definiu accessors per a les variables amplada i alçada.

// bubble_graph.js
var bubbleChart = function () {amplària var = 600 alçada = 400;
Pla de funcions (selecció) {// Arribem aquí} chart.width = function (valor) {if (! Arguments.length) {return width; } amplada = valor; Taula de retorn; }
chart.height = funció (valor) {if (! arguments.length) {alçada de retorn; } Alçada = valor; Taula de retorn; } Taula de retorn; }

Quan truqueu bubbleChart () (sense atributs d’amplada ni alçada), el gràfic es crea amb els valors per defecte d’amplada i alçada que heu definit al tancament. Si el mètode s’anomena sense arguments, retorna el valor variable.

// bubble_graph.html
var chart = bubbleChart (); bubbleChart (). amplada (); // retorna 600

Potser us preguntareu per què els mètodes retornen la funció del gràfic. Aquest és un patró de JavaScript usat per simplificar el codi. Es diu mètode encadenament. Amb aquest patró, podeu crear nous objectes de la següent manera:

// bubble_graph.html
diagrama var = bubbleChart (). Amplada (600). Alçada (400);

en lloc de:

// bubble_graph.html
var chart = bubbleChart (); chart.setWidth (600); chart.setHeight (400);

Connecta dades al nostre diagrama

Ara aprenem a connectar dades als elements del gràfic. Així s’estructura el diagrama: La divisió amb el diagrama té un element SVG i cada punt de dades correspon a un cercle del diagrama.

// bubble_graph.html després d'haver trucat a la funció bubbleChart ()
// una història a partir de dades // una altra història de dades ...

3. d3.data ()

La funció d3.selection.data ([dades [, clau]]) retorna una nova selecció que representa un element que es va enllaçar amb èxit amb les dades. Per fer-ho, primer heu de carregar les dades del fitxer CSV. Utilitzeu la funció d3.csv (url [[, fila], callback]).

// bubble_graph.html
d3.csv ('file.csv', function (error, our_data) {var data = our_data; // aquí podeu fer el que vulgueu amb les dades}
// medium_january.csv | títol | Categoria | Cors | | -------------------------------------- | ---------- ---- | -------- | | Ningú vol utilitzar el desenvolupament de programari 2700 | | Navegació sense pèrdues amb rutes Disseny | 688 | | L'auge del Data Engineer Science Science | 862 |

Selecció 🖍 d3

Utilitzeu les funcions d3-select () i data () per passar les nostres dades al diagrama.

Una selecció permet una transformació potent i basada en dades del model d'objectes documentals (DOM): configuració d'atributs, estils, propietats, contingut HTML o de text i molt més. - Documentació D3
// bubble_graph.html
d3.csv ('medium_january.csv', function (error, nostra_data) {if (error) {console.error ('Error en obtenir o analitzar les dades.'); error de llançament;}
diagrama var = bubbleChart (). Amplada (600). Alçada (400); d3.select ("# gràfic"). dades (nostra_data) .call (gràfic);
});

Un altre seleccionador important és d3.selectAll (). Suposem que teniu la següent estructura:


    
    
    

d3.select ("cos"). selectAll ("div") selecciona totes aquestes divisions per a nosaltres.

3. d3.enter ()

Ara coneixeu una funció D3 important: d3.enter (). Suposem que teniu una etiqueta de cos buida i un conjunt de dades. Voleu passar per cada element de la matriu i crear una nova divisió per a cada element. Podeu fer-ho amb el següent codi:

 // buit 
---- // js script
var our_data = [1, 2, 3] var div = d3.select ("body") .selectAll ("div") .data (our_data) .enter () .append ("div"); ---


    
    
    

Per què necessiteu selectAll ("div") quan els divs encara no existeixen? Perquè a D3 diem el que volem en lloc de dir com fer alguna cosa.

En aquest cas, voleu assignar un element de la matriu a cada div. Ho dius amb selectAll ("div").

var div = d3.select ("body") .selectAll ("div") // aquí es diu 'hey d3, cada element de dades de la matriu que ve a continuació està lligat a una div' .data (our_data) .enter (). append ("div");

La tecla Enter () retorna la selecció amb les dades vinculades a l'element de la matriu. A continuació, afegiu aquesta selecció al DOM mitjançant el .append ("div").

d3.forceSimulation ()

Necessiteu alguna cosa per simular la física dels cercles. Per a això, utilitzeu d3.forceSimulation ([node]). També heu d’especificar quin tipus de força canvia la posició o la velocitat dels nusos.

En el nostre cas utilitzem d3.forceManyBody ().

// bubble_chart.js
var simulation = d3.forceSimulation (dades) .force ("càrrega", d3.forceManyBody (). force ([- 50])) .force ("x", d3.forceX ()) .force ("y", d3.forceY ()) .on ("tick", marcat);

Un valor de força positiu fa que els nodes s’atraguin els uns als altres, mentre que un valor de força negatiu fa que es repel·lin.

L’efecte force ()

Tanmateix, no volem que els nodes abastin tot l'espai SVG, de manera que utilitzem d3.forceX (0) i d3.forceY (0). Això "arrossega" els cercles a la posició 0. Proveu d’eliminar-ho del codi per veure què passa.

Quan actualitzeu la pàgina, podeu veure que els cercles s’ajusten fins que finalment s’estabilitzen. La funció marcada () actualitza les posicions dels cercles. D3.forceManyBody () actualitza constantment la posició x i y de cada node i la funció ticked () actualitza el DOM amb aquests valors (els atributs cx i cy).

// bubble_graph.js
Funció marcada (e) {node.attr ("cx", funció (d) {return dx;}) .attr ("cy", funció (d) {return dy;}); // "node" és qualsevol cercle del gràfic de bombolles
}

Aquí teniu el codi amb tot:

var simulation = d3.forceSimulation (dades) .force ("càrrega", d3.forceManyBody (). force ([- 50])) .force ("x", d3.forceX ()) .force ("y", d3.forceY ()) .on ("tick", marcat);
Funció marcada (e) {node.attr ("cx", funció (d) {return dx;}) .attr ("cy", funció (d) {return dy;}); }

En resum, es pot dir que aquesta simulació dóna a cada cercle una posició x i una y.

d3.escales

Aquí ve la part més emocionant: afegir cercles. Recordes la funció enter ()? Ja ho faràs servir. A la nostra taula, el radi de cada cercle és proporcional al nombre de recomanacions de cada història. Per fer-ho, utilitzeu una escala lineal: d3.scaleLinear ()

Per utilitzar escales, heu de definir dues coses:

  • Domini: els valors mínims i màxims de les dades d’entrada (en el nostre cas el nombre mínim i màxim de recomanacions). Per obtenir els valors mínims i màxims, utilitzeu les funcions d3.min () i d3.max ().
  • Abast: el valor de sortida mínim i màxim de l'escala. En el nostre cas, volem el radi més petit de mida 5 i el radi més gran de mida 18.
// bubble_graph.js
var scaleRadius = d3.scaleLinear () .domain ([d3.min (dades, funció (d) {return + d.views;})), d3.max (dades, funció (d) {return + d.views; })]) .range ([5,18]);

I finalment creeu els cercles:

// bubble_graph.js
var node = svg.selectAll ("Kreis") .data (introduïu dades) () .append ("cercle") .attr ('r', funció (d) {scaleRadius (d.views) return})}) ;

Per acolorir els cercles, utilitzeu una escala categòrica: d3.scaleOrdinal (). Aquesta escala retorna valors discrets.

El nostre conjunt de dades té tres categories: disseny, desenvolupament i ciències de dades. Assignau cadascuna d’aquestes categories a un color. d3.schemeCategory10 ens ofereix una llista amb 10 colors, que és suficient per a nosaltres.

// bubble_graph.js
var colorCircles = d3.scaleOrdinal (d3.schemeCategory10); var node = svg.selectAll ("Kreis") .data (introduïu dades) () .append ("cercle") .attr ('r', funció (d) {return scaleRadius (d.views)}) .style ("omplir", funció (d) {return colorCircles (d.category)});

Si voleu que els cercles es dibuixin al mig del fitxer SVG, mueu cada cercle al centre (la meitat de l'amplada i la meitat de l'alçada). Elimineu-ho del codi per veure què passa.

// bubble_graph.js
var node = svg.selectAll ("Kreis") .data (introduïu dades) () .append ("cercle") .attr ('r', funció (d) {return scaleRadius (d.views)}) .style ("omplir", funció de retorn (d) {colorCircles (d.category)}) .attr ('transformar', 'traduir (' + [amplitud / 2, alçada / 2] + ')');

Afegiu ara les indicacions al diagrama. S'han de mostrar sempre que movem el ratolí per sobre dels cercles.

var tooltip = selecció .append ("div") .style ("posició", "absoluta") .style ("visibilitat", "oculta") .style ("color", "blanc") .style ("encoixinat" , "8px") .style ("color de fons", "# 626D71") .style ("radi de fotograma", "6px") .style ("alinear text", "centre") .style ("família de tipus de lletra", "monospai" ) .style ("amplada", "400px"). Text ("");
var node = svg.selectAll ("Kreis") .data (introduïu dades) () .append ("cercle") .attr ('r', funció (d) {return scaleRadius (d.views)}) .style ("omplir", funció de retorn (d) {colorCircles (d.category)}) .attr ('transformar', 'traduir (' + [amplitud / 2, alçada / 2] + ')') .on ("ratolí ", Funció (d) {tooltip.html (d.category +" "+ d.title +" "+ d.views); retornar tooltip.style (" visibilitat "," visible ");}) .on (" mousemove ", function () {return tooltip.style (" superior ", (d3.event.pageY- 10) + "px"). Estil ("esquerra", (d3.event.pageX + 10) + "px");}) .on ("mouseout", function () {return tooltip.style ("visibilitat") , "amagat");});

El punter del ratolí segueix el cursor quan es mou. d3.event.pageX i d3.event.pageY retornen les coordenades del ratolí.

I ja està! Podeu veure el codi final aquí.

Podeu jugar amb la taula de bombolles aquí.

Us ha semblat útil aquest article? Faig tot el possible per escriure un article de immersió profunda cada mes. Podeu rebre un correu electrònic quan en publiqui un de nou.

Preguntes o suggeriments? Deixeu-los als comentaris. Gràcies per llegir!

Gràcies especials a John Carmichael i Alexandre Cisneiros.