Синтаксис шаблонов
Синтаксис в макросе @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)