Tworzenie wykresu liniowego w D3.js v. 5

nadszedł czas, aby przyspieszyć naszą grę i stworzyć wykres linii od podstaw. I nie tylko każdy wykres liniowy: wykres wieloseryjny, który może pomieścić dowolną liczbę linii. Oprócz obsługi wielu linii, będziemy pracować z skalami czasu i liniowymi, osiami i etykietami-a raczej zlecimy im pracę dla nas. Jest wiele do zrobienia, więc proponuję odpalić serwer D3 i do roboty.

stworzymy to piękno! Zabawa!

przygotowanie dokumentu

jako pierwszy krok musimy przygotować Dane i strukturę plików do wizualizacji. Utwórz line_chart.html, style.css i danych.plik csv w folderze projektu i wypełnij go poniższymi fragmentami. To da nam początek.

Wklej to do line_chart.plik html. Kod definiuje dla nas element svg, dzięki czemu możemy od razu zacząć rysować. Stworzyłem również podstawową strukturę z góry, dzięki czemu łatwiej jest poruszać się po dokumencie podczas pracy nad jego poszczególnymi sekcjami.

Zostaw styl.dokument css na razie pusty. Wklej następujące wiersze do danych.csv. Wykres liniowy będzie zawierał wiele serii: odpowiadających kolumnom A, B i 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

przygotowanie danych

pierwszym krokiem-i kluczowym dla całej wizualizacji-jest prawidłowy odczyt danych. Użyłem przykładu z wielu serii nie bez powodu: chociaż rysowanie pojedynczej ścieżki jest dość proste, obsługa wielu linii (zwłaszcza nieokreślonej ich liczby) wymaga nieco więcej finezji D3. Wklej następujące elementy do sekcji dane, przeładuj dokument html i przejrzyj dziennik konsoli w przeglądarce:

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);

przejrzyjmy transformacje wywołane na naszym zbiorze danych jeden po drugim: – data.columns zwraca nagłówki csv-data.kolumny.slice (1) zwraca nagłówki csv bez kolumny date (kawałek zaczyna się od Kolumny indeksowanej na 1) – map () wywołuje funkcję na każdym elemencie tablicy (składającej się z A, B I C) – nazwijmy każdy z tych elementów 'slice’– map () przypisuje nazwę kolumny jako element id do każdego plasterka– następnie przypisuje tablicę wartości do każdego plasterka– zauważ, jak element values wywołuje funkcję. Tutaj mapujemy informacje z oryginalnego zbioru danych: tablica będzie składać się z 2 kolumn, daty i pomiaru. Data jest wyprowadzana z pierwszej kolumny (i przekształcana do formatu daty), A pomiar jest pobierany z kolumny odpowiadającej wycinkowi id.At na koniec tych przekształceń otrzymujemy 3 tablice: A, B I C, z 2 kolumnami każda: date i measurement. Jest to niezwykle elastyczny sposób na pokrojenie zestawu danych: niezależnie od tego, ile ma kolumn! Wszystko odbywa się w tych kilku rzędach. Wydrukowałem kilka informacji na konsoli, aby pomóc ci przejrzeć fragment.

Dziennik konsoli

przejrzyj dziennik konsoli, aby uzyskać więcej informacji

przygotowanie wagi

po odczytaniu danych musimy skonfigurować mechanizm skalowania. Odbywa się to w celu wydrukowania wykresu zgodnie z nieruchomościami svg. Skale przekształcają dane wejściowe (nasze daty i wartości) w Współrzędne na płaszczyźnie svg. Wklej następujące linie do sekcji wagi.

const xScale = d3.scaleTime().range();const yScale = d3.scaleLinear().rangeRound();xScale.domain(d3.extent(data, function(d){ return timeConv(d.date)}));yScale.domain();

narysujemy daty na osi x i wartości na osi Y. D3 udostępnia metodę scaleTime () do skalowania dat oraz metodę scaleLinear () do wartości ciągłych. Najpierw decydujemy o zakresie skali: na co należy przetłumaczyć wartości wejściowe. W tym przypadku będziemy rozciągać wartości danych od 0 do szerokości svg, a wartości liczbowe od wysokości svg do 0. W drugim kroku określamy domenę danych wejściowych. Domena składa się ze wszystkich wartości pomiędzy określonym minimum i maksimum, które może przyjąć zestaw danych. Zamiast ręcznie sprawdzać te wartości, przekazujemy je przez wbudowane funkcje D3– – d3.extent() Zwraca minimalną i maksymalną wartość tablicy (w porządku naturalnym)– będzie to działać idealnie na naszym zestawie dat-d3.max () Zwraca maksymalną wartość tablicy. Zauważ, jak w tym przykładzie najpierw wyodrębniamy maksymalną wartość z każdej tablicy, aby następnie wybrać maksymalnie wszystkie trzy. Dodałem również 4 do maksymalnej wartości z czysto subiektywnych względów estetycznych: chciałem mieć trochę miejsca nad wykresem.

Jeśli nie masz wystarczająco dużo skal i chciałbyś zobaczyć więcej przykładów, spójrz na mój poprzedni tutorial.

dobrze skonfigurowana skala umożliwia nam rozpoczęcie rysowania wartości na svg. Za każdym razem, gdy wywołujemy zestaw danych, musimy po prostu nazwać go odpowiednią skalą.

dość gadania-narysujmy już coś! Osie są dobrym punktem wyjścia: jeśli są prawidłowo narysowane, zapewnią nam, że dane zostały odczytane zgodnie z oczekiwaniami i że skalują się tak ładnie, jak sobie wyobrażaliśmy.

Wklej to do osi pod sekcją przygotowanie:

const yaxis = d3.axisLeft().scale(yScale); const xaxis = d3.axisBottom().scale(xScale);

a to do osi pod sekcją rysunku:

svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + height + ")") .call(xaxis);svg.append("g") .attr("class", "axis") .call(yaxis);

i tak po prostu, że mamy wykreślone osie x i y!

osie już są!

co prawda, osie nie są najbardziej eleganckie na świecie (jest tam kilka ładnych osie), ale są tutaj! Istnieje kilka dodatkowych poprawek, które możemy zastosować, aby uczynić je bardziej przyjaznymi dla czytelnika.

spójrzmy najpierw na oś x: z datami dzieje się coś zabawnego. Czytając od lewej dostajemy „Sob 20”, „Lip 21”, „Pon 22”, a w pewnym momencie docieramy tylko do „sierpnia”. Wydaje się, że miesiące i dni nadeszły w niesubordynowanej mieszance odmian. Musimy skończyć z tym freestylem, i przez to mam na myśli, że powinniśmy zdecydować, jaki format daty chcemy wydrukować na ekranie. D3.metoda axis() pozwala nam dostosować wszystkie rzeczy dla wskazów – ich liczbę, odstęp między punktami, format wyświetlania, itp. Skonfigurujmy niektóre z nich dla obu osi.

Zastąp definicję osi w sekcji przygotowanie poniższym fragmentem i odśwież wizualizację:

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);

powyższy kod określa ustawioną ilość wskazów dla osi y (14, lub tyle, ile jest elementów tablicy / wierszy csv). W przypadku osi x będzie wyświetlany kleszcz z ziarnistością dnia, każdego dnia. Zostało to osiągnięte przez ustawienie właściwości tick na d3.timeDay.każdy(1). Format wyświetlanych dat pokazuje dzień i skrócony miesiąc dla każdego kleszcza. Po tych zmianach otrzymujemy nieco ulepszone osie:

nareszcie

nieposłuszne randki nie są już problemem!

żeby było jeszcze lepiej (czy to w ogóle możliwe!!!) możemy dodać etykietę do osi y, aby pokazać, co oznaczają wartości. Chociaż daty są oczywiste, liczby same w sobie nie zawierają żadnych informacji. Dodaj etykietę (nazwij ją jak chcesz-poszedłem z częstotliwością), dołączając następujący rysunek do osi y:

//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");

(nie ma ustawionego stylu dla etykiety, więc nie wyświetli się na wykresie-ale uwierz mi i narzędziom programistycznym Google Chrome, jest tam)

Etykieta

Etykieta osi y jest niewidoczna

wreszcie poprawimy wygląd osi. Przez poprawienie mam na myśli: Ustaw kolory, szerokości i renderowanie każdego pojedynczego elementu i zdecyduj, jakiej czcionki użyć. Wklej poniższe elementy do pliku css i możesz podejmować własne decyzje dotyczące stylu:

/* 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%;}

kleszcze są kontrolowane przez .element liniowy osi, podczas gdy rzeczywista oś jest ustawiona za pomocą .element ścieżki. Osie wyglądają ostro (skromna opinia autora) i gotowe do przyjęcia niektórych danych:

Prettified axes

bez dalszych ceregieli, narysujmy Wykres!

Wykres linii

linie są zasadniczo d3.paths (), które łączą kilka współrzędnych (x, y) na płaszczyźnie 2D. Aby utworzyć linię, musisz powiedzieć jej, gdzie ma znaleźć jej współrzędne x i y, a następnie dołączyć ją do svg. Wklej następujące fragmenty do wcześniej utworzonych elementów zastępczych i przejrzyjmy razem kod.

to powinno trafic do LINES bit pod dzialka przygotowania:

const line = d3.line() .x(function(d) { return xScale(d.date); }) .y(function(d) { return yScale(d.measurement); });

w tym fragmencie nazwaliśmy konstruktor linii, d3.line (), która używa dwóch accesorów: x Dla wartości na płaszczyźnie poziomej i y dla osi pionowej. Tutaj po prostu wskazujemy najbardziej ziarniste wartości naszej tablicy, daty i pomiaru (nie jest to czas, aby martwić się o zagnieżdżoną strukturę csv). Po zakończeniu wklej następujące elementy do linii w sekcji rysowanie:

const lines = svg.selectAll("lines") .data(slices) .enter() .append("g"); lines.append("path") .attr("d", function(d) { return line(d.values); });

wymaga to wyjaśnienia. Zmienna lines wybiera niezidentyfikowaną liczbę linii z svg – i natychmiast informuje D3, że będą 3 linie, wskazując na zestaw plasterków (linie A, B I C). Następnie dodaje element g do każdego z nich: element grupujący, który ułatwi nam życie we właściwym czasie. Element g zbierze wszystko, co ma związek z konkretną serią Wykresów (aka wycinek w tablicy): linię (reprezentowaną powyżej jako ścieżka), jej punkty danych, nad którymi będziemy mogli najechać kursorem, oraz etykiety serii.

pierwszą rzeczą, którą należy dołączyć do linii (które są w rzeczywistości 3 pustymi kontenerami g), są same linie wykresu. Nazywamy d3.konstruktor linii () na danych do narysowania ścieżki. Zobacz, jak najpierw musimy uzyskać dostęp do wartości pod każdym plasterkiem. Jest to następnie przekazywane do konstruktora, który pobiera daty i pomiary zgodnie z wymaganiami.

po zapisaniu zmian wizualizacja zostanie zaktualizowana do tego:

zamiast wykresu liniowego mamy wykres górski

OK, to nie jest idealne, ale uwierz mi, że do tego zmierzamy! Zastosujmy kilka estetycznych poprawek na wykresie i obserwujmy, jak się kształtuje. Dołącz następujące elementy do stylów.css:

/* LINE CHART */path {fill: none;stroke: #ed3700;}

musimy ustawić wypełnienie na none, aby kształty pojawiały się ponownie jako linie. Odśwież Wykres:

pojawiły się linie

co oddziela wykres liniowy od kilku linii sklejonych razem na wykresie? Umiejętność rozróżniania serii. W tej chwili mamy tylko te pierwsze.

na początek, musimy dokonać rozróżnienia między liniami w kodzie. Dodajmy id do każdej klasy linii – dodaj następujące elementy do sekcji linie w części przygotowawczej:

let id = 0;const ids = function () { return "line-"+id++;}

ten mały fragment kodu tworzy licznik, który możemy wykorzystać, aby automatycznie przypisać identyfikator linii do każdej dodanej linii. Odwołajmy się do licznika we właściwości class ścieżki. Dostosuj kod w sekcji linie, aby dodać właściwość class:

const lines = svg.selectAll("lines") .data(slices) .enter() .append("g"); lines.append("path") .attr("class", ids) .attr("d", function(d) { return line(d.values); });

i magicznie, każda ścieżka dostaje swoją klasę!

Klasa ścieżki

linie mają własną tożsamość

co nam pozostaje do zrobienia, to odniesienie do tych klas w css i nadanie każdej linii własnego unikalnego charakteru. Zmień sekcję wykresu liniowego css na:

/* 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;}

zauważ, że nie tylko zmieniam kolor, ale także zmieniam obrys każdej linii. Pamiętaj, że około 10% wszystkich ludzi ma pewien stopień ślepoty na kolory i szczerze mówiąc, rozróżnianie kolorów może być trudne dla każdego z nas. Kolory po prostu się mieszają, jeśli jest zbyt wiele serii danych, a ich odcień pokaże się inaczej na każdym monitorze.

po wprowadzeniu zmian linie są wyraźnie oddzielone na wykresie – jak pokazano poniżej:

linie są wizualnie różne od siebie

teraz serie są zróżnicowane, ale nadal nie można powiedzieć, który z nich jest który, chyba że zapamiętałeś Podstawowe Dane i masz dość chorą wyobraźnię wizualną, w takim przypadku zastanawiam się, dlaczego w ogóle potrzebujesz wykresu. Aby pomóc większości z nas w rozpoznawaniu serii, proponuję dołączyć nazwę serii po prawej stronie wykresu. Dodaj następujące elementy do sekcji rysowania linii:

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; });

fragment lokalizuje koniec każdego wiersza i dodaje do niego element tekstowy. Tekst zostanie wydrukowany jako Serie A, Serie B lub Serie C, w zależności od wiersza. Dodaj następujące elementy do dokumentu css, aby dostosować etykiety serii:

.serie_label {fill: #2b2929;font-family: Georgia;font-size: 80%;}

etykiety są dołączone! Dobre czasy.

każda seria ma swoją własną etykietę

wszyscy możemy się zgodzić, że to jeden przystojny Wykres linii! Wklejam Pełny kod poniżej. Zapoznaj się z drugą częścią samouczka, która przedstawia dwa scenariusze dodawania interaktywności do wykresu.

Śledź mnie na Twitterze, aby uzyskać więcej danych-sciency / projekty wizualizacji danych!

próbki kodu

line_chart.html:

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