Статьи

Как создать интерактивные диаграммы JavaScript из пользовательских наборов данных

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

В моей предыдущей статье — Начало работы с AnyChart: 10 практических примеров — я представил библиотеку AnyChart и продемонстрировал, как она отлично подходит для ваших задач визуализации данных. Сегодня я хочу немного углубиться и взглянуть на функции сопоставления данных AnyChart, которые позволяют создавать красивые графики из пользовательских наборов данных с минимальными усилиями.

Я также хочу рассмотреть множество способов настройки AnyChart в соответствии с вашими требованиями, а также способы изменения внешнего вида диаграмм AnyChart с помощью тем. В настоящее время на выбор доступно 17 готовых тем или вы можете создать свою собственную. И если у вас нет лучшего взгляда на дизайн, почему бы не купить нашу книгу, чтобы поднять ногу.

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

Отображение данных в AnyChart

Чтобы упростить интеграцию пользовательских источников данных в приложения для построения диаграмм, AnyChart имеет специальные объекты, называемые наборами данных . Эти объекты действуют как промежуточные контейнеры для данных. Когда данные хранятся в наборах данных, AnyChart может отслеживать изменения, анализировать их и работать с этими данными более надежным и эффективным способом. Вкратце: интерактивные JavaScript-диаграммы никогда не были проще!

Независимо от того, есть ли у вас массив объектов, массив массивов или файл .csv

  • обеспечить полный и явный контроль над созданной серией
  • определить, какой столбец является аргументом (ось X)
  • определить, какие столбцы содержат значения для какой серии
  • фильтровать данные
  • сортировать данные

Основы отображения данных

Лучший способ узнать, как работает сопоставление данных в AnyChart, — взглянуть на пример. Давайте представим массив со следующим набором пользовательских данных:

 var rawData = [
  ["A", 5, 4, 5, 8, 1, "bad"],
  ["B", 7, 1, 7, 9, 2, "good"],
  ["C", 9, 3, 5, 4, 3, "normal"],
  ["D", 1, 4, 9, 2, 4, "bad"]
];

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

 var rawData = [
  ["A", 5, 4, 5, 8, 1, "bad"],
  ["B", 7, 1, 7, 9, 2, "good"],
  ["C", 9, 3, 5, 4, 3, "normal"],
  ["D", 1, 4, 9, 2, 4, "bad"]
];

var dataSet = anychart.data.set(rawData);

И затем, как только данные были загружены в набор данных, начинается настоящее волшебство: теперь вы можете создавать так называемые представления . Это наборы данных, полученные из других наборов данных.

 var rawData = [
  ["A", 5, 4, 5, 8, 1, "bad"],
  ["B", 7, 1, 7, 9, 2, "good"],
  ["C", 9, 3, 5, 4, 3, "normal"],
  ["D", 1, 4, 9, 2, 4, "bad"]
];

var dataSet = anychart.data.set(rawData);

var view1 = dataSet.mapAs({x: 0, value: 1});
var view2 = dataSet.mapAs({x: 0, value: 2});
var view3 = dataSet.mapAs({x: 0, high: 3, low: 4});
var view4 = dataSet.mapAs({x: 0, value: 5, meta: 6});

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

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

 anychart.onDocumentLoad(function() {
  var rawData = [
    ["A", 5, 4, 5, 8, 3, "Bad"],
    ["B", 7, 1, 7, 9, 5, "Good"],
    ["C", 9, 3, 5, 4, 4, "Normal"],
    ["D", 1, 4, 9, 2, 3, "Bad"]
  ];

  var dataSet = anychart.data.set(rawData);
  var view4 = dataSet.mapAs({x: 0, value: 5, meta: 6});

  // create chart
  var chart = anychart.pie(view4);
  chart.title("AnyChart: Pie Chart from Custom Data Set");
  chart.labels().format("{%meta}: {%Value}");
  chart.container("container").draw();
});

И это то, что мы в конечном итоге:

Примечание . Вы можете найти все демонстрации в этой статье в виде коллекции CodePen .

Многосерийная комбинированная диаграмма с настраиваемым набором данных

Теперь давайте посмотрим, как мы можем использовать одни и те же пользовательские данные для создания комбинированной диаграммы с диаграммами области линий и областей на одном графике. Этот раздел будет очень коротким, так как теперь вы знаете, что это за взгляды. Все, что вам нужно сделать, это выбрать правильные представления и явно создать необходимые серии:

 anychart.onDocumentLoad(function() {
  var rawData = [
    ["A", 5, 4, 5, 8, 3, "Bad"],
    ["B", 7, 1, 7, 9, 5, "Good"],
    ["C", 9, 3, 5, 4, 4, "Normal"],
    ["D", 1, 4, 9, 2, 3, "Bad"]
  ];

  var dataSet = anychart.data.set(rawData);

  var view1 = dataSet.mapAs({x: 0, value: 1});
  var view2 = dataSet.mapAs({x: 0, value: 2});
  var view3 = dataSet.mapAs({x: 0, high: 3, low: 4});

  // create chart
  var chart = anychart.line();
  // create two line series
  chart.line(view1).name("EUR");
  chart.line(view2).name("USD");
  // create range area series
  chart.line(view2).name("Trend");

  // set title and draw chart
  chart.title("AnyChart: Combined Chart from Data Set");
  chart.container("container").draw();
});

Вот как это выглядит:

Потоковая передача данных и фильтрация

А теперь, чтобы продемонстрировать красоту видов и наборов данных. Для этого мы создадим столбчатую диаграмму и многострочную диаграмму, данные потокового вещания в набор данных и примем только определенные значения в столбчатую диаграмму. Звучит сложно? Это не совсем так!

 anychart.onDocumentLoad(function() {
  var rawData = [
    ["A", 5, 4, 2, 6, 3, "Bad"],
    ["B", 7, 2, 1, 9, 5, "Good"],
    ["C", 8, 3, 2, 9, 4, "Normal"],
    ["D", 1, 4, 1, 4, 3, "Bad"]
  ];

  dataSet = anychart.data.set(rawData);

  var view1 = dataSet.mapAs({ x: 0, value: 1 });
  var view2 = dataSet.mapAs({ x: 0, value: 2 });
  var view3 = dataSet.mapAs({ x: 0, value: 3 });
  var view4 = dataSet.mapAs({ x: 0, value: 4 });
  var view5 = dataSet.mapAs({ x: 0, value: 5 });

  // create chart
  var chart1 = anychart.line();
  // create several line series
  chart1.line(view1).name("EUR");
  chart1.line(view2).name("USD");
  chart1.line(view3).name("YEN");
  chart1.line(view4).name("CNY");

  // create column chart
  // based on filtered view
  // that accepts values of less than 5 only
  var chart2 = anychart.column(
    view5.filter("value", function(v) {
      return v < 5;
    })
  );

  // set title and draw multi-line chart
  chart1.title("Line: Streaming from Data Set");
  chart1.legend(true);
  chart1.container("lineContainer").draw();

  // set title and draw column chart
  chart2.title("Column: Filtering Stream");
  chart2.container("columnContainer").draw();
});

// streaming function
var streamId;
function stream() {
  if (streamId === undefined) {
    streamId = setInterval(function() {
      addValue();
    }, 1000);
  } else {
    clearInterval(streamId);
    streamId = undefined;
  }
}

// function to add new value and remove first one
function addValue() {
  // generate next letter/symbol as argument
  var x = String.fromCharCode(
    dataSet.row(dataSet.getRowsCount() - 1)[0].charCodeAt(0) + 1
  );
  // append row of random values to data set
  dataSet.append([
    x,
    Math.random() * 10,
    Math.random() * 10,
    Math.random() * 10,
    Math.random() * 10,
    Math.random() * 10
  ]);
  // remove first row
  dataSet.remove(0);
}

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

Это всего лишь верхушка айсберга в отношении отображения данных! Вы можете сделать то же самое с массивами объектов и данными CSV, и вы можете сортировать , искать , прослушивать изменения, просматривать значения , а также изменять существующие строки . Существуют также специальные представления для иерархических данных, используемых в древовидных картах и ​​диаграммах Ганта, и такие представления можно искать и просматривать .

Настройка визуализации диаграммы

AnyChart чрезвычайно универсален, когда дело доходит до настройки диаграммы. Вы можете изменять стили линий, заливать цвета, использовать градиентную заливку или заливку изображения практически для любого элемента. Цвета могут быть установлены с использованием строковых констант, HEX или RGB-обозначений или функции, которая возвращает одно из этих значений. Библиотека также предлагает ряд встроенных шаблонов, а также возможность создавать собственные шаблоны.

Темы

Самый простой способ изменить внешний вид диаграмм AnyChart — это изменить тему.

Фактически, вы можете создать свою собственную тему или использовать одну из тех, которые уже поставляются с библиотекой. В настоящее время в AnyChart доступно 17 готовых тем: кофе , темно-синий , темная земля , темный гламур , темный прованс , темная бирюза , тема по умолчанию , светло-голубой , светло-земля , светлый гламур , светлый прованс , светло-бирюзовый , Монохромный , утренний , пастельный , морской , винный . Файлы для этих тем можно получить в разделе Темы на AnyChart CDN .

Вы можете ссылаться на них так:

 <script src="https://cdn.anychart.com/js/latest/anychart-bundle.min.js"></script>
<script src="https://cdn.anychart.com/themes/latest/coffee.min.js"></script>

И активировать тему всего одной строкой:

 anychart.theme(anychart.themes.coffee);

Давайте в качестве примера рассмотрим базовую диаграмму из предыдущей статьи .

 anychart.onDocumentLoad(function() {
  // set theme referenced in scripts section from
  // https://cdn.anychart.com/themes/latest/coffee.min.js
  anychart.theme(anychart.themes.coffee);
  // create chart and set data
  var chart = anychart.column([
    ["Winter", 2],
    ["Spring", 7],
    ["Summer", 6],
    ["Fall", 10]
  ]);
  // set chart title
  chart.title("AnyChart Coffee Theme");
  // set chart container and draw
  chart.container("container").draw();
});

Теперь внешний вид этой диаграммы совершенно другой.

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

раскраска

Как вы видели, цветовые элементы в библиотеке диаграмм AnyChart довольно тривиальны. Но это еще не все! Также возможно окрашивать фигуры в сплошные цвета с непрозрачностью, использовать радиальные и линейные градиенты и делать линии пунктирными. Вы можете применять цвета по именам их веб-констант, HEX-кодам или значениям RGB, RGBA, HSL, HSLA — так же, как в CSS.

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

 anychart.onDocumentReady(function() {

  // create Pareto chart
  var chart = anychart.pareto([
    {x: "Defect 1", value: 19},
    {x: "Defect 2", value: 9},
    {x: "Defect 3", value: 28},
    {x: "Defect 4", value: 87},
    {x: "Defect 5", value: 14},
  ]);

  // set chart title
  chart.title("Pareto Chart: Conditional coloring");

  // set container id and draw
  chart.container("container").draw();
});

Но скажем, мы хотим выделить элементы, которые имеют относительную частоту менее 10%. Все, что нам нужно сделать в этом случае, это добавить некоторые функции раскраски:

 // Get Pareto column series
// and configure fill and stroke
var column = chart.getSeriesAt(0);
column.fill(function () {
  if (this.rf < 10) {
    return '#E24B26 0.5'
  } else {
    return this.sourceColor;
  }
});
column.stroke(function () {
  if (this.rf < 10) {
    return {color: anychart.color.darken('#E24B26'), dash:"5 5"};
  } else {
    return this.sourceColor;
  }
});

Как показано в примере, мы можем получить доступ к контексту функции окраски с помощью ключевого слова this Мы используем простую строку HEX с непрозрачностью для цвета: '#E24B26 0.5' Для линии мы вычисляем более темный цвет, используя специальную функцию преобразования цвета AnyChart, а также устанавливаем соответствующий параметр тире: {color: anychart.color.darken('#E24B26'), dash:"5 5"}

Вот что мы получаем в итоге:

Заливка по умолчанию

JavaScript-библиотека AnyChart JavaScript также предоставляет очень гибкий способ работы с заливками шаблонов (штриховок). Для начала у вас есть 32 типа заливки . Они могут быть организованы в ваши собственные пользовательские поддоны, и вы можете напрямую установить, какой из них использовать для определенной серии, элемента и т. Д. Вот как может выглядеть код для монохромной круговой диаграммы:

 anychart.onDocumentReady(function() {
  // create a Pie chart and set data
  chart = anychart.pie([
    ["Apple", 2],
    ["Banana", 2],
    ["Orange", 2],
    ["Grape", 2],
    ["Pineapple", 2],
    ["Strawberry", 2],
    ["Pear", 2],
    ["Peach", 2]
  ]);

  // configure chart using chaining calls to shorten sample
  chart.hatchFill(true).fill("white").stroke("Black");
  chart
    .labels()
    .background()
    .enabled(true)
    .fill("Black 1")
    .cornerType("Round")
    .corners("10%");

  // draw a chart
  chart.container("container").draw();
});

Единственное, что мы сделали для включения заполнения шаблона, — это chart.hatchFill(true)

Заполнение пользовательских шаблонов

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

Мы будем создавать шаблоны, используя один старый шрифт, который раньше был довольно популярен среди геологов — Interdex. Я помню, как один из клиентов AnyChart спросил нас, как использовать это в своем проекте, и он был очень рад узнать, насколько это просто.

Во-первых, если вы хотите использовать шрифт, которого нет в вашей системе, вам нужно создать веб-шрифт и правильно ссылаться на него в файле CSS. Образец такого файла для нашего шрифта Interdex можно найти в AnyChart CDN . Нам нужно сослаться на него вместе с используемой библиотекой управления шрифтами и библиотекой диаграмм AnyChart:

 <link rel="stylesheet" type="text/css" href="https://cdn.anychart.com/fonts/interdex/interdex.css"/>

<script src="https://cdn.anychart.com/js/latest/anychart-bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fontfaceobserver/2.0.7/fontfaceobserver.js"></script>

После этого мы можем приступить к кодированию:

 var fontLoad = new FontFaceObserver("Conv_interdex");

// create chart when font is loaded using
// https://cdnjs.cloudflare.com/ajax/libs/fontfaceobserver/2.0.7/fontfaceobserver.js
fontLoad.load().then(function() {
  anychart.onDocumentReady(function() {
    // enable hatch fill by default and make chart monochrome
    anychart.theme({
      chart: {
        defaultSeriesSettings: {
          base: {
            hatchFill: true,
            fill: "White",
            hoverFill: "White",
            stroke: "Black"
          }
        }
      }
    });

    // create a stage, it is needed to create a custom pattern fill
    stage = anychart.graphics.create("container");

    // create a column chart and set title
    chart = anychart.column();
    chart.title("AnyChart Custom Pattern");
    // set the data
    chart.data([
      ["Jan", 1000, 1200, 1500, 1000, 1200, 1500],
      ["Feb", 1200, 1500, 1600, 1200, 1500, 1600],
      ["Mar", 1800, 1600, 1700, 1800, 1600, 1700],
      ["Apr", 1100, 1300, 1600, 1100, 1300, 1600],
      ["May", 1900, 1900, 1500, 1900, 1900, 1500]
    ]);

    // set custom hatch palette
    // it can be populated with any number of patterns
    chart.hatchFillPalette([
      getPattern("A"),
      getPattern("N"),
      getPattern("Y"),
      getPattern("C")
    ]);

    // set container and draw chart
    chart.container(stage).draw();
  });
});

// function to create patterns
function getPattern(letter) {
  var size = 40;
  // create a text object
  var text = anychart.graphics
    .text()
    .htmlText(
      "<span " +
        "style='font-family:Conv_interdex;font-size:" +
        size +
        ";'>" +
        letter +
        "</span>"
    );
  // create a pattern object
  var pattern = stage.pattern(text.getBounds());
  //  add text to a pattern
  pattern.addChild(text);
  return pattern;
}

Я включил все пояснения в комментарии, но просто подытожу — вы можете создавать собственные шаблоны, размещать их в пользовательских палитрах шаблонов, которые затем можно применять ко всей диаграмме. Если вы обратите внимание, вы заметите, что мы использовали механизм тем AnyChart для установки значений по умолчанию.

Вот конечный результат. Прекрасно, я уверен, что вы согласитесь …

Custom Series

И последний пример в этой статье для тех, кто хочет еще большей гибкости и настройки. В то время как AnyChart стремится обеспечить постоянно растущее число типов диаграмм из коробки, визуализация данных — это огромное поле, и у каждого проекта есть свои требования. Чтобы сделать (и сохранить) всех как можно более счастливыми и предоставить способ создавать визуализации по своему выбору, AnyChart открыла библиотеку JavaScript-графики GraphicsJS с открытым исходным кодом, а также открыла исходный код самой библиотеки JavaScript-графики AnyChart .

Но если вы действительно, действительно хотите использовать 100% собственное рисование, вам придется проделать большую работу, если вы решите раскошелиться на исходный код. Например, настройка может быть проблемой, и у вас могут возникнуть проблемы с объединением будущих версий, если AnyChart не примет ваш запрос на извлечение.

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

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

 anychart.onDocumentReady(function() {
  // create a chart
  var chart = anychart.cartesian();
  chart.xAxis().title("Month");
  chart.yAxis().title("Cherry price");

  // create a data set
  var data = [
    { x: "Apr", low: 29, high: 37 },
    { x: "May" },
    { x: "Jun", low: 29, high: 47 },
    { x: "Jul", low: 12, high: 27 },
    { x: "Aug", low: 20, high: 33, color: "#ff0000" },
    { x: "Sep", low: 35, high: 44 },
    { x: "Oct", low: 20, high: 31 },
    { x: "Nov", low: 44, high: 51 }
  ];

  // create a range column series
  var series = chart.rangeColumn(data);
  // set a meta field to use as a cherry size
  // series.meta("cherry", 50);
  // set a meta field to use as a stem thickness
  // series.meta("stem", 1);

  // optional: configurable select fill
  series.selectFill("white");

  // call a custom function that changes series rendering
  cherryChartRendering(series);

  // set container id for the chart and initiate chart drawing
  chart.container("container").draw();
});

// custom function to change range column series rendering to
// cherry chart with a special value line markers
function cherryChartRendering(series) {
  // cherry fill color
  series.fill(function() {
    // check if color is set for the point and use it or series color
    color = this.iterator.get("color") || this.sourceColor;
    return anychart.color.lighten(color, 0.25);
  });
  // cherry stroke color
  series.stroke(function() {
    // check if color is set for the point and use it or series color
    color = this.iterator.get("color") || this.sourceColor;
    return anychart.color.darken(color, 0.1);
  });

  // set rendering settings
  series.rendering()// set point function to drawing
  .point(drawer);
}

// custom drawer function to draw a cherry chart
function drawer() {
  // if value is missing - skip drawing
  if (this.missing) return;

  // get cherry size or set default
  var cherry = this.series.meta("cherry") || this.categoryWidth / 15;
  // get stem thickness or set default
  var stem = this.series.meta("stem") || this.categoryWidth / 50;

  // get shapes group
  var shapes = this.shapes || this.getShapesGroup(this.pointState);
  // calculate the left value of the x-axis
  var leftX = this.x - stem / 2;
  // calculate the right value of the x-axis
  var rightX = leftX + stem / 2;

  shapes["path"]
    // resets all 'path' operations
    .clear()
    // draw bulb
    .moveTo(leftX, this.low - cherry)
    .lineTo(leftX, this.high)
    .lineTo(rightX, this.high)
    .lineTo(rightX, this.low - cherry)
    .arcToByEndPoint(leftX, this.low - cherry, cherry, cherry, true, true)
    // close by connecting the last point with the first
    .close();
}

Вот что мы получаем в итоге:

Вывод

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

Если вы еще не пробовали AnyChart, я призываю вас попробовать. В нашем последнем выпуске (версия 7.14.0) добавлено восемь новых типов диаграмм, пять новых технических индикаторов, загрузчик данных Google Spreadsheet, выбор и масштабирование области выделения, перенос текста и другие интересные новые функции.

И если вы используете AnyChart в своих проектах и ​​у вас есть какие-либо комментарии или вопросы относительно библиотеки, я хотел бы услышать это в комментариях ниже.