A fazer um gráfico de linhas em D3.js v. 5

chegou a hora de aumentar o nosso jogo e criar um gráfico de linha a partir do zero. E não apenas qualquer Gráfico de linhas: um gráfico de várias séries que pode acomodar qualquer número de linhas. Além de lidar com várias linhas, trabalharemos com escalas de tempo e lineares, eixos e rótulos – ou melhor, fazê-los trabalhar para nós. Há muito que fazer, por isso sugiro que dispares do teu servidor D3 e vamos começar.Vamos criar esta beleza! Divertido!

preparação do documento

como o primeiro passo que precisamos para preparar os dados e a estrutura do arquivo para a visualização. Criar um ‘line_chart’.html, styles.css, and data.csv na sua pasta de projecto e preencha-os com os excertos que se seguem. Isto vai fazer-nos começar.

Cole isto no “line_chart”.ficheiro html. O código define o elemento svg para nós para que possamos começar a desenhar imediatamente. Eu também criei uma estrutura básica à frente para que seja mais fácil navegar no documento enquanto trabalhamos em suas seções particulares.

deixar os estilos.documento css vazio por agora. Colar as seguintes linhas aos dados.csv. O gráfico de linhas contará com várias séries: correspondentes às colunas A, B E 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

preparação de dados

o primeiro passo – e um passo crucial para toda a visualização-é ler correctamente os dados. Eu usei um exemplo multi-série por uma razão: embora seja bastante simples traçar um único caminho, lidar com várias linhas (especialmente um número indefinido delas) requer um pouco mais de finesse D3. Colar o seguinte na secção de dados, reler o documento html e rever o registo da consola no seu navegador:

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

vamos rever as transformações chamadas em nosso conjunto de dados um por um: – dados.as colunas devolve os dados dos cabeçalhos do csv.coluna.o slice (1) devolve os cabeçalhos csv sem a coluna de data (a fatia começa na coluna indexada em 1) – map () chama uma função em cada elemento da lista (consistindo em A, B E C) – Vamos chamar a cada um desses elementos um ‘slice’– map () atribui o nome da coluna como um elemento id para cada fatia– então atribui uma lista de valores a cada fatia– note como o elemento valores evoca uma função. Aqui nós mapeamos informações do conjunto de dados originais: o array consistirá de 2 colunas, Data e medição. A data é derivada da primeira coluna (e transformada num formato de data), e a medida é retirada da coluna correspondente à fatia id.At o fim dessas transformações temos 3 matrizes: A, B E C, com 2 colunas cada: data e medida. Esta é uma maneira incrivelmente flexível de cortar um conjunto de dados: independentemente de quantas colunas ele tem! Está tudo feito nessas poucas filas. Imprimi algumas informações na consola para te ajudar a rever o excerto.

log de Console

reveja o log de console para mais informações

preparação de escalas

depois que os dados foram lidos em precisamos configurar o mecanismo de escala. Isto é feito para imprimir o gráfico de acordo com os imóveis do svg. Escalas transformam a entrada de dados (nossas datas e valores) para coordenadas no plano svg. Cole as seguintes linhas na secção de escalas.

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

vamos traçar as datas no eixo x e os valores no eixo Y. D3 provides a scaleTime () method for scaling dates, and a scaleLinear () method for continuous values. Primeiro decidimos o intervalo da escala: para que os valores de entrada devem ser traduzidos. Neste caso, vamos esticar os valores de dados de 0 para a largura do svg, e os valores numéricos a partir da altura do svg para 0. Como segundo passo, especificamos o domínio de dados de entrada. Um domínio consiste de todos os valores entre um mínimo especificado e máximo que um conjunto de dados pode tomar. Em vez de procurar manualmente esses valores, passamos através dele construído em funções D3:– d3.o extent () devolve um valor mínimo e máximo de um array (numa ordem natural) – isto irá funcionar perfeitamente no nosso conjunto de datas– d3.o max () devolve um valor máximo da lista. Note Como neste exemplo nós primeiro extraímos um valor máximo de cada array para então selecionar um máximo de todos os três. Eu também adicionei 4 ao valor máximo por razões estéticas puramente subjetivas: eu queria ter algum espaço acima do gráfico.

as balanças estão agora colocadas. Se você não tem escamas suficientes e gostaria de ver mais exemplos, dê uma olhada no meu tutorial anterior.

uma escala bem configurada permite – nos começar a traçar valores no svg. Cada vez que evocamos o conjunto de dados, só precisamos chamar uma escala apropriada sobre ele.Já chega de conversa – vamos desenhar alguma coisa! Os eixos são um bom ponto de partida: se traçados corretamente, eles nos garantirão que os dados foram lidos como esperado e que escalam tão bem quanto imaginamos.

Colar isto aos Eixos sob a secção de Preparação:

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

e isto aos Eixos sob a secção de desenho:

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

e tão simples quanto isso nós traçamos os eixos x e y!Os eixos estão aqui!Reconhecidamente, os eixos não são os mais elegantes do mundo (há alguns eixos bonitos lá fora), mas estão aqui! Existem alguns ajustes adicionais que podemos aplicar para torná-los mais amigáveis para o leitor.

vamos olhar primeiro para o eixo x: há algo engraçado acontecendo com as datas. Lendo a partir da esquerda, temos ‘Sat 20′,’ Jul 21′,’ Mon 22′, e em algum momento chegamos apenas’agosto’. Parece que os meses e os dias chegaram numa mistura insubordinada de variações. Temos de pôr fim a este estilo livre, e com isto quero dizer que devemos decidir qual o formato de data que gostaríamos de imprimir no ecrã. O d3.o método axis () nos permite ajustar todo o tipo de coisas para carrapatos-seu número, intervalo entre os pontos, formato de exibição, etc. Vamos configurar alguns deles para ambos os eixos.

substituir a definição dos eixos na secção de Preparação pelo seguinte excerto e actualizar a visualização:

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

o código acima especifica um conjunto de carrapatos para o eixo y (14, ou tantos quantos existem elementos array / linhas csv). No caso do eixo x um carrapato será exibido com uma granularidade de um dia, todos os dias. Isso foi conseguido ajustando a propriedade da carraça para d3.timeDay.every (1). O formato das datas apresentadas irá mostrar o dia e o mês abreviado para cada carrapato. Depois dessas mudanças acabamos com eixos um pouco melhorados:

Ordnung, finalmente as datas de desobediência não são mais um problema!

para torná-lo ainda melhor (é possível!!!) podemos adicionar uma etiqueta ao eixo y para mostrar o que os valores representam. Embora as datas sejam auto-explicativas, os números por si só não têm nenhuma informação. Adicione uma legenda (chame-lhe o que quiser-eu fui com frequência) adicionando o seguinte ao desenho do eixo 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");

(não existe um conjunto de estilo para o rótulo, por isso não aparece no gráfico – mas acredite em mim e nas Ferramentas de desenvolvimento do Google Chrome, ele está lá)

rótulo

o rótulo do eixo y é invisível

finalmente, vamos melhorar a aparência dos eixos. Por melhorar quero dizer: definir as cores, larguras e renderização de cada elemento, e decidir a fonte a usar. Cole o seguinte no ficheiro css e sinta-se à vontade para tomar as suas próprias decisões de estilo:

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

as carraças são controladas pelo.o elemento de linha do eixo, enquanto o eixo real é definido com o .elemento do caminho. Os eixos parecem afiados (a humilde opinião do autor) e prontos para receber alguns dados:

eixos Pretificados

sem mais delongas, vamos traçar o gráfico!

linha gráfico

as linhas são essencialmente d3.caminhos() que ligam um monte de (x, y) coordenadas em um plano 2D. Para construir uma linha você precisa dizer-lhe onde encontrar suas coordenadas x e y e, em seguida, adicioná-lo ao svg. Cola os seguintes excertos nas substituições criadas anteriormente e vamos rever o código em conjunto.

isto deve chegar às linhas bit sob a secção de Preparação:

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

neste trecho chamamos um construtor de linhas, d3.linha () que usa dois acessores: x para valores no plano horizontal, e y para o eixo vertical. Aqui nós simplesmente apontamos para os valores mais granulares de nossa matriz, data e medição (Este não é o momento para se preocupar com a estrutura aninhada csv). Uma vez feito, cole o seguinte em linhas sob a seção de desenho:

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

isto requer alguma explicação. As linhas variáveis selecionam um número não identificado de linhas do svg-e imediatamente diz ao D3 que haverá 3 linhas apontando para as fatias definidas (linhas A, B, E C). Em seguida, adiciona um elemento g a cada um deles: um elemento de agrupamento que tornará a nossa vida mais fácil no devido tempo. O elemento g irá coletar tudo o que tem a ver com uma determinada série de gráficos (aka uma fatia no array): a linha (representada acima como um caminho), seus pontos de dados que seremos capazes de pairar sobre, e as etiquetas da série.

a primeira coisa a adicionar às linhas (que são de fato 3 recipientes g vazios) são as próprias linhas de gráfico. Chamamos o d3.linha () construtor sobre os dados para desenhar um caminho. Veja como primeiro precisamos acessar os valores sob cada fatia. Isto é então passado para o construtor que puxa datas e medidas Conforme necessário.

após as alterações serem gravadas, a visualização é actualizada para este:

em vez de um gráfico de linha temos um gráfico de montanha

Ok, isto não é perfeito, mas acredite em mim, Estamos chegando lá! Vamos aplicar algumas correcções estéticas no gráfico e observar como ele se forma. Adicione o seguinte aos estilos.css:

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

precisamos de ajustar o enchimento a nenhum para que as formas reapareçam como linhas. Actualizar o gráfico:

as linhas surgiram

o que separa um gráfico de linhas de um conjunto de linhas presas em conjunto num gráfico? A capacidade de diferenciar entre as séries. Neste momento, só temos o primeiro. Para começar, precisamos fazer uma distinção entre as linhas do Código. Vamos adicionar um id a cada classe de linha-adicione o seguinte à seção de linhas na parte de Preparação:

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

este pequeno pedaço de código cria um contador que podemos alavancar para atribuir automaticamente um ID de linha a cada linha adicionada. Vamos referenciar o contador na propriedade de classe dos caminhos. Ajustar o código na secção de linhas para adicionar a propriedade da classe:

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

e magicamente, cada caminho tem a sua própria classe!

Path class

the lines are given their own identity

What’s left for us to do is to reference these classes in the css and give each line its own unique character. Mudar a secção do Gráfico de linhas do css para dizer:

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

repare que não estou apenas a alterar a cor, mas também a mudar o traço de cada linha. Lembre-se que cerca de 10% de todas as pessoas têm algum grau de cegueira da cor e, com toda a justiça, diferenciar entre cores pode ser complicado para qualquer um de nós. As cores misturar-se-ão se existirem demasiadas séries de dados e a sua tonalidade mostrar-se-á de forma diferente em cada monitor.

após as alterações terem sido aplicadas, as linhas devem ser claramente separadas no gráfico abaixo.:

as linhas são visualmente diferentes umas das outras

agora a série é diferenciada, mas ainda é impossível dizer qual é qual, a menos que você tenha memorizado os dados subjacentes e tenha uma imaginação visual bastante doente, caso em que eu me pergunto por que você precisava de um gráfico em primeiro lugar. Para ajudar a maioria de nós no reconhecimento da série, proponho que anexemos o nome da série ao lado direito do gráfico. Adicionar o seguinte à secção de desenho das linhas:

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

o excerto localiza o fim de cada linha e adiciona-lhe um elemento de texto. O texto será impresso como Serie A, Serie B ou Serie C, dependendo da linha. Adicionar o seguinte ao documento css para ajustar as legendas das séries:

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

as etiquetas estão anexadas! Melhor.

cada série tem o seu próprio rótulo

podemos todos concordar que este é um gráfico de linhas bonito! Colei o código completo abaixo. Certifique-se de verificar a segunda parte do tutorial que apresenta dois cenários de adicionar interatividade ao gráfico.

Siga-me no Twitter para mais projetos de visualização de dados.

amostras de código

line_chart.html:

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: