Синтаксис шаблонов
Синтаксис в макросе @recipe лучше всего пояснить на примере. Предположим, у нас есть пользовательский тип, хранящий результаты моделирования x и y и меру ε для максимальной погрешности в y.
struct Result
x::Vector{Float64}
y::Vector{Float64}
ε::Vector{Float64}
end
Чтобы построить график значений x и y такого результата с полосой погрешности, заданной ε, можно выполнить что-то вроде следующего
res = Result(1:10, cumsum(rand(10)), cumsum(rand(10)) / 5)
using Plots
# строит полосу погрешностей в виде невидимой строки с диапазоном заполнения
plot(
res.x,
res.y .+ res.ε,
xlabel = "x",
ylabel = "y",
fill = (res.y .- res.ε, :lightgray, 0.5),
linecolor = nothing,
primary = false, # без записи условных обозначений
)
# добавляет данные на графики
plot!(res.x, res.y, marker = :diamond)
Вместо постоянного ввода этой команды для получения различных результатов можно определить пользовательский шаблон, чтобы указать Plots, что делать с входными данными типа Result. Приведем пример такого пользовательского шаблона с дополнительной функцией выделения точек данных, где максимальная погрешность превышает определенный порог ε_max.
@recipe function f(r::Result; ε_max = 0.5)
# задает значение по умолчанию для атрибута с помощью `-->`
xlabel --> "x"
yguide --> "y"
markershape --> :diamond
# добавляет ряд для полосы погрешностей
@series begin
# применяет аргумент с помощью `:=`
seriestype := :path
# игнорирует ряды в условных обозначениях и изменении палитры цветов
primary := false
linecolor := nothing
fillcolor := :lightgray
fillalpha := 0.5
fillrange := r.y .- r.ε
# гарантирует, что для полосы погрешностей не отображаются маркеры
markershape := :none
# возвращает данные ряда
r.x, r.y .+ r.ε
end
# получает цвет ряда, указанный пользователем
c = get(plotattributes, :seriescolor, :auto)
# выделяет значительные погрешности, в противном случае использует определяемый пользователем цвет
markercolor := ifelse.(r.ε .> ε_max, :red, c)
# возвращает данные
r.x, r.y
end
Разберем этот шаблон пошагово. Сначала сигнатура функции в определении шаблона определяет тип шаблона, в данном случае — пользовательский шаблон. Имя функции f является несущественным и может быть заменено любым другим именем функции. @recipe не использует его. В теле шаблона можно задать значения по умолчанию для атрибутов Plots.
attr --> val
При этом attr будет задано val, если в команде plot пользователь не укажет иное.
plot(args...; kw..., attr = otherval)
Аналогичным образом можно принудительно задать значение атрибута с помощью :=.
attr := val
Будет перезаписано все, что пользователь передал в plot для attr, и задано val.
|
Настоятельно рекомендуется не использовать псевдонимы атрибутов в шаблонах, так как в некоторых случаях это может привести к неожиданному поведению. В приведенном выше шаблоне |
С помощью макроса @series мы добавляем на график новый ряд для полосы погрешностей. Внутри блока @series можно использовать тот же синтаксис, что и выше, для принудительной установки или задания значений по умолчанию для атрибутов.
В @recipe у нас есть доступ к plotattributes. Это словарь (AbstractDict), хранящий атрибуты, которые уже были обработаны на текущем этапе конвейера Plots. Для пользовательских шаблонов, которые вызываются на ранних этапах конвейера, здесь в основном содержатся именованные аргументы, указанные пользователем в команде plot. В нашем примере мы хотим выделить точки данных с погрешностью выше определенного порога, изменив цвет маркера. Для всех остальных точек данных мы задаем цвет маркера, который используется по умолчанию или был указан в качестве именованного аргумента. Это можно сделать, получив seriescolor из plotattributes и установив по умолчанию auto, если он не был указан пользователем.
Наконец, в обоих блоках @recipe и @series мы возвращаем данные, которые хотим передать Plots (или в следующий шаблон).
С помощью приведенного выше шаблона мы можем построить график Result, используя только
plot(res)
или
scatter(res, ε_max = 0.7, color = :green, marker = :star)