Inici | Fonaments | Instal·lació | Afegir Elements | Encadenar Mètodes | Asociar Dades | Fent servir les Dades | Desplegar els divs | La funció data() | El primer SVG | Dibuixant SVG's | Tipus de dades | Fent un diagrama de barres | Fent un gràfic de dispersió | Escales | Eixos | Transicions |

Fent un diagrama de barres

última actualització 30 de Desembre de 2012
traducció 28 de Maig de 2015

Ara integrarem tot el que hem aprés fins ara per construir un diagrama de barras senzill amb D3.

Començarem revisant el diagrama de barres que varem fer en capítols anteriors fent servir elements div.

Després adaptarem el codi per dibuixar les barres dins un SVG, perque ens donarà més flexibilitat i més possibilitats sobre la presentació.

Finalment, afegirem les etiquetes, per poder veure clarament els valors de les dades.

L'Anterior Diagrama

Aquí tenim el que varem fer l'últim cop, amb algunes dades noves.

            
                var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
                        11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];

                d3.select("body").selectAll("div")
                    .data(dataset)
                    .enter()
                    .append("div")
                    .attr("class", "bar")
                    .style("height", function(d) {
                        var barHeight = d * 5;
                        return barHeight + "px";
                    });
                
            

Diagrama de barres amb divs

Pot ser es difícil d'imaginar, però podem millorar clarament aquest senzill diagrama de barres fet amb divs.

El Nou Diagrama

Primer de tot, hem de decidir les dimensions del nou SVG:

            //Amplada i alçada
                var w = 500;
                var h = 100;
            
        

(Per suposat, pot anomenar w i h i alguna cosa més, com ara svgWidth i svgHeight. Fes servir el nom que per a tu estigui més clar. JavaScript té una fixació d'arrel per ser eficiènt, i en general es veuen noms de variable d'un sol caràcter , codi escrit sense espais , i altres tipus de sintaxi de difícil lectura , però feta amb programació eficient)

Llavors, li direm a D3 que crei un element SVG i l'afegeixi al DOM:

            
            //Creem un element SVG
                var svg = d3.select("body")
                    .append("svg")
                    .attr("width", w)
                    .attr("height", h);
            
        

Això inserta un nou element <svg> just abans de l'etiqueta de tancament de </body>, i assigna al SVG una amplada i una alçada de 500 per 100 pixels. Aquest declaració també col·loca el resultat dins la nostra variable svg, per poder fer-ne referència fàcilment sense haver de reseleccionar-la més tard fent servir alguna cosa com d3.select("svg").

Després, enlloc de crear un divs, crearem rects i els afegirem al svg.

            
                svg.selectAll("rect")
                   .data(dataset)
                   .enter()
                   .append("rect")
                   .attr("x", 0)
                   .attr("y", 0)
                   .attr("width", 20)
                   .attr("height", 100);
                
            

Aquest codi selecciona tots els rects que hi hagin dins el svg. Per suposat, no n'hi ha cap encara, per tant retorna una selecció buida. (Estrany, si, però paciència. Amb D3, sempre has de seleccionar primer els elements sobre els que vols actuar, encara que aquesta selecció sigui de moment buida.)

Llavors, data(dataset) veurà que tenim 20 valors al nostre conjunt de dades, i farà un bucle amb tots els mètodes posteriors de 20 passades.

enter(), alhora, retorna un espai creat per a cada dada de la selecció encara que no hi afegeix el corresponent rectangle rect.

Per a cadascún dels 20 espais, append("rect") inserta un rect dins del DOM. Tal com varem aprendre al primer SVG, per cada rect s'ha de definir la x, la y, l'amplada width, i l'alçada height. Farem servir el mètode attr() per afegir aquests atributs a cadascún dels rectangles creats.

Maco, no?

One lonely bar

D'acord, potser no gaire. Totes les barres són aquí (mira-ho si no t'ho creus al DOM de la página de demostració amb l'inspector web del teu navegador), però totdes comparteixen el mateix valor de x, y, width, i height i per tant estan superposades. Aquí no hi ha una visualització de les dades encara.

Deixa que arregli primer el tema de la superposició. Enlloc de una x igual a zero, li assignarem un valor dinàmic que correspon a la i, que pren el valor de la posició que ocupa aquella dada dins el conjunt de dades seleccionat. Així doncs la x de la primera barra serà zero, de la segona serà 21, la següent 42, i així successivament.

.attr("x", function(d, i) {
            return i * 21;  //l'amplada de la barra que es 20 + 1 pixel d'espai entre barres
        })
        

20 barres

Aquí tens el codi en funcionament.

Això rutlla, però encara no és gaire flexible. Si el nostre conjunt de dades fos molt gran, les barres sortirien de l'espai de l'SVG pel costat dret i no les veuriem. Si cada barra ocupa 20 píxels d'amplada i en tenim 1 d'espai entre barres, llavors amb una amplada de 500 píxels de l'SVG només ens hi entren 23 barres. Fixa't com la barra que fa 24 queda amagada:

24 barres

És una molt bona pràctica fer servir valors dinàmics per a les coordenades — heights, widths, x, i y — perque així la visualització pot adaptar-se a l'espai del svg segons la quatitat de dades que tingui la selecció.

I com qualsevol altre cosa en programació, hi ha milers de maneres de fer-ho. En faré servir una de molt senzilla.

Primer de tot, corregiré la línia que defineix la posició de la x de cada barra:

            
                .attr("x", function(d, i) {
                    return i * (w / dataset.length);
                })
            
        

Ara el valor de la x està en funció del valor de l'amplada (w) de l'SVG i la quantitat de dades del conjunt de dades (dataset.length). Això comença a ser interessant, perque ara les nostres barres quedaran unifmormement espaiades tan si hi ha 20 valors

20 barres uniformement espaiades

com si només n'hi ha 5:

5 barres uniformement espaiades

Aquí pots veure el codi de l'exemple.

Ara farem que l'amplada de la barra widths sigui proporcional, també, perqué s'estrenyi o aixampli depenenet de la quantitat de dades que hi hagi. Afegirem noves variables que continguin l'amplada i l'alçada de l'SVG

            //Amplada i alçada
                var w = 500;
                var h = 100;
                var barPadding = 1;  // <-- Aquesta es nova!
            
        

i les farem servir a la línia a on definim width l'amplada de cada barra. Enlloc d'un valor estàtic de 20, l'amplada ara ocuparà una fracció de l'amplada total de l'SVG segons el nombre de dades a representar:

            
                .attr("width", w / dataset.length - barPadding)
            
        

20 barres uniformement espaiades amb amplada variabl

Funciona!

L'amplada de les barres i la posició x s'escala correctament, tant si hi ha 20 barres com 5

5 barres uniformement espaiades amb amplada variable

o 100:

100 barres uniformement espaiades amb amplada variabl

Per últim, hem de codificar l'alçada (height) de cada barra perque s'adapti al valor de les dades. Et podria semblar que ha de ser tan senzill com assignar d a cada valor d'alçada height:

            
                .attr("height", function(d) {
                    return d;
                });
            
        

Alçades dinàmiques

Hmm, curiós. Potser cal escalar una mica el valor de l'alçada?

.attr("height", function(d) {
            return d * 4;  // <--  escalat 4 vegades!
        });
        

Alçades dinàmiques

Per desgràcia, no és així de fàcil - volem que les nostres barres creixin cap amunt i no cap avall - però no culpem D3 , culpem l'SVG .

Recordaràs, del capítol "El primer svg" que les formes, en aquest cas els rectangles rects, tenen l'origen de les coordenades x i y a la cantonada superior-esquerra.A nosaltres, ens seria molt més senzill que l'origen de coordenades fos a la cantonada inferior-esquerra, però, que hi farem!. No és així com ho té definit l'SVG, i, francament li importa un rabe el que ens sembli millor.

Sabent que les nostres barres creixent cap per avall, els hem de capgirar. Si restem a l'alcada total de l'SVG el valor de la dada trobarem el punt superior de la nostra barra capgirada:

            
                .attr("y", function(d) {
                    return h - d;  //L'alçada menys el valor de la dada
                })
            
        

Llavors, per fer que la part inferior de la nostra barra, sigui la part inferior de l'SVG, cada alçada del rects serà el valor mateix de la dada:

            
                .attr("height", function(d) {
                    return d;  //Just the data value
                });
            
        

Creixent des de dalt cap a baix

Escalem un xic les barres canviant d per d * 4. (Notea: Més tard aprendrem el mètode de D3 scales, que ofeix més i millors opcions per aquesta tasca.)

Creixent més des de dalt cap a baix

Aquí tens el codi pel nostre diagrama que creix de baix cap a dalt

Color

Afegir color és senzill. Fes servir el mètode attr() per definir l'atribut fill:

            
                .attr("fill", "teal");
            
        

barres de color verd

Aquí tens el diagrama de color verd. Però sovint, el que voldràs és que el color reflecteixi alguna propietat de les dades. Per tant el que vols és encode les dades en forma de color. (En el cas del nostre diagrama de barres, hi haurà una codificació dual, amb la que la mateixa dada codificarà dues propietats de la barra, la seva alçada i el seu color.)

Fer servir les dades per donar color és tan senzill com escriure una nova funció que lligui d amb l'atribut color. Aquí, canviem el color verd "tral" per la nostra funció:

            
                .attr("fill", function(d) {
                    return "rgb(0, 0, " + (d * 10) + ")";
                });
            
        

barres de tons blaus en funció de les dades

Aquí tens el codi. Aquest no és un efecte visua particulament útil, però et dona una pista de com traduir les dades en color. Aquí, la d es multiplica per 10, i fem servir aquest valor per definir la part blava de l'ordre rgb(). Així doncs, a valors més grans de d (barres més altes) major quantitat de blau i per tant un blau més brillant i clar i viceversa, valors més petits menor quantitat de blau i per tant un color més proper al negre.

Etiquetes

A vegades amb l'efecte visual no es suficient, i necessites mostra el valor de la dada en forma de text dins la visualització. Aquí, és a on entre el valor de l'etiqueta i amb D3, són molt fàcils de generar.

Recordes del capítol sobre le primer svg, que podiem afegir elements de text a un SVG. Començarem amb:

            
                svg.selectAll("text")
                   .data(dataset)
                   .enter()
                   .append("text")
            
        

Ho reconeixes? El mateix que hem fet pel rectangles (rect), ara ho fem pels texts. Primer, selecciona el que vols, agafa les dades amb data(), fes servir enter() per crear els espais per a les noves dades, i finalment afegeix (append()) els nous elements text al DOM.

Allargarem el codi per incloure el valor de les dades a cada element text fent servir el mètode text().

               
                .text(function(d) {
                    return d;
                })
            
        

i l'allargarem una mica més enllà, incloent la x i y de la posició del text. És tan fàcil com copiar i enganxar el mateix codi x/y que hem fet servir per les barres:

            
                .attr("x", function(d, i) {
                    return i * (w / dataset.length);
                    })
                .attr("y", function(d) {
                    return h - (d * 4);
                });
            
        

Petites etiquetes del valor!

Aha! Etiquetes dels valors! Però alguna queda tallada per la part superior. Intentem moure-es cap a baix, dins les barres, afegint-hi una petita quantitat al cálcul de la x and y:

            
               .attr("x", function(d, i) {
                    return i * (w / dataset.length) + 5;  // +5
                })
                .attr("y", function(d) {
                    return h - (d * 4) + 15;              // +15
                });
            
        

etiquetes dels valors a l'interior de les barres

Millor, però no es llegeix gaire bé. Aforturnadament ho podem arreglar així:

               
                .attr("font-family", "sans-serif")
                .attr("font-size", "11px")
                .attr("fill", "white");
            
        

Etiquetes de valor realment boniques

Fantasti-codi! Si no ets un obsés de la tipografia, ja ho tens tot. Si, pel contrari, ets com jo, hauràs notat que les etiquetes no estan perfectament alineades amb les seves barres. Això és força fàcil d'arreglar.Fem servir l'atribut text-anchor de l'SVG per centrar-les horitzontalment al valor de x assignat:

            
              .attr("text-anchor", "middle")
            
        

Llavors, canviem la manera en que calculem la coordenada x definint-la al costat esquerra de cada barra més la meitat de la seva amplada:

                
                .attr("x", function(d, i) {
                    return i * (w / dataset.length) + (w / dataset.length - barPadding) / 2;
                })
            
        

I també afegirem un píxel més a la coordenad yperque quedi més ben espait:

            
                .attr("y", function(d) {
                    return h - (d * 4) + 14;  //15 is now 14
                })
            
        

Etiquetes centrades

Fet! Ja tenim llest el diagrama de barres. Anem a explorar altres temes.

Ves al següent capítol: Fent un gràfic de dispersió