Достаточно часто встречается задача, когда надо нарисовать нечто, представляющее из себя граф. Это может быть иерархическая сструктура работ проекта, иерархическая структура рисков, организационная структура, топология сети и т.п. Если же количество вершин и ребер достаточно велико, то нарисовать это красиво становится нетривиальной задачей. К счастью, существует программа Graphviz, которая использует язык описания графов dot и имеет графический интерфейс под Windows.
Сама программа бесплатна и может быть скачена по ссылке. После установки в Windows в меню «Пуск» появится приложение gvedit, которое является графическим интерфесом к установленному пакету программ. В Mac OS X такого не произойдет и обходное решение описано ниже (если, конечно, вы не готовы работать в терминале).
Рассмотрим направленный граф, описывающий все возможные коммуникации между четырьмя участниками проекта. Листинг, описывающий граф привожу ниже:
digraph test{
1->2;
1->3;
1->4;
2->1;
2->3;
2->4;
3->1;
3->2;
3->4;
4->1;
4->2;
4->3;
}
В приложении это будет выглядеть следующим образом:
В данном случае, был использован параметр по умолчанию и для построения графа использована утилита dot.
Для примера, тот же граф, построенный с помощью:
![]() |
![]() |
![]() |
circo | fdp | twopi |
Подробнее это описано в документации на сайте. Если кратко, то dot, а далее в статье используется только он, рисует граф в заданном в порядке ветвления; twopi — использует радиальное построение, когда вершины располагаются на концентрических окружностях, circo — связанные вершины располагаются по кругу.
В данном случае, у нас был изображен орграф. Если граф не ориентирован, иными словами «стрелочки» на рисунке нам не важны, то граф описывается следующим образом (обратите внимание на первой слово «graph» вместо «digraph», как задаются связи («—» вместо «->»), а также на команду node [shape = box] — задающую прямоугольники в качестве вершин). Несомненно, формат стрелок можно определить и в самом графе на языке dot, но алгоритмы построения для ориентированных и неориентированных графов имеют небольшое отличие.
graph test{
node [shape = box];
"Проект А"--"Фаза 1";
"Проект А"--"Фаза 2";
"Проект А"--"Фаза 3";
"Фаза 1"--"Задача 1";
"Фаза 1"--"Задача 2";
"Фаза 2"--"Задача 3";
"Фаза 2"--"Задача 4";
}
Результат выглядит так:
Пример из заметки по динамическому программированию: меняем ориентацию графа (строится справа налево), добавляем подписи и стили линий и задаем размер листа.
digraph ex01 {
rankdir=LR;
size="8,5"
node [shape = box];
"1" -> "2" [ label = "3",style=bold,color=red ];
"1" -> "3" [ label = "7",style=dotted];
"1" -> "4" [ label = "2",style=dotted ];
"2" -> "5" [ label = "9",style=dotted ];
"2" -> "6" [ label = "11",style=bold,color=red ];
"3" -> "5" [ label = "5",style=dotted ];
"3" -> "6" [ label = "10",style=dotted ];
"3" -> "7" [ label = "7",style=dotted ];
"4" -> "6" [ label = "15",style=dotted ];
"4" -> "7" [ label = "13",style=dotted ];
"5" -> "8" [ label = "7",style=dotted ];
"5" -> "9" [ label = "5",style=dotted ];
"6" -> "8" [ label = "3",style=bold,color=red ];
"6" -> "9" [ label = "4",style=dotted ];
"7" -> "8" [ label = "7",style=dotted ];
"7" -> "9" [ label = "1",style=dotted ];
"8" -> "10" [ label = "1",style=bold,color=red ];
"9" -> "10" [ label = "4",style=dotted ];
}
Более сложные пример приведен в заметке по методу анализа иерархии.
При выборе формата записи результирующего графа, определенный интерес представляет формат svg в поле Output File Type. Формат svg — векторный формат, файлы в этом формате можно редактировать, например, с помощью Inkscape. Также обратите внимание на векторный формат emf, позволяющий внедрять и масштабировать рисунки в Word без потери качества. Для этого сайта был использован растровый формат png.
И последний пример, использование структуры в качестве вершины графа.
digraph structs {
node [shape=record, style="rounded,filled"];
struct1 [label="<f0> Инициация|<f1> Планирование|<f2> Исполнение|<f3> Завершение",
fillcolor=yellow];
struct2 [label="<f0> Мониторинг|<f1> Контроль"];
struct1:f0-> struct2:f0;
struct1:f1-> struct2:f0;
struct1:f2-> struct2:f0;
struct1:f3-> struct2:f0;
struct2:f1 -> struct1:f0 [color="red"];
struct2:f1 ->struct1:f1 [color="red"];
struct2:f1 ->struct1:f2 [color="red"];
struct2:f1 ->struct1:f3 [color="red"];
}
Иcпользование команд консоли
Самый простой вариант создать диаграмму — это запустить в консоли соответсвующую команду (на рисунке показано окно Терминала под Mac OS X), например, dot. Если файл называется test.gv, а на выходе мы хотим получить png, то команда будет такая, как это показано ниже (а также выше на рисунке). Решение универсально и работает в любой операционной системе.
dot -Tpng -O test.gv
Использование RStudio для рисования графов
Следует отметить, что в Mac OS X при установке graphviz такой удобной графической оболочки как в Windows, вы не получите. Если вариант собрать его из исходников из портов (если вы хоть что-то поняли из написанных слов, значит инструкции не нужны), можно использовать консоль, как это показано выше, но также имеется возможноть использовать средства языка R и RStudio. Возможно, что кому-то это будет проще.
Для корректной работы вам понадобится пакет DiagrammeR. Установите его из меню или командой
install.packages("DiagrammeR")
После установки создайте файл, скопируйте туда код, сохраните его с расширением .gv, поставьте галочку «Preview on Save». Обратите внимание, если вы работаете в Windows, то файл необходимо сохранять в кодировке cp1251, иначе вместо русского языка вы получите кракозябры.
Дальше, используя кнопку Export, можно сохранить получившуюся диаграмму в формате png или jpeg.
Альтернативные варианты рисования диаграмм
Следует отметить, что DiagrammeR позволяет работать и с диаграммами в формате mermaid. В таком случае, файлы надо сохранять с расширением .mmd. Помимо простых графов, этот формат позволяет описать диаграмму последовательности, как это показано на рисунке ниже.
Код:
sequenceDiagram
Покупатель->>Кассир: запрос билетов
Кассир->>База данных: наличие мест
alt билеты имеются
База данных->>Кассир: Имеются
Кассир->>Покупатель: Подтверждение
Покупатель->>Кассир: Согласие
Кассир->>База данных: Бронирование мест
Кассир->>Принтер: Печать билета
else билеты проданы
База данных->>Кассир: Свободных мест нет
Кассир->>Покупатель: Извините
end
Диаграмма (обратите внимание, что пропали стрелки и непонятно, как идут сообщения. Это давно известная ошибка, которую так и не исправили):
К счастью, в формате mermaid можно редактировать online с сохранением результата в формате SVG, где результат будет выглядеть больше похожим на то, что ожидалось.
Дополнительные материалы
- Примеры на сайте graphviz. При кликании мышкой по графу показывается его код.
- Документация: различные типы узлов
- Документация: различные типы стрелок
- Документация: цветовая палитра