Datavisualisering – kul med statistik och D3.js

Marcus Olsson,

Datavisualisering – och "infographics" – är verkligen något som jag alltid har älskat och varit fascinerad av. När jag var liten kunde jag spendera timmar på att studera grafer och diagram i olika uppslagsverk och tidningar. Idag är den stora "förebilden" Nicholas Feltron (tycka vad man vill om Facebooks "Timeline" som han var med och skapade) som minutiöst håller reda på all möjligt statistik om sitt liv, och sedan släpper dem som "årliga rapporter" som visar grafer över allt som han har varit med om och gjort under året.

Att skapa infografik själv är inget som jag varken är vidare duktig på, eller har försökt göra. Men jag håller just nu på med ett projekt där jag ska visualisera viss data för att ge en snabb överblick – och hade tänkt att testa något annat än Google Charts API, nämligen D3.js (D3 = Data Driven Documents) som är ett mycket kraftfullt JavaScript-bibliotek för att plottra ut grafer och diagram från data. Haken med D3.js är ju dock att man bör hantera sin gymnasiematte för att få ett någorlunda bra resultat...

Här nedan visar jag en mycket simpelt diagram över de fem mest förekommande internetleverantörerna (procentuellt) som besökarna av min webbplats använder (fungerar endast i "moderna webbläsare", alltså inte < IE9. Försökt att göra så att den anpassas på bredden – men kan se lite lustigt ut på t.ex. en iPhone). Och om du missade animationen, ladda om sidan:

Det var betyderligt lättare att komma igång med än vad jag hade räknat med, och tänker definitivt fortsätta att experimentera med det för att försöka göra mer avancerade saker.

Här nedan finns den fulla källkoden (kommenterad) för den som är intresserad:

1<script type="text/javascript">
2
3 //Ange höjden och bredden på svg-elementet
4 var svgHeight = 220;
5 var svgWidth = '100%';
6 //Skapa en skala med 10 färger som vi kan använda
7 var color = d3.scale.category10();
8
9 //Ange datan i JSON
10 var data = [
11 {name: 'Telia', visitors: 30},
12 {name: 'ComHem', visitors: 18},
13 {name: 'Tele2', visitors: 12},
14 {name: 'Telenor', visitors: 10},
15 {name: 'Tre', visitors: 5},
16 ];
17
18 //Skapa svg-elementet och append:a den till div#isp
19 var svg = d3.select('#isp').append('svg:svg')
20 .attr("width", svgWidth)
21 .attr("height", svgHeight);
22
23 var elementWidth = document.getElementById("isp");
24 //Ett litet extra steg, då den här webbplatsen använder
25 //responsiv design, så vill man ju ha bredden på #isp
26 elementWidth = elementWidth.offsetWidth;
27 //Avståndet mellan cirklarna
28 var spacing = elementWidth / (data.length+1);
29
30 //Skapa cirklar
31 svg.selectAll("circle")
32 .data(data)
33 .enter().append("svg:circle")
34 .attr("class", "circle")
35 //Koordinater i x och y-led, samt radien
36 .attr("cx", 0)
37 .attr("cy", 100)
38 .attr("r", function(d) {
39 // d = datum (data i singular)
40 // besökarantalet i procent ggr 3
41 // anger cirkelns radie
42 return d.visitors * 3}
43 )
44 .style("stroke", "#fff")
45 //"Mappa" en färg till cirkeln
46 .style("fill", function(d, i) {
47 return color(i % 3)
48 })
49 //En fin liten animation.
50 //Cirklarna börjar alla på koordinaterna
51 // 0x 100y, men animeras till sin "riktiga"
52 // x-position
53 .transition()
54 .delay(100)
55 .duration(2000)
56 .attr("x", function(d, i) {
57 // Placera cirkeln i x-led:
58 // avståndet mellan cirklarna + indexet för
59 // datan (ex. 0 = telia, 1 = ComHem etc.)
60 return spacing * (i+1);
61 });
62
63 //Typ samma som för ovan, men append:ar text
64 svg.selectAll("text")
65 .data(data)
66 .enter().append("svg:text")
67 .attr("x", 0)
68 .attr("y", 105)
69 .attr("text-anchor", "middle")
70 .text(function(d) { return d.name;})
71 .attr("fill", "#000;")
72 .transition()
73 .delay(100)
74 .duration(2000)
75 .attr("x", function(d, i) {
76 return spacing * (i+1);
77 });
78</script>
1<script type="text/javascript">
2
3 //Ange höjden och bredden på svg-elementet
4 var svgHeight = 220;
5 var svgWidth = '100%';
6 //Skapa en skala med 10 färger som vi kan använda
7 var color = d3.scale.category10();
8
9 //Ange datan i JSON
10 var data = [
11 {name: 'Telia', visitors: 30},
12 {name: 'ComHem', visitors: 18},
13 {name: 'Tele2', visitors: 12},
14 {name: 'Telenor', visitors: 10},
15 {name: 'Tre', visitors: 5},
16 ];
17
18 //Skapa svg-elementet och append:a den till div#isp
19 var svg = d3.select('#isp').append('svg:svg')
20 .attr("width", svgWidth)
21 .attr("height", svgHeight);
22
23 var elementWidth = document.getElementById("isp");
24 //Ett litet extra steg, då den här webbplatsen använder
25 //responsiv design, så vill man ju ha bredden på #isp
26 elementWidth = elementWidth.offsetWidth;
27 //Avståndet mellan cirklarna
28 var spacing = elementWidth / (data.length+1);
29
30 //Skapa cirklar
31 svg.selectAll("circle")
32 .data(data)
33 .enter().append("svg:circle")
34 .attr("class", "circle")
35 //Koordinater i x och y-led, samt radien
36 .attr("cx", 0)
37 .attr("cy", 100)
38 .attr("r", function(d) {
39 // d = datum (data i singular)
40 // besökarantalet i procent ggr 3
41 // anger cirkelns radie
42 return d.visitors * 3}
43 )
44 .style("stroke", "#fff")
45 //"Mappa" en färg till cirkeln
46 .style("fill", function(d, i) {
47 return color(i % 3)
48 })
49 //En fin liten animation.
50 //Cirklarna börjar alla på koordinaterna
51 // 0x 100y, men animeras till sin "riktiga"
52 // x-position
53 .transition()
54 .delay(100)
55 .duration(2000)
56 .attr("x", function(d, i) {
57 // Placera cirkeln i x-led:
58 // avståndet mellan cirklarna + indexet för
59 // datan (ex. 0 = telia, 1 = ComHem etc.)
60 return spacing * (i+1);
61 });
62
63 //Typ samma som för ovan, men append:ar text
64 svg.selectAll("text")
65 .data(data)
66 .enter().append("svg:text")
67 .attr("x", 0)
68 .attr("y", 105)
69 .attr("text-anchor", "middle")
70 .text(function(d) { return d.name;})
71 .attr("fill", "#000;")
72 .transition()
73 .delay(100)
74 .duration(2000)
75 .attr("x", function(d, i) {
76 return spacing * (i+1);
77 });
78</script>