D3.js — это библиотека javascript, используемая для создания интерактивных диаграмм на основе данных. Его можно использовать для создания самых крутых диаграмм. У него очень крутая кривая обучения. Но как только вы поймете основы D3.js, вы сможете заставить самые крутые графики работать на вас. В этом уроке мы собираемся создать линейную диаграмму, отображающую мое использование Интернета в 2020 году. Я собираюсь рассказать вам, как построить простую линейную диаграмму с помощью библиотеки D3.js. В качестве предварительного условия я бы попросил вас пройти первый блог — D3.JS — DEMYSTIFIED в этой серии, если вы еще не знакомы с основами d3.js.

Dataset

Дата выставления счета за использование Интернета01/01/2020001/02/202027701/03/202034301/04/202034201/05/202031001/06/202020801/07/202020901/08/2020220001/09/2012026 /11/202024401/12/2020339

ЧТО ТАКОЕ ЛИНЕЙНЫЙ СЮЖЕТ?

По данным Инвестопедии,

Линейный график – это графическое представление исторической динамики цены актива, которое соединяет ряд точек данных непрерывной линией. Это самый простой тип графика, используемый в финансах, и обычно он отображает только цены закрытия ценной бумаги с течением времени. Линейные графики можно использовать на любом таймфрейме, но чаще всего с ежедневными изменениями цен.

Линейная диаграмма — это ряд точек данных, соединенных линией, которые могут содержать удивительные истории о своих пиках и впадинах. Линейные диаграммы позволяют отображать тенденции, подчеркивать успехи и предупреждать о потенциальных опасностях — и все это за считанные секунды. Это отчасти их привлекательность и отчасти причина, по которой они являются одним из наиболее распространенных способов визуализации данных, которые меняются со временем. Давайте разделим этапы создания линейной диаграммы на разные подэтапы.

ШАГ 1: НАЧАЛО

Прежде всего, мы импортируем библиотеку D3.js прямо из CDN внутри нашего HTML. Я бы попросил вас загрузить последний файл библиотеки d3 и поместить его в папку, где создается index.html. Язык гипертекстовой разметки (HTML) является наиболее распространенным языком, используемым для создания документов во всемирной паутине. HTML использует сотни различных тегов для определения макета веб-страницы. Для большинства тегов требуется открывающий ‹тег› и закрывающий ‹/тег›.

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Sample SuperStore Timeline</title>
    </head>
    <body>
        <div id="wrapper"></div>
        <script src="./../../d3.v5.js"></script>
        <script src="./chart.js"></script>
    </body>
</html>

Давайте теперь перейдем к написанию нашего кода JavaScript.

<!DOCTYPE html>

Тип документа должен определять страницу как документ HTML5.

<html lang="en">

Атрибут HTML lang используется для определения языка текстового содержимого в Интернете. Эта информация помогает поисковым системам возвращать результаты для конкретного языка, а также используется программами чтения с экрана, которые переключают языковые профили для обеспечения правильного акцента и произношения.

HTML <head> Tag

Тег ‹head› в HTML используется для определения части заголовка документа, которая содержит информацию, относящуюся к документу.

HTML <div> Tag

Тег <div> определяет раздел или раздел в документе HTML. Он используется в качестве контейнера для элементов HTML, которые затем стилизуются с помощью CSS или управляются с помощью JavaScript. Тег <div> легко стилизуется с помощью атрибута class или id.

HTML <script> Tag

Тег ‹script› в HTML используется для определения скрипта на стороне клиента. Тег ‹script› содержит инструкции сценария или указывает на внешний файл сценария.

ШАГ 2 — СОЗДАНИЕ СРЕДЫ РАЗРАБОТКИ НА ОСНОВЕ NPM

Прежде чем мы начнем, убедитесь, что у вас правильно установлен NPM. NPM входит в состав установки Node.js. Вы можете скачать Node.js с http://nodejs.org/download/. Выберите правильную бинарную сборку Node.js для вашей ОС. После установки команда npm станет доступна в вашей терминальной консоли. Когда вы печатаете, вы можете увидеть что-то вроде изображения ниже.

https://riptutorial.com/ru/d3-js/пример/2955/установка

Если вы используете NPM, вы можете установить d3 с помощью следующей команды

npm install d3

Затем введите приведенную ниже команду для запуска Live Server.

Live-server --quiet

Если вы используете VScode, вы также можете установить плагин Live-Server по ссылке ниже https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer

Некоторые из вас могут подумать, зачем вообще нужен менеджер пакетов Node для D3.js?

Node.js позволяет писать приложения на JavaScript на сервере. Он построен на среде выполнения JavaScript V8 и написан на C++, поэтому работает быстро. Первоначально он задумывался как серверная среда для приложений, но разработчики начали использовать его для создания инструментов, помогающих им в автоматизации локальных задач. С тех пор развилась совершенно новая экосистема инструментов на основе Node (таких как Grunt, Gulp и webpack), чтобы изменить лицо фронтенд-разработки.

ШАГ 3: ЗАГРУЗИТЕ НАБОР ДАННЫХ

Создайте файл в той же папке и переименуйте его в Chart.js. Как видите, в вашем HTML-файле мы имеем в виду Chart.js в теге Script.

Наша следующая задача — определить функцию и назвать ее drawLineChart(). Мы напишем весь наш код внутри функции. Мы также используем асинхроннуюфункцию, которая указывает, что скрипт выполняется асинхронно. Слово асинхронный перед функцией означает одну простую вещь: функция всегда возвращает обещание. Другие значения автоматически оборачиваются в разрешенное обещание.

async function drawLineChart() {
    // write your code here
}
drawLineChart()

Теперь мы загрузим набор данных с помощью встроенной функции D3. D3 может обрабатывать различные типы данных, определенные либо локально в переменных, либо из внешних файлов. D3 предоставляет следующие методы для загрузки различных типов данных из внешних файлов.

MethodDescriptiond3.json() Функция d3.json() используется для получения файла JSONd3.csv()d3 Функция .csv() используется для загрузки файла csvd3.xml()Метод d3.xml() принимает URL-адрес файла xml и возвращает XML-объект

Весь код для этого проекта можно полностью просмотреть ручкой ниже.

async function drawLineChart() {
  //1. Load your Dataset
  const dataset = await d3.csv("./../../Internet Usage.csv");
  //Check the sample values available in the dataset
  //console.table(dataset[0]);
}
drawLineChart()

await буквально приостанавливает выполнение функции до тех пор, пока промис не установится, а затем возобновляет его с результатом промиса. Это не требует никаких ресурсов ЦП, потому что движок JavaScript тем временем может выполнять другие задачи: выполнять другие скрипты, обрабатывать события и т. д.

Обещание используется для обработки асинхронного результата операции. JavaScript разработан так, чтобы не ждать полного выполнения асинхронного блока кода, прежде чем другие синхронные части кода смогут выполняться. Например, при отправке API-запросов к серверам мы не знаем, находятся ли эти серверы в автономном режиме или в сети, или сколько времени требуется для обработки запроса к серверу.

С помощью промисов мы можем отложить выполнение блока кода до тех пор, пока не будет выполнен асинхронный запрос. Таким образом, другие операции могут выполняться без перерыва.

Пожалуйста, обратитесь к приведенной ниже ссылке, в которой подробно рассказывается об await и Async
https://medium.com/jspoint/javascript-promises-and-async-await-as-fast-as-possible-d7c8c8ff0abc

если вы хотите проверить, правильно ли загружены данные, используйте метод Console.log(), который используется для регистрации текущего значения, содержащегося внутри определенной переменной. . Console.log() является частью глобального объекта window в веб-браузере. It also печатает элемент в HTML-подобном дереве. Давайте запустим эту команду и посмотрим сами.

ШАГ 4: ОПРЕДЕЛИТЕ ЗНАЧЕНИЕ X И Y

На нашей линейной диаграмме ось Y (вертикальная) слева состоит из значений Использование Интернета, а ось X (горизонтальная) внизу состоит из Даты счета. Чтобы получить правильные метрики из каждой точки данных, нам понадобятся функции доступа. Функции доступа преобразуют одну точку данных в значение метрики.

const yAccessor = (d) => d.InternetUsage;
  const dateParser = d3.timeParse("%d/%m/%Y");
  const xAccessor = (d) => dateParser(d["Bill Date"]);
  // Check the value of xAccessor function now
  // console.log(xAccessor(dataset[0]));

Примечание. В отличие от синтаксических анализаторов даты на "естественном языке" (включая встроенный синтаксический анализ JavaScript), этот метод является строгим. Если указанная строка не совсем соответствует соответствующему описателю формата, этот метод возвращает значение null. Например, если связанный формат представляет собой полную строку ISO 8601 «%Y-%m-%dT%H:%M:%SZ», то строка «2011–07–01T19:15:28Z» будет проанализирована правильно. , но «2011–07–01T19:15:28», «2011–07–01 19:15:28» и «2011–07–01» вернут значение null, несмотря на то, что они действительны для 8601 даты.

console.log(xAccessor(dataset[0]));

ШАГ 5: СОЗДАЙТЕ КОНТЕЙНЕРЫ ГРАФИК

При рисовании диаграммы есть два контейнера, размеры которых нам нужно определить: оболочка и границы.

Оболочка содержит всю диаграмму: элементы данных, оси, метки и т. д. Здесь будет содержаться каждый элемент SVG.

Границы содержат все наши элементы данных: в данном случае нашу строку.

Это различие поможет нам выделить пространство, необходимое для посторонних элементов (осей, меток), и позволит нам сосредоточиться на нашей основной задаче: построении наших данных. Одна из причин, по которой это так важно определить заранее, — непоследовательный и незнакомый способ определения размера элементов SVG».

Отрывок: Нейт Мюррей. «Полная визуализация данных с помощью D3»

// 2. Create a chart dimension by defining the size of the Wrapper and Margin
  let dimensions = {
    width: window.innerWidth * 0.6,
    height: 600,
    margin: {
      top: 115,
      right: 20,
      bottom: 40,
      left: 60,
    },
  };
  dimensions.boundedWidth =
    dimensions.width - dimensions.margin.left - dimensions.margin.right;
  dimensions.boundedHeight =
    dimensions.height - dimensions.margin.top - dimensions.margin.bottom;
// 3. Draw Canvas
  const wrapper = d3
    .select("#wrapper")
    .append("svg")
    .attr("width", dimensions.width)
    .attr("height", dimensions.height);
  //Log our new Wrapper Variable to the console to see what it looks like
  //console.log(wrapper);

Здесь мы выберем WrapperID, который мы передали в нашем файле Index.html. Как мы знаем, мы можем выбрать все элементы с идентификатором (#id)

ШАГ 6: СОЗДАТЬ ОГРАНИЧИВАЮЩИЙ КОНТЕЙНЕР?

Нам часто может понадобиться группировать элементы вместе, что позволяет нам применять преобразования или устанавливать атрибуты, которые наследуются всеми дочерними элементами этой группы. Один из способов добиться этого — использовать контейнер SVG-элемента ‹g›, что позволяет нам организовать наши элементы в группы. Затем мы добавим стиль с функцией transform.translate(), чтобы получить преобразование, перевод которого tx1 и ty1 равен tx0 + tk и ty0 + tk, где tx относится к перемещению по оси x и ty относится к перемещению по оси Y.

// 4. Create a Bounding Box
  const bounds = wrapper
    .append("g")
    .style("transform",
      `translate(${dimensions.margin.left}px,${dimensions.margin.top}px)`
    );

Если мы посмотрим на нашу панель «Элементы» в нашем браузере, мы увидим наш новый элемент со стилем ‹g›. Мы видим, что размер элемента составляет 0 пикселей на 0 пикселей — вместо того, чтобы принимать атрибут ширины или высоты, элемент будет расширяться, чтобы соответствовать своему содержимому. Когда мы начнем рисовать нашу диаграмму, мы увидим это в действии.

ШАГ 7: НАСТРОЙКА ОБЛАСТЕЙ И ДИАПАЗОНОВ ВЕСОВ

Чтобы отобразить максимальные значения использования Интернета в правильном месте, нам нужно преобразовать их в пространство пикселей. Итак, идея масштабирования состоит в том, чтобы взять имеющиеся у нас значения данных и поместить их в доступное пространство. В двух словах, цель состоит в том, чтобы убедиться, что данные, которые мы получаем, правильно вписываются в наш график. Поскольку у нас есть два разных типа данных (дата и числовые значения), их нужно обрабатывать отдельно. Чтобы правильно изучить всю эту концепцию масштабов, доменов и диапазонов, обратитесь к моему первому блогу D3.js -demistified
Прежде чем перейти к следующему разделу, я настоятельно рекомендую вам прочитать это (и многие другие замечательные работы Фила), поскольку он действительно точно описывает описание
https://observablehq.com/@d3/introduction-to-d3s-scales

Чтобы создать новую шкалу, нам нужно создать экземпляр конструкции d3.scaleLinear(). d3.scaleLinear создает шкалу с линейной зависимостью между входом и выходом. Затем мы будем использовать функцию .domain, чтобы сообщить D3, каков будет объем данных, а затем передать их функции масштабирования.

Модуль d3-array также имеет метод d3.extent() для захвата этих чисел. d3.extent() принимает два параметра:

  • массив точек данных
  • функция доступа, которая по умолчанию является функцией идентификации (d => d)
// 5. Define Domain and Range for Scales
  const yScale = d3
    .scaleLinear()
    .domain(d3.extent(dataset, yAccessor))
    .range([dimensions.boundedHeight, 0]);

Здесь функция .extent находит максимальное и минимальное значения в массиве, а затем функция .domain возвращает эти максимальное и минимальное значения до D3 в качестве диапазона для оси x.

.range([dimensions.boundedHeight, 0])

Y-значения SVG отсчитываются сверху вниз, и в этом случае максимальное и минимальное количество пикселей, на которых наша точка будет располагаться по оси x.

ШАГ 8: СОЗДАЙТЕ ЭТАЛОННУЮ ПОЛОСУ

Создайте эталонную полосу, которая будет действовать как порог для наших данных. Я держу 100 ГБ в качестве цели. Давайте визуализируем этот порог, добавив прямоугольник. Нам просто нужно дать ему четыре атрибута: x, y, ширина и высота.

// console.log(yScale(100));
  const referenceBandPlacement = yScale(100);
  const referenceBand = bounds
    .append("rect")
    .attr("x", 0)
    .attr("width", dimensions.boundedWidth)
    .attr("y", referenceBandPlacement)
    .attr("height", dimensions.boundedHeight - referenceBandPlacement)
    .attr("fill", "#ffece6");

Точно так же создайте шкалы для оси X, как мы сделали для оси Y.

const xScale = d3
    .scaleTime()
    .domain(d3.extent(dataset, xAccessor))
    .range([0, dimensions.boundedWidth]);

ШАГ 9: ПРЕОБРАЗОВАТЬ ТОЧКУ ДАННЫХ В ЗНАЧЕНИЕ X И Y

Метод d3.line() используется для создания нового генератора строк с настройками по умолчанию. Генератор линий затем используется для создания линии. Метод d3.line() создаст генератор, который преобразует точки данных в строку d. Это преобразует наши точки данных как с помощью функции доступа, так и с масштабом, чтобы получить масштабированное значение в пространстве пикселей.

//6. Convert a datapoints into X and Y value
  const lineGenerator = d3
    .line()
    .x((d) => xScale(xAccessor(d)))
    .y((d) => yScale(yAccessor(d)))
    .curve(d3.curveBasis);

CurveBasis() генерирует кривую на линии на основе кубических сплайнов. Вы можете опустить последнюю строку, если не хотите, чтобы график был изогнутым.

ШАГ 10: ПРЕОБРАЗОВАТЬ X И Y В ПУТЬ

Этот метод используется для создания нового пути на основе точек данных X и Y. d3.path возвращает объект, который реализует те же методы пути, что и контекст Canvas 2D, но сериализует их в данные пути SVG.

// 7. Convert X and Y into Path
  const line = bounds
    .append("path")
    .attr("d", lineGenerator(dataset))

.attr("fill", "none")
    .attr("stroke", "Red")
    .attr("stroke-width", 2);

ШАГ 11: СОЗДАЙТЕ ОСИ X И Y

Оси можно рисовать с помощью встроенных функций D3. Он состоит из линий, делений и меток. Функция d3.axisLeft() в D3.js используется для создания левой вертикальной оси. Эта функция создаст новый генератор оси, ориентированный влево, для заданного масштаба с пустыми аргументами деления, размером деления 6 и отступом 3.

Когда мы вызовем наш генератор осей, он создаст множество элементов, поэтому лучше создать элемент g, чтобы содержать все эти элементы и поддерживать порядок в нашем DOM.

//8. Create X axis and Y axis
  // Generate Y Axis
  const yAxisGenerator = d3.axisLeft().scale(yScale);
  const yAxis = bounds.append("g").call(yAxisGenerator);
  // Generate X Axis
  const xAxisGenerator = d3.axisBottom().scale(xScale);
  const xAxis = bounds
    .append("g")
    .call(xAxisGenerator);

Почему d3.axisBottom() не нарисовал ось в нужном месте? Функции генератора осей d3 знают, где размещать деления и метки делений относительно линии оси, но они понятия не имеют, где разместить саму ось.

Чтобы переместить ось X вниз, мы можем сдвинуть группу оси X, подобно тому, как мы сдвинули границы диаграммы с помощью CSS-преобразования.

Отрывок: Нейт Мюррей. «Полная визуализация данных с помощью D3».

Есть еще две проблемы, связанные с осью X.

  • Точка данных показывала «2020» вместо «Январь».
  • Точка данных, соответствующая «декабрю», показывает усеченную версию декабря.

давайте исправим этот код XAxisGenerator,

// Generate X Axis
  const xAxisGenerator = d3.axisBottom().scale(xScale);
  const xAxis = bounds
    .append("g")
    .call(xAxisGenerator.tickFormat(d3.timeFormat("%b,%y")))
    .style("transform", `translateY(${dimensions.boundedHeight}px)`);

ШАГ 12: ДОБАВЬТЕ НАЗВАНИЕ ГРАФИКА

Заголовок диаграммы — это первое, что видит пользователь, когда читает вашу визуализацию данных, поэтому его должно быть легко понять. Не используйте просто числа, используйте слова для описания этих чисел.

wrapper
    .append("g")
    .style("transform", `translate(${50}px,${15}px)`)
    .append("text")
    .attr("class", "title")
    .attr("x", dimensions.width / 2)
    .attr("y", dimensions.margin.top / 2)
    .attr("text-anchor", "middle")
    .text("My 2020 Internet Usage(in GB) ")
    .style("font-size", "36px")
    .style("text-decoration", "underline");

ВЫВОД:

Миссия по объяснению линейной диаграммы завершена. Поздравляем! Мы успешно создали линейную диаграмму с помощью D3.js. Из простой цели создания линейной диаграммы мы изучили многие основные концепции D3:

  • Выборы
  • Напольные весы
  • Оси
  • Трансформация

Конечно, в D3 есть намного больше: манипулирование данными, генераторы компоновки, сложные переходы. Но, надеюсь, это руководство пробудило в вас желание двигаться дальше.
В следующем сообщении в блоге мы изучим взаимодействие и добавим всплывающую подсказку к этой линейной диаграмме.

Вот полный код, который я использовал.

async function drawLineChart() {
  //1. Load your Dataset
  const dataset = await d3.csv("./../../Internet Usage.csv");
  //Check the sample values available in the dataset
  //console.table(dataset[0]);
  const yAccessor = (d) => d.InternetUsage;
  const dateParser = d3.timeParse("%d/%m/%Y");
  const xAccessor = (d) => dateParser(d["Bill Date"]);
  //Check the value of xAccessor function now
  //console.log(xAccessor(dataset[0]));
  // 2. Create a chart dimension by defining the size of the Wrapper and Margin
  let dimensions = {
    width: window.innerWidth * 0.6,
    height: 600,
    margin: {
      top: 115,
      right: 20,
      bottom: 40,
      left: 60,
    },
  };
  dimensions.boundedWidth =
    dimensions.width - dimensions.margin.left - dimensions.margin.right;
  dimensions.boundedHeight =
    dimensions.height - dimensions.margin.top - dimensions.margin.bottom;
  // 3. Draw Canvas
  const wrapper = d3
    .select("#wrapper")
    .append("svg")
    .attr("width", dimensions.width)
    .attr("height", dimensions.height);
  //Log our new Wrapper Variable to the console to see what it looks like
  //console.log(wrapper);
  // 4. Create a Bounding Box
  const bounds = wrapper
    .append("g")
    .style(
      "transform",
      `translate(${dimensions.margin.left}px,${dimensions.margin.top}px)`
    );
  // 5. Define Domain and Range for Scales
  const yScale = d3
    .scaleLinear()
    .domain(d3.extent(dataset, yAccessor))
    .range([dimensions.boundedHeight, 0]);
  // console.log(yScale(100));
  const referenceBandPlacement = yScale(100);
  const referenceBand = bounds
    .append("rect")
    .attr("x", 0)
    .attr("width", dimensions.boundedWidth)
    .attr("y", referenceBandPlacement)
    .attr("height", dimensions.boundedHeight - referenceBandPlacement)
    .attr("fill", "#ffece6");
  const xScale = d3
    .scaleTime()
    .domain(d3.extent(dataset, xAccessor))
    .range([0, dimensions.boundedWidth]);
  //6. Convert a datapoints into X and Y value
  const lineGenerator = d3
    .line()
    .x((d) => xScale(xAccessor(d)))
    .y((d) => yScale(yAccessor(d)))
    .curve(d3.curveBasis);
  // 7. Convert X and Y into Path
  const line = bounds
    .append("path")
    .attr("d", lineGenerator(dataset))
    .attr("fill", "none")
    .attr("stroke", "Red")
    .attr("stroke-width", 2);
  //8. Create X axis and Y axis
  // Generate Y Axis
  const yAxisGenerator = d3.axisLeft().scale(yScale);
  const yAxis = bounds.append("g").call(yAxisGenerator);
  // Generate X Axis
  const xAxisGenerator = d3.axisBottom().scale(xScale);
  const xAxis = bounds
    .append("g")
    .call(xAxisGenerator.tickFormat(d3.timeFormat("%b,%y")))
    .style("transform", `translateY(${dimensions.boundedHeight}px)`);
  //9. Add a Chart Header
  wrapper
    .append("g")
    .style("transform", `translate(${50}px,${15}px)`)
    .append("text")
    .attr("class", "title")
    .attr("x", dimensions.width / 2)
    .attr("y", dimensions.margin.top / 2)
    .attr("text-anchor", "middle")
    .text("My 2020 Internet Usage(in GB) ")
    .style("font-size", "36px")
    .style("text-decoration", "underline");
}
drawLineChart();

Если вам интересно узнать больше о D3.js, обязательно ознакомьтесь с курсом Fullstack D3 от Амелии Ваттенбергер.

Мы рассмотрели здесь много вопросов, но основные принципы относительно просты. В следующем сообщении в блоге я расскажу, как добавить всплывающую подсказку к этой простой линейной диаграмме. Как только вы преодолеете первоначальный дискомфорт от изучения основ D3, вы сможете выбирать из тысяч типов диаграмм в D3 из галерей примеров D3 и изменять их в соответствии со своими потребностями.

Мы постарались охватить как можно больше, чтобы новичок мог начать работу с D3.js. Надеюсь, вам понравится. Как всегда, мы приветствуем обратную связь и конструктивную критику. Если вам понравился этот блог, мы будем рады, если вы нажмете кнопку Поделиться, чтобы другие могли наткнуться на него. Пожалуйста, нажмите кнопку подписки, если вы хотите, чтобы вас добавляли в мой еженедельный список рассылки, и не забудьте подписаться на Vizartpandey в Instagram!