Lage Et Linjediagram I D3.js v. 5
Tiden er kommet for å øke spillet vårt og lage et linjediagram fra bunnen av. Og ikke bare et linjediagram: en multi-serie graf som kan huse et hvilket som helst antall linjer. I tillegg til å håndtere flere linjer, vil vi jobbe med tids-og lineære skalaer, akser og etiketter – eller rettere sagt, få dem til å fungere for oss. Det er mye å gjøre, så jeg foreslår at du brenner Av D3-serveren din og la oss få sprekker.
Dokumentforberedelse
som første trinn må vi forberede dataene og filstrukturen for visualiseringen. Opprett line_chart.html, stiler.css og data.csv i prosjektmappen og fylle dem med utdrag som følger. Dette vil få oss i gang.
Lim inn dette i linjediagrammet.html-fil. Koden definerer svg-elementet for oss, slik at vi kan begynne å tegne med en gang. Jeg har også laget en grunnleggende struktur på forhånd, slik at det er lettere å navigere i dokumentet når vi jobber med sine spesielle seksjoner.
Legg igjen stilene.css-dokumentet er tomt for nå. Lim inn følgende rader til data.csv. Linjediagrammet vil inneholde flere serier: tilsvarende kolonnene A, B Og C.
date,A,B,C20-Jul-2019,10,20,1621-Jul-2019,11,22,1822-Jul-2019,13,19,2123-Jul-2019,11,17,2224-Jul-2019,15,16,2025-Jul-2019,16,19,1826-Jul-2019,19,21,1827-Jul-2019,22,25,1528-Jul-2019,18,24,1229-Jul-2019,14,20,1630-Jul-2019,14,18,1831-Jul-2019,16,18,2101-Aug-2019,15,20,2202-Aug-2019,14,21,19
Data Forberedelse
det første trinnet – og et avgjørende skritt for hele visualiseringen – er å lese dataene riktig. Jeg har brukt et multi-serie eksempel av en grunn: mens det er ganske enkelt å plotte en enkelt bane, håndterer flere linjer (spesielt et udefinert antall av dem) krever Litt Mer Av D3 finesse. Lim inn følgende i data-delen, last inn html-dokumentet og se gjennom konsollloggen i nettleseren din:
const timeConv = d3.timeParse("%d-%b-%Y");const dataset = d3.csv("data.csv");dataset.then(function(data) { const slices = data.columns.slice(1).map(function(id) { return { id: id, values: data.map(function(d){ return { date: timeConv(d.date), measurement: +d }; }) }; });console.log("Column headers", data.columns);console.log("Column headers without date", data.columns.slice(1));// returns the sliced datasetconsole.log("Slices",slices); // returns the first sliceconsole.log("First slice",slices);// returns the array in the first sliceconsole.log("A array",slices.values); // returns the date of the first row in the first sliceconsole.log("Date element",slices.values.date); // returns the array's lengthconsole.log("Array length",(slices.values).length);
La oss se gjennom transformasjonene som kalles på datasettet vårt en etter en:– data.kolonner returnerer csv-overskrifter-data.kolonne.slice(1) returnerer csv overskrifter uten dato kolonne (slice starter på kolonne indeksert på 1) – map () kaller en funksjon på hvert element i matrisen – bestående Av A, B, Og C)– la oss kalle hver av disse elementene en ‘slice’ – map() tildeler kolonnenavnet som et id-element til hver skive – så det tildeler en verdier array til hver skive-merk hvordan verdier element fremkaller en funksjon– Her kartlegger vi informasjon fra det opprinnelige datasettet: arrayet vil bestå av 2 kolonner, dato og måling. Datoen er avledet fra den første kolonnen (og forvandlet til et datoformat), og målingen er tatt fra kolonnen som svarer til stykkets id.At på slutten av disse transformasjonene får vi 3 arrays: A, B Og C, med 2 kolonner hver: dato og måling. Dette er en utrolig fleksibel måte å skjære opp et datasett: uansett hvor mange kolonner den har! Alt er gjort i de få radene. Jeg har skrevet ut litt informasjon til konsollen for å hjelpe deg med å se gjennom kodebiten.
Skalering Forberedelse
etter at dataene er lest inn, må vi konfigurere skaleringsmekanismen. Dette gjøres for å skrive ut diagrammet i henhold til svgs eiendom. Skalaer transformere data input (våre datoer og verdier) til koordinater på svg planet. Lim inn følgende linjer i SKALAVSNITTET.
const xScale = d3.scaleTime().range();const yScale = d3.scaleLinear().rangeRound();xScale.domain(d3.extent(data, function(d){ return timeConv(d.date)}));yScale.domain();
vi vil plotte datoene på x-aksen og verdiene på y-aksen. D3 gir en scaleTime () metode for skalering datoer, og en scaleLinear() metode for kontinuerlige verdier. Vi bestemmer først skalaens rekkevidde: hva inngangsverdiene skal oversettes til. I dette tilfellet vil vi strekke dataverdiene fra 0 til svgs bredde, og de numeriske verdiene fra svgs høyde til 0. Som det andre trinnet angir vi inngangsdatadomenet. Et domene består av alle verdier mellom et angitt minimum og maksimum som et datasett kan ta. I stedet for manuelt å se opp disse verdiene, sender vi det gjennom innebygde d3-funksjoner– – d3.omfang() returnerer en minimums – og maksimumsverdi for en matrise (i naturlig rekkefølge)– dette vil fungere perfekt på vår datosett-d3.max () returnerer en maksimumsverdi for matrisen. Legg merke til hvordan i dette eksemplet trekker vi først ut en maksimumsverdi fra hver matrise for å velge maksimalt alle tre. Jeg har også lagt 4 til maksimumsverdien av rent subjektive estetiske grunner: jeg ønsket å ha litt plass over grafen.
skalaene er nå satt opp. Hvis du ikke har nok av skalaer og ønsker å se flere eksempler, ta en titt på min forrige tutorial.
en godt konfigurert skala gjør at Vi kan begynne å plotte verdier på svg. Hver gang vi fremkaller datasettet, trenger vi bare å ringe en passende skala på den.
Akser
nok chatting-la oss tegne noe allerede! Akser er et godt utgangspunkt: hvis plottet riktig, vil de forsikre oss om at dataene er lest som forventet, og at det skaleres så pent som vi har forestilt oss.
Lim inn DETTE TIL AKSER under Prepareringsdelen:
const yaxis = d3.axisLeft().scale(yScale); const xaxis = d3.axisBottom().scale(xScale);
OG DETTE TIL AKSER under Tegningsseksjonen:
svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + height + ")") .call(xaxis);svg.append("g") .attr("class", "axis") .call(yaxis);
og så enkelt som at vi har plottet x-og y-aksene!
Ganske Vist er aksene ikke de mest elegante i verden (det er noen vakre akser der ute), men de er her! Det er noen ekstra tweaks vi kan søke om å gjøre dem mer vennlige for leseren.
La oss se på x-aksen først: det er noe morsomt som skjer med datoene. Lesing fra venstre, vi får ‘Lør 20’, ‘Juli 21’, ‘Man 22′, og på et tidspunkt kommer vi bare’August’. Virker som måneder og dager har kommet i en ulydig blanding av variasjoner. Vi må sette og avslutte denne freestyle, og med dette mener jeg at vi bør bestemme hvilket datoformat vi ønsker å skrive ut på skjermen. D3.axis () metoden tillater oss å justere alle slags ting for flått-deres antall, intervall mellom punktene, visningsformat, etc. La oss konfigurere noen av dem for begge aksene.
Erstatt aksedefinisjonen i Delen Forberedelse med følgende tekstutdrag og oppdater visualiseringen:
const yaxis = d3.axisLeft() .ticks((slices.values).length) .scale(yScale);const xaxis = d3.axisBottom() .ticks(d3.timeDay.every(1)) .tickFormat(d3.timeFormat('%b %d')) .scale(xScale);
koden ovenfor angir et angitt antall flått for y-aksen (14, eller så mange som det er matriseelementer / csv-rader). I tilfelle av x-aksen vil et kryss bli vist med en granularitet på en dag, hver dag. Det er oppnådd ved å sette tick-egenskapen til d3.tidsdag.hver(1). Formatet på viste datoer vil vise dagen og forkortet måned for hvert kryss. Etter disse endringene ender vi med noe forbedrede akser:
Ulydige datoer er ikke lenger et problem!
For å gjøre det enda bedre (er det enda mulig!!!) vi kan legge til en etikett på y-aksen for å vise hva verdiene står for. Mens datoene er selvforklarende, tallene på egen hånd bærer ingen informasjon. Legg til en etikett (kall det hva du vil – jeg gikk Med Frekvens) ved å legge til følgende på y-aksen tegningen:
//this you hadsvg.append("g") .attr("class", "axis") .call(yaxis)//this you append .append("text") .attr("transform", "rotate(-90)") .attr("dy", ".75em") .attr("y", 6) .style("text-anchor", "end") .text("Frequency");
(det er ingen stil satt for etiketten, så det vil ikke vises på grafen – men tro Meg Og google Chrome utviklerverktøy, det er der)
Til Slutt, la oss forbedre utseendet på aksene. Ved å forbedre mener jeg: angi farger, bredder og gjengivelse av hvert enkelt element, og bestem skrifttypen som skal brukes. Lim inn følgende til css-filen og gjerne lage din egen stil beslutninger:
/* AXES *//* ticks */.axis line{stroke: #706f6f;stroke-width: 0.5;shape-rendering: crispEdges;}/* axis contour */.axis path {stroke: #706f6f;stroke-width: 0.7;shape-rendering: crispEdges;}/* axis text */.axis text {fill: #2b2929;font-family: Georgia;font-size: 120%;}
flåttene styres av .linjeelementet i aksen, mens den faktiske aksen er satt med den .sti element. Aksene ser skarpe ut (forfatterens ydmyke mening) og klar til å ønske noen data velkommen:
Uten videre, la oss plotte diagrammet!
Linjediagram
Linjer er i hovedsak d3.baner () som forbinder en haug med (x, y) koordinater på ET 2d-plan. For å konstruere en linje må du fortelle den hvor du skal finne x-og y-koordinatene og deretter legge det til svg. Lim inn følgende utdrag til de tidligere opprettede plassholderne, og la oss se gjennom koden sammen.
Dette burde komme TIL LINJER litt under Forberedelsesdelen:
const line = d3.line() .x(function(d) { return xScale(d.date); }) .y(function(d) { return yScale(d.measurement); });
I denne kodebiten kalte vi en linjekonstruktør, d3.linje () som bruker to aksessorer: x for verdier på horisontalplanet og y for den loddrette aksen. Her peker vi bare på de mest granulære verdiene av vår matrise, dato og måling (dette er ikke på tide å bekymre seg for den nestede csv-strukturen). Når det er gjort, lim inn FØLGENDE LINJER under Tegningsdelen:
const lines = svg.selectAll("lines") .data(slices) .enter() .append("g"); lines.append("path") .attr("d", function(d) { return line(d.values); });
Dette krever litt forklaring. Variabellinjene velger et uidentifisert antall linjer fra svg-og forteller Umiddelbart D3 At Det vil være 3 linjer ved å peke på stykkene (linjene A, B og C). Det legger deretter et g-element til hver av dem: et grupperingselement som vil gjøre livet enklere i løpet av tiden. G-elementet samler alt som har å gjøre med en bestemt diagramserie (aka et stykke i arrayet): linjen (representert over som en bane), datapunktene som vi vil kunne svinge over, og serieetikettene.
den første tingen å legge til linjer (som faktisk er 3 tomme g-beholdere) er diagramlinjene selv. Vi kaller d3.linje () konstruktor pa dataene for a tegne en bane. Se hvordan vi først må få tilgang til verdiene under hvert stykke. Dette sendes deretter til konstruktøren som trekker datoer og målinger etter behov.
etter at endringene er lagret, blir visualiseringen oppdatert til dette:
Ok, dette er ikke perfekt,men tro meg, vi kommer dit! La oss bruke noen estetiske reparasjoner på diagrammet og observere hvordan det former seg. Legg til følgende stiler.css:
/* LINE CHART */path {fill: none;stroke: #ed3700;}
vi må sette fyllet til ingen for å få figurene til å vises som linjer. Oppdater grafen:
hva skiller et linjediagram fra en rekke linjer som sitter fast sammen på en graf? Evnen til å skille mellom serien. For øyeblikket har vi bare den tidligere.
For det første må vi skille mellom linjene i koden. La oss legge til en id til hver linjeklasse-legg til FØLGENDE I LINES-delen I Forberedelsesdelen:
let id = 0;const ids = function () { return "line-"+id++;}
denne lille koden skaper en teller som vi kan utnytte for å automatisk tildele en linje id til hver ekstra linje. La oss referere til telleren i klassens eiendom av stiene. Juster koden i LINJER-delen for å legge til klasseegenskapen:
const lines = svg.selectAll("lines") .data(slices) .enter() .append("g"); lines.append("path") .attr("class", ids) .attr("d", function(d) { return line(d.values); });
og magisk, får hver bane sin egen klasse!
det som gjenstår for oss å gjøre er å referere til disse klassene i css og gi hver linje sin egen unike karakter. Endre Linjediagramdelen av css for å si:
/* LINE CHART */path.line-0 {fill: none;stroke: #ed3700;}path.line-1 {fill: none;stroke: #2b2929;stroke-dasharray: 2;}path.line-2 {fill: none;stroke: #9c9c9c;stroke-dasharray: 6;}
Legg merke til hvordan jeg ikke bare endrer fargen, men også endrer streken på hver linje. Husk at ca 10% av alle mennesker har en viss grad av fargeblindhet, og i all rettferdighet kan det være vanskelig for noen av oss å skille mellom farger. Farger vil bare blande sammen hvis det er for mange dataserier, og deres fargetone vil vise forskjellig på hver skjerm.
etter at endringene er brukt, blir linjene tydelig skilt på grafen-som vist nedenfor:
nå er serien differensiert, Men det er fortsatt umulig å fortelle hvilken som er hvilken, med mindre du har lagret de underliggende dataene og har en ganske syk visuell fantasi, i så fall lurer jeg på hvorfor du trengte en graf i utgangspunktet. For å hjelpe de fleste av oss i seriegjenkjenningen foreslår jeg at vi legger serienavnet til høyre side av grafen. Legg til følgende i tegningsdelen AV LINJER:
lines.append("text") .attr("class","serie_label") .datum(function(d) { return { id: d.id, value: d.values}; }) .attr("transform", function(d) { return "translate(" + (xScale(d.value.date) + 10) + "," + (yScale(d.value.measurement) + 5 ) + ")";}) .attr("x", 5) .text(function(d) { return ("Serie ") + d.id; });
kodebiten lokaliserer slutten av hver linje og legger til et tekstelement. Teksten skrives Ut Som Serie A, Serie B eller Serie C, avhengig av linjen. Legg til følgende i css-dokumentet for å justere serieetikettene:
.serie_label {fill: #2b2929;font-family: Georgia;font-size: 80%;}
etikettene er vedlagt! Gøy.
Vi kan alle være enige om at dette er et stilig linjediagram! Jeg har limt inn hele koden nedenfor. Pass på å sjekke ut den andre delen av opplæringen som presenterer to scenarier for å legge til interaktivitet i diagrammet.
Følg Meg På Twitter for flere data-sciency / datavisualiseringsprosjekter!
Kodeeksempler
line_chart.html:
stiler (styles).css:
/* AXES *//* ticks */.axis line{stroke: #706f6f;stroke-width: 0.5;shape-rendering: crispEdges;}/* axis contour */.axis path {stroke: #706f6f;stroke-width: 0.7;shape-rendering: crispEdges;}/* axis text */.axis text {fill: #2b2929;font-family: Georgia;font-size: 120%;}/* LINE CHART */path.line-0 { fill: none; stroke: #ed3700;}path.line-1 { fill: none; stroke: #2b2929; stroke-dasharray: 2;}path.line-2 { fill: none; stroke: #9c9c9c; stroke-dasharray: 6;}.serie_label { fill: #2b2929; font-family: Georgia; font-size: 80%;}
data.csv: