4  Формирование документов в Quarto

4.1 Элементы Div и Span

Если выходной формат HTML, то Quarto с помощью Pandoc преобразует Markdown в HTML-код, в частности, для базовых блочных элементов <div> в HTML существует прообраз в Quarto. Классы Div в Quarto начинаются с ограждения, содержащего не менее трех последовательных двоеточий ::: (возможно больше) плюс некоторые атрибуты. Div должен быть отделен пустыми строками от предшествующих и последующих блоков, кроме того, Div могут быть вложенными. Пусть (например, с помощью CSS) определен некий класс .my_class:

Quarto
::: {.my_class}
Новое форматирование.
:::

Тогда Quarto преобразует разметку в HTML:

HTML
<div class="my_class">
  <p>Новое форматирование.</p>
</div>

Хорошим примером применения классов такого рода являются выносные блоки, которые мы рассмотрим далее. Покажем пример сложного вложения Div:

Quarto
:::: {#special .my_class}

::: {.note}
Внутренний текст.
:::

Внешний текст.
::::

Quarto преобразует пример выше в HTML-разметку:

HTML
<div id="special" class="my_class">
  <div class="note">
    <p>Внутренний текст.</p>
  </div>
  <p>Внешний текст.</p>
</div>

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

Quarto
[Это *некий текст*]{.class key="val"}

После рендеринга в HTML Quarto преобразует разметку в:

HTML
<span class="class" data-key="val">
  Это <em>некий текст</em>
</span>

Еще один пример: определим новый CSS-класс .newtext как это сделано ниже

CSS
.newtext {
  font-size: 30px;
  color: red;
}

Тогда текст

Quarto
В HTML документе есть [селекторы по CSS классу]{.newtext}. 

будет преобразован в следующий:

В HTML документе есть селекторы по CSS классу.

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

В случае, если текст занимает много места или есть необходимость спрятать его под сворачивающийся комментарий, то это можно сделать с помощью тега <details>.

Quarto
<details>
<summary>Пример скрытого текста</summary>

Lorem pharetra erat bibendum pharetra, tellus metus mi mi molestie; risus natoque justo. Curabitur aliquet venenatis risus, urna, libero lobortis erat nibh? Per lectus sem dictumst nullam aliquet risus velit lacus cras tempus. Faucibus rhoncus faucibus tempus varius.

</details>
Пример скрытого текста Lorem pharetra erat bibendum pharetra, tellus metus mi mi molestie; risus natoque justo. Curabitur aliquet venenatis risus, urna, libero lobortis erat nibh? Per lectus sem dictumst nullam aliquet risus velit lacus cras tempus. Faucibus rhoncus faucibus tempus varius.

4.2 Перекрестные ссылки

Перекрестные ссылки в Quarto позволяют связывать части документа, а читателю ориентироваться в тексте. В Quarto перекрестные ссылки представляют собой пронумерованные ссылки и гиперссылки на различные элементы текста и могут создаваться автоматически. Удобство состоит в том, что, как и в LaTeX, нумерация ссылок создается автоматически. В том месте, где создается ссылка, необходимо создать уникальный идентификатор с префиксом типа перекрестной ссылки. Все известные зарезервированные префиксы: #fig-, #tbl-, #lst-, #tip-, #nte-, #wrn-, #imp-, #cau-, #thm-, #lem-, #cor-, #prp-, #cnj-, #def-, #exm-, #exr-, #sol-, #rem-, #eq-, #sec-. Некоторые примеры показаны в таблице далее.

Таблица 4.1: Примеры префиксов
Префикс Вывод
#fig- Рисунок
#tbl- Таблица
#sec- Глава
#thm- Теорема
#eq- Уравнение

Перекрестная ссылка устроена следующим образом: внутри блока кода label: prefix-LABEL, внутри Markdown #prefix-LABEL, где LABEL – уникальное имя ссылки. В тексте можно сослаться как @prefix-LABEL. Покажем на примерах, как это работает. Мы уже видели примеры перекрестных ссылок в рисунках.

Quarto
![Рисунок](picture.png){#fig-my-picture}

См. иллюстрацию: @fig-my-picture.

Отметим, что ссылки можно кастомизировать как это показано ниже в таблице.

Тип ссылки Синтаксис Вывод
По умолчанию @fig-Moscow Рисунок 3.2
Заглавные @Fig-Moscow Рисунок 3.2
Собственный префикс [рис. @fig-Moscow] рис. 3.2
Без префикса [-@fig-Moscow] 3.2
Quarto
Перекрестные ссылки можно группировать: [@fig-fires; @fig-wildfires].

Перекрестные ссылки можно группировать: Рисунок 3.3 (a), Рисунок 3.3 (b).

Также можно ссылаться на таблицы.

Quarto
| пункт 1 | пункт 2 | пункт 3 |
|:--------|:--------|:--------|
| one     | один    | 1       |
| two     | два     | 2       |

: Таблица со ссылкой {#tbl-sample-table}

См. таблицу: @tbl-sample-table.
Таблица 4.2: Таблица со ссылкой
пункт 1 пункт 2 пункт 3
1 один one
2 два two

См. таблицу: Таблица 4.2.

На разделы документа можно ссылаться с помощью #sec-, как это сделано ниже.

Quarto
## Введение {#sec-introduction}

См. раздел @sec-introduction.

Ссылки на уравнения делаются на основе префикса eq-.

Quarto
Линейная регрессионная модель выражается соотношением
$$
y = \beta_1 x_1 + \beta_2 x_2 + \ldots + \beta_n x_n + \varepsilon.
$$ {#eq-regression}

См. уравнение: @eq-regression.

Линейная регрессионная модель выражается соотношением \[ y = \beta_1 x_1 + \beta_2 x_2 + \ldots + \beta_n x_n + \varepsilon. \tag{4.1}\]

См. уравнение: Уравнение 4.1.

На математические конструкции (доказательства, леммы, определения и т.д.) также можно ссылаться.

Quarto
::: {#lem-Zorn}

## Лемма Цорна

Если в частично упорядоченном множестве M для всякого линейно-упорядоченного подмножества существует верхняя грань, то в M существует максимальный элемент.

См. лемму: @lem-Zorn.
:::

Лемма 4.1 (Лемма Цорна) Если в частично упорядоченном множестве M для всякого линейно-упорядоченного подмножества существует верхняя грань, то в M существует максимальный элемент.

См. лемму: Лемма 4.1.

Отметим, что класс .proof не обладает нумерацией.

Quarto
::: {.proof}
Текст доказательства.
:::

Доказательство. Текст доказательства.

Списки (листинги) также могут иметь перекрестные ссылки.

Quarto
::: {#lst-customers}

```{.sql}
SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country;
```

Запросы клиентов
:::
Список 4.1: Запросы клиентов
SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country;

В Quarto можно создавать собственные перекрестные ссылки. Ниже показан пример собственного определения перекрестных ссылок, причем ссылаться на этот вариант ссылки можно будет как {#vid-LABEL}.

_quarto.yaml
crossref:
  custom:
    - kind: float
      reference-prefix: Video
      key: vid

4.3 Выносные блоки

Интересной новинкой Quarto являются выносные блоки, которые служат способом привлечения дополнительного внимания к определенным указаниям или концепциям, например, о том, что определенное содержимое является важным или применимо только к некоторым сценариям. Выносные блоки работают в HTML, PDF, MS Word, ePub и Revealjs. Основные типы блоков: note, warning, important, tip и caution.

Замечание

Этот элемент будет использоваться для замечаний.

Предостережение

Этот элемент будет использоваться для предостережений.

Важно

Этот элемент будет использоваться для важных сообщений.

Совет

Этот элемент будет использоваться для рекомендаций и советов.

Осторожно

Этот элемент будет использоваться для предупреждений.

Ниже показан пример кода для оформления блока.

Quarto
:::{.callout-tip}
## Совет

Этот элемент будет использоваться для рекомендаций и советов.
:::

либо

Quarto
::: {.callout-tip title="Совет"}
Этот элемент будет использоваться для рекомендаций и советов.
:::
Совет

Этот элемент будет использоваться для рекомендаций и советов.

Пример блока с развертыванием.

Quarto
::: {.callout-caution collapse="true"}
## Разверните блок

Это пример *свернутого* блока.
:::

Это пример свернутого блока, который может быть развернут пользователем. Вы можете управлять параметрами, указав collapse="true" или collapse="false".

Пример кастомизации блока.

Quarto
::: {.callout-note icon=false}
## Обратите внимание

Здесь нет иконки
:::
Обратите внимание

Здесь нет иконки

4.4 Диаграммы

В Quarto реализована вставка диаграмм — это еще одно ноу-хау в Quarto, имеющее встроенную поддержку Mermaid и Graphviz диаграмм. Данное новшество позволяет создавать блок-схемы, диаграммы последовательностей, диаграммы состояний, диаграммы Ганта и многое другое, используя синтаксис обычного текста, мотивированный Markdown.

Mermaid — это инструмент для создания диаграмм и графиков на основе JavaScript, который преобразует текстовые определения в формат Markdown для динамического создания и изменения диаграмм с достаточно простым синтаксисом и большой вариативностью настроек. Для Mermaid существует онлайн редактор.

Ниже показан пример оформления диаграммы.

Mermaid
```{mermaid}
%%| label: fig-mermaid-example
%%| fig-cap: |
%%|  Простая диаграмма Mermaid
flowchart LR
  A[Прямоугольник] --> B(Скругленные края)
  B --> C{Решение}
  C --> D[Первый результат]
  C --> E[Второй результат]
```
flowchart LR
  A[Прямоугольник] --> B(Скругленные края)
  B --> C{Решение}
  C --> D[Первый результат]
  C --> E[Второй результат]
Рисунок 4.1: Простая диаграмма Mermaid

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

Graphviz
```{dot}
//| label: fig-graphviz-example
//| fig-width: 5
//| fig-cap: |
//|   Пример Graphviz-диаграммы
digraph {
    Москва -> Минск[color=red,label="0.2",weight="0.2"];
    Москва -> Казань[label="0.4",weight="0.4"];
    Казань -> Минск[color=red,label="0.6",weight="0.6"];
    Казань -> Воронеж[label="0.6",weight="0.6"];
    Воронеж -> Воронеж[label="0.1",weight="0.1"];
    Воронеж -> Минск[color=red,label="0.7",weight="0.7"];
}
```
Москва Москва Минск Минск Москва->Минск 0.2 Казань Казань Москва->Казань 0.4 Казань->Минск 0.6 Воронеж Воронеж Казань->Воронеж 0.6 Воронеж->Минск 0.7 Воронеж->Воронеж 0.1
Рисунок 4.2: Пример Graphviz-диаграммы
Опции для диаграмм

Обратите внимание, что ячейки имеют несколько различный синтаксис: %%| для параметров и %% для комментариев {mermaid} (Mermaid), и //| для параметров и // для комментариев в {dot} (Graphviz).

4.5 Формулы в Quarto

Математические формулы в Quarto реализованы на основе LaTeX-синтаксиса: также как и в \(\LaTeX\) здесь используются разделители $ для встроенных математических элементов в тексте и разделители $$ для выносной математики. Пример формулы в Quarto приведен ниже.

LaTeX
$$
Y_Q(a_1, \ldots, a_n) = 
  (-1)^{\frac{(d-1)(d-2)}{2}} \int_{Z_f}
  \Omega\wedge
  Q\left(
    a_1\frac{\partial}{\partial a_1},\ldots,
    a_n\frac{\partial}{\partial a_n}
  \right)\Omega
$$

\[ Y_Q(a_1, \ldots, a_n) = (-1)^{\frac{(d-1)(d-2)}{2}} \int_{Z_f} \Omega\wedge Q\left( a_1\frac{\partial}{\partial a_1},\ldots, a_n\frac{\partial}{\partial a_n} \right)\Omega \]

Для математической обработки HTML с использованием MathJax (по умолчанию) можно использовать команды \def, \newcommand, \renewcommand, \newenvironment, \renewenvironment и \let для создания собственных макросов и сред. Если необходимо определить пользовательские макросы TeX (в PDF), включите их в разделители $$, заключенные в блок .hidden. Например:

Quarto
::: {.hidden}
$$
 \def\RR{{\bf R}}
 \def\bold#1{{\bf #1}}
$$
:::

4.6 Наборы вкладок

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

::: {.panel-tabset .nav-pills}

## Диаграмма рассеяния

...

## Диаграмма размаха

...

## Данные

...

:::

4.7 Включения

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

{{< include _text.qmd >}}

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

Замечание

Обратите внимание на то, что файл начинается с префикса подчеркивания _file_name.qmd для включаемых файлов, это позволяет автоматически игнорировать их для исполнения при выполнении quarto render. Во избежание конфликтов, рекомендуется на использовать метаданные во включаемых файлах.

Включения позволяют делать вставки как Quarto-файлов как с ячейками с кодом, так и отдельные файлы с кодом, сохраненным в виде скриптов.

Замечание

Следует отметить, что ячейки исходного и включаемого файлов должны использовать один и тот же движок — то есть knitr или jupyter, но не оба. Вычислительные функции работают только в *.qmd-файлах, но не работают в *.ipynb файлах Jupyter-блокнотов.

Приведем пример файла с включениями, где вычисления происходят как в основном файле, так и внутри файла-включения.

---
title: "Пример файла с включениями"
---

## Обзор

Здесь может быть текст основного файла.

{{< include _secondary.qmd >}}

## Немного кода

Включение **R**-кода...

```{r}
{{< include _demo.R >}}
```
_secondary.qmd
Небольшой включаемый текст с кодом.

```{r}
library(ggplot2)

mpg |>
  ggplot(aes(x = displ, y = hwy, 
             colour = class)) + 
  geom_point()
```

Помимо include, возможно встраивание вывода другого документа Quarto (*.qmd или *.ipynb) с помощью конструкции embed. Чтобы встроить вывод блока кода или ячейки блокнота, необходимо указать путь к документу и идентификатор блока или ячейки.

Пусть hello.ipynb это файл, из которого необходимо извлечь фрагмент кода.

hello.ipynb
---
title: "Пример ноутбука"
jupyter: python3
---

#| label: fig-polar
#| fig-cap: "Пример встроенного графика"

import numpy as np
import matplotlib.pyplot as plt

r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
fig, ax = plt.subplots(
  subplot_kw = {'projection': 'polar'} 
)
ax.plot(theta, r)
ax.set_rticks([0.5, 1, 1.5, 2])
ax.grid(True)
plt.show()

Встраивание из hello.ipynb происходит следующим образом.

Quarto
{{< embed hello.ipynb#fig-polar >}}
Рисунок 4.3: Пример встроенного графика

4.8 Переменные

В Quarto возможно вставлять переменные в виде формата «short codes» (так называемые «короткие элементы кода»), общий вид которых

{{< shortcode code >}}

В Quarto существует три варианта для «коротких элементов кода»: meta, env и var.

meta

Короткий элемент кода meta позволяет вставлять в текст содержимое из текущих YAML-метаданных Pandoc (например, из _quarto.yml). Например, для того, получить название текущей главы можно так:

{{< meta title >}}

4  Формирование документов в Quarto

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

{{< meta book.title >}}

Научно-издательская система Quarto

env

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

  1. Можно определить версии R и Python, которые будут использоваться (например, QUARTO_PYTHON, QUARTO_R, PY_PYTHON).
  2. Настроить поведение исполняемого кода (например, OMP_NUM_THREADS, ARROW_IO_THREADS и т.д.).
  3. Предоставить учетные данные для доступа к удаленному серверу, адрес прокси-сервера (например, HTTP_PROXY).

Значения переменных можно включать в файл _environment на корневом уровне вашего проекта в котором находится файл _quarto.yml, они будут прочитаны и установлены в среде при чтении конфигурации проекта.

_environment
# пример настройки окружения
# сервер баз данных
DATABASE_SERVER=https://db.example.com
DATABASE_API=${DATABASE_SERVER}/api

Подробнее о возможностях env можно прочесть на странице документации Quarto.

var

Предположим, что в документе есть изменяемые элементы, которые могут изменяться в различных версиях, тогда изменямые элементы можно хранить в файле _variables.yml и обращаться с помощью конструкции {{< var key>}}. Приведем пример файла _variables.yml.

_variables.yml
version: 1.1

course: 
  name: Курс по языкам программирования

instructor:
  name: А.А. Иванов
  email: my@email.com
  url: "https://site.name.com/"
  mastodon: mymastodon
  github: mygithub

university:
  name: МГУ им. М.В. Ломоносова
  url: "[университет](<https://university.site.com/>)"

Теперь чтобы включить значение переменной, можно использовать {{< var key>}} либо {{< var key.subkey>}}, например:

Quarto
*{{< var course.name>}}* ожидает большая переработка, в новой версии {{< var version>}} будут существенные изменения. Основной лектор нашего курса --- {{< var instructor.name>}}, {{< var university.name>}}.

Курс по языкам программирования ожидает большая переработка, в новой версии 1.1 будут существенные изменения. Основной лектор нашего курса — А.А. Иванов, МГУ им. М.В. Ломоносова.

Безусловно, короткие элементы кода в базвом Quarto позволяют делать автоматизированные шаблоны, что значительно облегчает набор документов, вместе с тем, расширения Quarto также дают возможность использовать ту же конструкцию на базе {{< shortcode code >}}. Например, расширение now позволяет вставлять в документы текущие временные показатели, например, год, месяц, дату и прочее.

Таблица 4.3: Примеры вставки даты и времени
Эелемент кода Результат Формат
{{< now >}}

2024-10-13 03:44:38

"%F %T"
{{< now year >}}

2024

"%Y"
{{< now day >}}

13

"%d"
{{< now hour >}}

03

"%I"
{{< now minute >}}

44

"%M"

4.9 Макеты страницы

Рассмотрим расположение компонентов макета для HTML-страниц. Несмотря на то, что документы Quarto HTML по умолчанию хорошо оптимизированы для чтения, иногда возникает потребность в настройке страниц. Мы рассмотрим следующие варианты настройки:

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

4.9.1 Настройка ширины блоков страницы

Ширина содержимого контролируется с помощью классов Div. Например, column-body задает ширину по умолчанию.

.column-body

В том случае, если необходимо расширить ширину содержимого, можно использовать Div класс column-body-outset.

Quarto
:::{.column-body-outset}
Dolor tempor cras arcu ornare quam, curae porttitor per suspendisse gravida sagittis? Tristique sed duis dictumst hendrerit dapibus dignissim mauris pharetra mattis viverra. Vitae pellentesque facilisi gravida habitasse class inceptos fames semper. Parturient euismod sem: facilisi parturient nostra et cubilia consequat. At facilisis velit?
:::

Dolor tempor cras arcu ornare quam, curae porttitor per suspendisse gravida sagittis? Tristique sed duis dictumst hendrerit dapibus dignissim mauris pharetra mattis viverra. Vitae pellentesque facilisi gravida habitasse class inceptos fames semper. Parturient euismod sem: facilisi parturient nostra et cubilia consequat. At facilisis velit?

.column-body-outset

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

.column-page

Например, для широкого рисунка:

:::{.column-page}
![](Elbrus.png)
:::

Аналогично с помощью класса column-screen можно разместить содержимое на всю ширину страницы без полей.

.column-screen

Отметим, что такого рода классы применимы и к ячейкам с кодом. Например, если график шире, чем узкая область текста, это можно регулировать параметрами column и out-width.

R
#| column: screen
#| out-width: 100%

library(leaflet)

leaflet() |>
  addTiles(options = providerTileOptions(opacity = 0.9,
                                         minZoom=15, 
                                         maxZoom=16)) |>
  addMarkers(lng = 174.768, 
             lat = -36.852, 
             popup = "Место рождения языка R")

В том случае, если содержимое выходит за пределы основной области только на левой или правой стороне документа, можно использовать версии right и left для компоновки переполнения как показано ниже.

.column-body-outset-right

.column-page-inset-right

.column-page-right

.column-screen-inset-right

.column-screen-right

Для переполнения слева все работает точно также, только -right нужно заменить на -left. Таким образом, для блоков переполнение работает как конструкция вида

Quarto
:::{.column-class}

Содержимое...

:::

в которой class может быть: body, body-outset, page-inset, page, screen-inset и screen, после чего может стоять уточняющий -right или -left. Если переполнение осуществляется внутри ячейки с кодом, то конструкция выше заменяется на

R/Python/Julia
#| column: class

Исполняемый код...

4.9.2 Настройка отдельных компонентов страницы

Макет документов Quarto HTML организован в структуру, состоящую из трех основных частей:

  • боковая панель (sidebar),
  • основная область (body),
  • поля страницы (margin),

а также пространства между этими элементами (gutter).

Рисунок 4.4: Макет Quarto HTML страницы

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

Таблица 4.4: Значения по умолчанию для ширины компонентов макета
Элемент страницы Размер
sidebar-width 250px
body-width 800px
margin-width 250px
gutter-width 1.5em

Ширину данных элементов можно варьировать с помощью переменных YAML или SCSS.

_quarto.yml
format:
  html: 
    grid:
      sidebar-width: 200px
      body-width: 750px
      margin-width: 450px
      gutter-width: 2em
style.scss
// левая боковая панель
$grid-sidebar-width: 200px !default;

// основная область
$grid-body-width: 750px !default;

// поля страницы справа
$grid-margin-width: 450px !default;

// пространство между элементами указанными выше
$grid-column-gutter-width: 2em !default;

Здесь sidebar-width, body-width, и margin-width должны быть указаны в пикселях (px), поскольку значения будут использоваться при вычислении других размеров, поскольку общая ширина документа обычно привязана к размеру браузера и адаптивным контрольным точкам, а не к размеру шрифта или другим относительным показателям.

Заключение

В данном разделе мы сделали обзор множества различных настроек для формирования документов Quarto:

  • перекрестные ссылки,
  • выносные блоки для акцентирования внимания,
  • диаграммы,
  • формулы,
  • наборы вкладок,
  • возможности повторного использования контента,
  • макеты веб-страниц.

Все эти элементы существенно расширяют базовые возможности Quarto, рассмотренные в предыдущем разделе.