Документация Engee

Пайплайн обработки

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

Здесь вы узнаете о действиях, которые происходят после вызова plot() или plot!(), а также о доступных при этом возможностях и гибкости.

Пример команды

Предположим, у нас есть следующие данные:

n = 100
x, y = range(0, 1, length = n), randn(n, 3)

и мы хотели бы визуализировать x по каждому столбцу y. Вот пример команды в Plots:

using Plots; pythonplot(size = (400, 300))
plot(
    x, y,
    line = (0.5, [4 1 0], [:path :scatter :histogram]),
    normalize = true,
    bins = 30,
    marker = (10, 0.5, [:none :+ :none]),
    color = [:steelblue :orangered :green],
    fill = 0.5,
    orientation = [:v :v :h],
    title = "My title",
)
pipeline1

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

Если проводить сравнение, это несколько похоже на следующие вызовы в PythonPlot:

import PythonPlot
fig = PythonPlot.gcf()
fig.set_size_inches(4, 3, forward = true)
fig.set_dpi(100)
PythonPlot.clf()

n = 100
x, y = range(0, 1, length = n), randn(n, 3)

PythonPlot.plot(x, y[:,1], alpha = 0.5, "steelblue", linewidth = 4)
PythonPlot.scatter(x, y[:,2], alpha = 0.5, marker = "+", s = 100, c="orangered")
PythonPlot.hist(
    y[:,3],
    orientation = "horizontal",
    alpha = 0.5,
    density = true,
    bins=30,
    color="green",
    linewidth = 0
)

ax = PythonPlot.gca()
ax.xaxis.grid(true)
ax.yaxis.grid(true)
PythonPlot.title("My title")
PythonPlot.legend(["y1","y2"])
PythonPlot.savefig("pythonplot.svg")
pythonplot

Шаг 1. Предварительная обработка атрибутов

Подробнее см. в разделах о замене псевдонимов и особых аргументах.

После этого происходит упрощение и сжатие некоторых аргументов, например преобразование логического параметра colorbar = false во внутреннее описание colorbar = :none, чтобы разрешить сложное поведение без сложного интерфейса, замена nothing невидимым RGBA(0,0,0,0) и т. п.


Шаг 2. Обработка входных данных: пользовательские шаблоны, группировка и многое другое

Графики редко требуют предварительной обработки входных данных. У вас есть массив Julia? Отлично. DataFrame? Нет проблем. Функция построения поверхностных диаграмм? Не вопрос!

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

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

mutable struct MyVecWrapper
  v::Vector{Float64}
end
mv = MyVecWrapper(rand(10))

@recipe function f(mv::MyVecWrapper)
    markershape --> :circle
    markersize  --> 8
    mv.v
end

plot(
    plot(mv.v),
    plot(mv)
)
pipeline2

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

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

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

В данном примере мы разбиваем точки данных на три группы случайным образом и присваиваем им различные формы маркеров ([:s :o :x] являются псевдонимами для :star5, :octagon и :xcross). Другие атрибуты (:markersize и :markeralpha) являются общими.

scatter(rand(100), group = rand(1:3, 100), marker = (10,0.3, [:s :o :x]))
pipeline3

Шаг 3 Инициализация и обновление графика и подграфиков

Извлекаются и обрабатываются атрибуты, применяемые к объектам Plot, Subplot или Axis. Запускаются методы бэкенда для инициализации фигуры/окна, и строится макет.


Шаг 4. Шаблоны рядов

Эта часть в некоторой степени является магической. Выполнив первые три шага, мы получили список словарей ключевых слов (тип KW), которые содержат как данные, так и атрибуты. Теперь мы будем рекурсивно применять шаблоны рядов, сначала проверяя, поддерживает ли бэкенд тип ряда, и если нет, то применяя шаблон ряда и выполняя повторную обработку.

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


Шаг 5. Подготовка для вывода

Большая часть интенсивной обработки разгружается до тех пор, пока она не понадобится. Plots будет стараться избегать дорогостоящих графических обновлений, пока вы не решите отобразить график. Непосредственно перед выводом на экран мы вычислим особенности макета и ограничительных рамок подграфиков и других компонентов графика, после чего запустим обратный вызов кода бэкенда для отрисовки/обновления графика.


Шаг 6. Отображение

Откройте или обновите окно графического интерфейса, запишите в файл или отобразите как встроенный в IJulia. Помните, что в IJulia или REPL график отображается только при возврате (точка с запятой подавляет возврат), либо при явном отображении с помощью display(), gui(), либо путем добавления show = true к команде plot.

Вы можете получить MATLAB-подобное интерактивное поведение, установив значение по умолчанию: default(show = true).