Готовим графики к публикации
Все мы когда-нибудь сталкивались с необходимость сделать отчет по ГОСТу и пройти нормоконтроль, пусть и с десятого раза. Вспомните, какую ценность имели шаблоны для Word и как неудобно было делать графики в Excel. В этой публикации я хочу показать как Engee помогает сделать графики, пригодные для отчетов. Причем сделать это можно тремя способами!
Требования к графикам
Пусть наш нормоконтроль имеет следующие требования к оформлению графиков:
- Подписи осей и графика - шрифт Times New Roman, 12ый кегль
- Подпись оси X находится в правой стороне оси
- Подпись оси Y находится в верхней стороне оси
- Легенда - без рамки, в правом верхнем углу осей
- Сетка черного цвета
- Линии - оттенки серого
- Толщина линии - 1.5
Обзор возможностей Engee для графиков
Библиотека Plots.jl предоставляет три подхода к настройке графиков:
- Указание атрибутов во время построения графиков, как аргумент команды
plot - Использование тем
- "Рецепты" для графиков
Дополнительно, библиотека предоставляет несколько бэкендов для отрисовки графиков. В Engee по умолчанию используется бэкенд plotlyjs. Однако, этот бэкенд не поддерживает некоторые атрибуты, поэтому воспользуемся классическим бэкендом GR.
Использование команды plot()
Самый простой способ - это задать атрибуты во время вызова команды plot. Давайте попробуем построить наш график. Для этого сначала сгенерируем данные:
x = 0:0.01:10;
ys = hcat(
sin.(x),
cos.(x),
);
Как же сделать линии оттенков серого? Можно изменить палитру графиков:
using Plots
gr()
plot(x,ys,palette=:grays)
Однако мы видим, что часть линий потерялась - их цвет слишком яркий! Но библиотека графиков дает возможность сделать свою палитру. Создадим ее с учетом того, что слишком светлые линии нам не нужны. А цветов в палитре будет ровно столько же, сколько и рядов для которых мы строим графики. Для создания палитры я написал такую вспомогательную функцию:
function gen_gs_pallete(data)
nseries = data isa AbstractVector ? 1 : size(data, 2)
if nseries > 1
GS_Pallete = [ RGB(t, t, t) for t in range(0.0, 0.75; length=nseries)]
else
GS_Pallete = RGB(0.0,0.0,0.0);
end
GS_Pallete
end
gspalette = gen_gs_pallete(ys)
Попробуем ее применить:
plot(x,ys,palette=gspalette)
Стало лучше. Теперь изменим шрифт и кегль. Сделать это можно выставив такие атрибуты как titlefont, guidefont, tickfont, legendfont. Для управления шрифтом используется объект font:
GOST_font = font(12, "times");
titlefont = GOST_font;
guidefont = GOST_font;
tickfont = GOST_font;
legendfont = GOST_font;
Применим настройки:
plot(x,ys,palette=gspalette,titlefont=titlefont,guidefont=guidefont,tickfont=tickfont,legendfont=legendfont)
Теперь давайте настроим вид легенды, ее положение, сетку и положение подписей к осям:
plot(x,ys,
palette=gspalette,
titlefont=titlefont,
guidefont=guidefont,
tickfont=tickfont,
legendfont=legendfont,
xguidefonthalign = :right,
yguidefontvalign = :top,
legend=(0.94,0.95),
foreground_color_legend = nothing,
background_color_legend = nothing,
gridalpha = 1
)
xlabel!("t,c")
ylabel!("Сигнал")
Обратите внимание, как указано расположение легенды. Я задаю его как положение относительно правого верхнего угла графика. Почти получилось, но давайте изменим границы осей. Для этого вычислим новые размеры при помощи вот такой функции:
function lim_helper(x,y)
xlim = (x[1],x[end] + x[end]/4)
yh = ceil(maximum(y) + maximum(y)/4)
ylim = (-yh,yh)
xlim,ylim
end
(xlim,ylim) = lim_helper(x,ys)
Итоговый график примет такой вид:
plot(x,ys,
palette=gspalette,
titlefont=titlefont,
guidefont=guidefont,
tickfont=tickfont,
legendfont=legendfont,
xguidefonthalign = :right,
yguidefontvalign = :top,
legend=(0.94,0.95),
foreground_color_legend = nothing,
background_color_legend = nothing,
gridalpha = 1,
xlim = xlim,
ylim = ylim
)
xlabel!("t,c")
ylabel!("Сигнал")
Применяем темы
Выше, команда plot приобрела слишком раздутый размер. Библиотека Plots позволяет сделать свою тему оформления графиков, что позволяет однократно настроить все атрибуты и распространить их на последующие графики. Тема создается с помощью команды PlotTheme, и необходимые атрибуты выступают ее аргументами:
gsTheme = PlotTheme(
linewidth = 1.5,
titlefont = GOST_font,
guidefont = GOST_font,
tickfont = GOST_font,
legendfont = GOST_font,
xguidefonthalign = :right,
yguidefontvalign = :top,
legend=(0.94,0.95),
foreground_color_legend = nothing,
background_color_legend = nothing,
gridalpha = 1
);
После того, как мы создали тему, ее надо зарегистрировать:
PlotThemes.add_theme(:gstheme, gsTheme);
Теперь можно использовать эту тему для построения графиков. В коде ниже тема применяется глобально к последующим графикам:
theme(:gstheme,palette=gen_gs_pallete(ys))
(xlim,ylim) = lim_helper(x,ys)
plot(x,ys,xlim = xlim,ylim = ylim)
xlabel!("t,c")
ylabel!("Сигнал")
Если мы хотим вернуться к теме по умолчанию, то необходимо выполнить команду:
theme(:default)
Рецепты для графиков и пользовательские графики
Библиотека Plots позволяет не только создавать темы, но и создавать пользовательские типы графиков на основе "рецептов".
Рецепты (Recipes) - это механизм, который позволяет определять правила визуализаций данных. Они применяются до отрисовки графиков в такой последовательности:
-
User Recipes (
@userplot): Создают новые функции для построения графиков с пользовательским интерфейсом (например,andrewsplot). -
Type Recipes: Определяют, как конкретный Julia-тип (например,
DistributionилиSimResult) должен быть преобразован в массивы для отрисовки. -
Plot Recipes: Описывают визуализацию данных до того, как будут созданы ряды (например, создание сложных макетов).
-
Series Recipes: Самый низкий уровень; определяют, как рисовать конкретную серию (например, описывается превращение гистограммы в набор столбцов).
Эти 4 пункта входят в так называемый пайплайн построения графиков. Подробнее пайплайн описан в документации. А еще эти рецепты не зависят от библиотеки Plots! Широко применяемый пакет StatsPlots.jl как раз использует механизм рецептов. Для нашего графика создадим новый тип графика, GOSTPlot:
@userplot GOSTPlot
@recipe function f(p::GOSTPlot)
x = p.args[1]
y = p.args[2]
# number of series
nseries = y isa AbstractVector ? 1 : size(y, 2)
if nseries == 1
linecolor := :black
else
palette := [RGB(t, t, t) for t in range(0.0, 0.75; length=nseries)]
end
linewidth := 1.5
seriestype := :path
fontfamily := "Times"
fontsize = 12;
titlefont := font(fontsize, "Times")
guidefont := font(fontsize, "Times")
tickfont := font(fontsize, "Times")
legendfont := font(fontsize, "Times")
foreground_color := :black
background_color := :white
legend := (0.94,0.95)
xlabel := "t, с"
ylabel := "Сигнал"
xlim := (x[1],x[end] + x[end]/4)
yh = ceil(maximum(y) + maximum(y)/4)
ylim := (-yh,yh)
xguidefonthalign := :right
yguidefontvalign := :top
grid := true
gridalpha := 1.0
foreground_color_legend := nothing
background_color_legend := nothing
x, y
end
Обратите внимание - теперь наш график автоматически создает палитру и сам назначает подписи осей. Так как рецепт нашего графика это макрос, то внутри функции мы можем вызывать любой код, который вычисляет значения атрибутов. Еще следует обратить внимание на то, что мы возвращаем x и y - они нужны для последующей отрисовки графиков.
Давайте испытаем наш новый график!
gostplot(x,ys)
Выводы
В данной публикации я рассмотрел три варианта построения графиков удовлетворяющих популярным требованиям нормоконтроля. Было рассмотрено три метода настройки графиков и рассмотрены особенности библиотеки Plots.






