1/11/2010

GraphViz: примеры создания графов и взаимосвязей

Есть много типов данных, которые можно и нужно представить наглядно в виде зависимости одного от другого. Это и генеалогическое дерево, и взаимосвязь проектов друг с другом, и многое другое. Это всё можно вручную отрисовывать в Inkscape, а можно последовать UNIX-way и доверить это GraphViz. Немного своих находок и зарубок на память - пример возможностей GraphViz.


Предыстория
В общем, GraphViz это язык управления взаимосвязями - не нужно их чертить руками, их нужно логически описать на языке программирования и доверить работу по вычерчиванию графов GraphViz. В первый раз я начал задумываться об этом тогда, когда мне пришлось построить взаимосвязи меду своими проектами - было подспудное ощущение, что я что-то пропустил и нужно создать ещё проекты.

Сначала я использовал Kmindmap, но его возможности по построению множественных взаимосвязей на разных уровнях очень ограничены. Inkscape это, конечно, ручная работа, что при числе проектов более трёх десятков совсем не весело. И хотелось бы всё это интегрировать с LaTeX... После недолгих скитаний я понял, что изучение
GraphViz сулит много хорошего и не так сложен в освоении.

GraphViz вообще пригоден для всего, что только имеет смысл представлять в виде графов - любые процессы, сети и взаимосвязи. В сочетании с любым скриптовым языком это прекрасный инструмент для анализа и визуализации практически любой информации.

Граф при визуализации может быть представлен как иерархически (dot), так и по алгоритму минимизации энергии (energy minimized), когда узлы располагаются максимально компактно, насколько это возможно, не нарушая наглядность (neato). В общем, если информацию можно представить в виде направленного или ненаправленного графа, GraphViz с этим легко справится.


Установка и первые шаги с GraphViz
GraphViz есть в каждом уважающем себя дистрибутиве, и ставится пакетной системой на раз:
# aptitude install graphviz
Единственное, что следует отметить, что GraphViz имеет кучу зависимостей, так что его сборку лучше доверить вашим дистростроителям.

Но допустим, что вы его успешно собрали или просто поставили из пакетного менеджера своего дистрибутива - что дальше? А дальше делаем быстрый старт: открываем свой любимый текстовый редактор и вставляем в него например вот это:
digraph G{
Рождение->Юность->Зрелость->Старость->Смерть;
Юность->Смерть;
Зрелость->Смерть;
}
И сохраняем в виде текстового файла, например temp.dot. Далее в консоли пишем
$ dot -Tpng temp.dot -o temp.png
То есть перегоняем наш граф в формат PNG, чтобы можно было просмотреть его, скажем, с помощью gqview или любым другим просмотрщиком изображений. И вот что мы увидим:
Начальную идею это должно передать, а за подробностями милости просим в это совершенно исключительное по художественной силе описание
GraphViz. А я тем временем поделюсь своими заметками.

Небольшой пример
В этом примере я сгруппировал наиболее часто мной используемые находки в GraphViz, такие как уровни в графе, размер текста и форму элементов. Дабы минимизировать проблемы с кириллицей, стоит сохранять данные в кодировке UTF8.

Предварительные обозначения
Сначала эти уровни обозначаем (в данном случае это от 50.000 до 10.000 - читавшие GTD меня поймут, не читавшие могут узнать об этом больше).
{ //Уровни в проектах
node[shape=plaintext]; // чтобы было не видно рамок
edge[color=white]; // чтобы было не видно рамок
50.000->40.000->30.000->20.000->10.000->5.000;
}
Последовательность символов -> означает связь двух объектов, shape - форму объекта (рамка вокруг текста), color - соответственно цвет (здесь белый - чтобы не было видно рамки вокруг текста).

После этого группируем данные и даём понять GraphViz, что группа принадлежит к этому уровню:
{ // <============= Задачи на уровне 10.000 ==============
node[shape=rectangle,fontsize=10]; // порядок важен: описание свойств узлов должно идти раньше, чем сами узлы
rank=same; 10.000; "Диссертация";
"Проект РНП";
"Time-Frequency";
}
Здесь задаём размер текста fontsize поменьше (из эстетических соображений) и говорим GraphViz, что эти куски текста должны быть на высоте 10.000 (определённый уровень).

Теперь выстраиваем взаимосвязи объектов друг с другом:
"Time-Frequency"-> "Научные\nпроекты";
"Диссертация"-> "Научные\nпроекты";
"Проект РНП"-> "Научные\nпроекты";

"Диссертация"->"Time-Frequency"[dir=both]; // зависимость направлена в обе стороны
"Диссертация"->"Проект РНП"[dir=both]; // зависимость направлена в обе стороны
Тут две тонкости:
  1. чтобы текст в рамке разбить на несколько строк, ставим \n
  2. если нужно, чтобы стрелка была направлена в обе стороны - взаимная зависимость двух объектов - нужно рядом с зависимостью приписать [dir=both];
После этого можно собирать проект и перегонять его в любой удобный формат: PNG удобнее всего смотреть в просмотрщике, пока выстраивается структура, а потом перегнать это всё в SVG для печати или встраивания куда-нибудь.


А теперь всё вместе
Как только выставленные связи вас устроили и вы хотите посмотреть, что получилось - собираем проект и припадаем к просмотрщику, любуясь результатами своей деятельности. Вот полный код примера:

digraph WorkAnalysis{
rankdir=TB; //как делается граф - сверху вниз.

{ //Уровни в проектах
node[shape=plaintext]; // чтобы было не видно рамок
edge[color=white]; // чтобы было не видно рамок

50.000->40.000->30.000->20.000->10.000;
}

{ // <============= Задачи на уровне 40.000 ============== node[shape=pentagon,fontsize=30]; rank=same; 40.000; "Исследования\nсигналов"; } { // <============= Задачи на уровне 20.000: Сфера ответственности node[shape=egg,fontsize=20]; rank=same; 20.000; "Научные\nпроекты"; } { // <============= Задачи на уровне 10.000 ============== node[shape=rectangle,fontsize=10]; rank=same; 10.000; "Диссертация"; "Проект РНП"; "Time-Frequency"; } "Time-Frequency"-> "Научные\nпроекты";

"Диссертация"-> "Научные\nпроекты";

"Проект РНП"-> "Научные\nпроекты";

"Диссертация"->"Time-Frequency"[dir=both]; // зависимость направлена в обе стороны

"Диссертация"->"Проект РНП"[dir=both]; // зависимость направлена в обе стороны
}
Вставляем его в файл a.dot и набираем:
dot -Tpng a.dot -o a.png
Видим что-то вроде этого:

Собственно, этот пример я состряпал на основе своего графа проектов, и этот код - только часть. Приведённый пример должен проиллюстрировать основные моменты работы с
GraphViz, которые я часто забываю :-)

20 комментариев:

  1. Приветы!

    Спросонья придраться могу только к трём местам:

    изучение GraphViz сулит много хорошего и не так сложен в изучении
    Не очень красиво вышло, но можно обойтись малой кровью, убрав слово «изучение». Также для пущей литературности можно добавить в конец «как кажется».

    GraphViz имеет кучу зависимостей
    aptitude show убеждает меня, что зависимостей не так уж много. Или ты имеешь в виду зависимости при сборке из сорцов?

    А ещё мне кажется, что в последнем листинге не хватает переводов строк — блок когда с задачами скомкан в единый комментарий. dot это парсить отказывается. Добавил newline'ы — получил картинку, как у тебя. Красотища!

    Кстати, поздравляю с переходом на aptitude. И, наверное, после твоего упоминания раздобуду себе экземлярчик GTD и почитаю.

    P.S. Мыло проверь, пожалуйста.

    ОтветитьУдалить
  2. А как быть с формулами, набранными в LaTeX? Умеет работать? Можно ли использовать TeX-шрифты?

    ОтветитьУдалить
  3. Спасибо за наглядное описание - я как раз давно искал похожую программу!

    ОтветитьУдалить
  4. @Minoru, 11.01.2010 8:25:00
    Спросонья придраться могу только к трём местам:
    Твой глаз зоркий, как у орла, даже спросонья. :-)

    изучение GraphViz сулит много хорошего и не так сложен в изучении
    Переперчил, да. Заменил на освоение.

    GraphViz имеет кучу зависимостей
    Имелось в виду при сборке - у меня была шальная мысль собрать самому его в пакет. После просмотра того, что он хочет в девелопмент-пакетах....ээээ... плюнул я на это :-)

    А ещё мне кажется, что в последнем листинге не хватает переводов строк
    Движок блоггера иногда меня доводит до бешенства - переводы строк там были, но он их сожрал. Переводы строк он признаёт только свои, рассово-православные, сделанные в блоггеровском же редакторе. Поправлено.

    Кстати, поздравляю с переходом на aptitude.
    Дык а это, Санчес, я уж давно тама :-)

    И, наверное, после твоего упоминания раздобуду себе экземлярчик GTD и почитаю.
    А вот это, кстати, крайне рекомендую. Самая толковая книга, прочитанная мной за последние лет 5. Не шучу. Я тут в посте, кстати, проболтался насчёт GTD - в планах серия статей по этому делу. Реализовал всё в LaTeX с гарниром Inkscape, и посыпано сверху GraphViz. Бон аппетит, да :-)

    P.S. Мыло проверь, пожалуйста.
    Ничего там не нашёл - шли на virens яндексру.


    @Михаил Саушкин, 11.01.2010 12:41:00

    А как быть с формулами, набранными в LaTeX? Умеет работать?
    Насколько я понял, нет.

    Можно ли использовать TeX-шрифты?
    Вроде можно, но я до этого не дошёл.


    @lizardie, 11.01.2010 14:49:00
    Спасибо за наглядное описание - я как раз давно искал похожую программу!
    Пожалуйста. Она единственная в своём роде, и по мощи ей равных особо не видно. Это как gnuplot - старый, но очень мощных бронтозавр :-)

    ОтветитьУдалить
  5. А как быть с формулами, набранными в LaTeX? Умеет работать?
    Есть скрипт dot2tex, который позволяет получать диаграммы в pdf через latex.

    ОтветитьУдалить
  6. Для GTD, по-моему, проще всего использовать технику mind map ну и, соответственно, ПО вроде Xmind http://www.xmind.net/.

    ОтветитьУдалить
  7. И хотелось бы всё это интегрировать с LaTeX...

    Из статьи не понял, можно ли её с Латехом интегрировать или не нужно.

    ОтветитьУдалить
  8. Спасибо, дядя! Сразу видно - подход профи. Не то что с git vs. svn))

    ОтветитьУдалить
  9. Камрад, а как узлы графа задать опеределённой формы? При изображении сетей связи уж очень удобно было бы, но там стандарты, например АТСЭ изображается кругом с треугольником внутри! Есть идеи?

    ОтветитьУдалить
  10. 1> можно ли её с Латехом интегрировать или не нужно

    Можно!
    Вариантов как минимум два:
    а) делать вывод в PostScript/PDF/... и результать внедрять в LaTeX как векторную картинку;
    б) использовать LaTeX'овый пакет dot2texi(sources, example), который в свою очередь опирается на пакет tikz и на скрипт dot2tex (about, examples) да и вообще, на http://www.fauskes.net/ много чего есть интересного ещё на тему внедрения схем в LaTeX.

    Способ (б) хорош только при небольшом (до десяти) простых схем, так как при каждой сборке рендеряться они заново, что занимает немалое время. Если схем много, то способ (а) будет куда быстрее (но придётся написать makefile).

    2> Можно ли использовать TeX-шрифты?
    Вот HTML точно можно (тут). Вполне вероятно, что уже этого может хватить?

    3>перегоняем наш граф в формат PNG, чтобы можно было просмотреть его

    Есть способ проще, быстрее и веселее! Если набратт dot -T_, то можно увидеть поддерживаемые форматы вывода. Особенно интересны такие как xlib и gtk. В обоих случаях запускается приложение с GUI. Если указан xlib, то приложение использует средства X11 для вывода графа, поддерживает масштабирование/перемещение. Кроме этого ещё и подписывается на inotify, чтобы перерисовать картинку при изменении файла!! Если указан gtk, то имеются те же zoom/move, но вместо inotify здесь меню с возможностью open/save (ещё есть подменю Edit, но оно не реализовано). Используйте каждый на свой вкус. (Обнаружил, что в ряде дистров graphviz поставляется без этих форматов вывода, поэтому можно пошуршать в репозиториях или собрать из исходников с нужными --enable-* у configure.)

    ?>как сделать подпись не внутри кружочка, а сбоку?
    Явно -- никак. В списках рассылки предлагают решение: создать стрелку-петлю прозрачного цвета с подписью.

    ОтветитьУдалить
  11. >а как узлы графа задать опеределённой формы

    Graphviz custom shapes

    ОтветитьУдалить
  12. Здравствуйте, Михаил. Изучил пресс-релиз Вашей диссертации. Я всегда полагал, что при "размытии" (и вообще при свёртке) часть информации теряется, и восстановить её не удастся. Вы же, насколько я понял, пытаетесь опровергнуть этот тезис. Можете привести количественные соображения, подтверждающие возможность восстановления? Ваша рукопись готова? Я бы с радостью почитал. Вы уже защитились? Простите за большое количество вопросов...

    ОтветитьУдалить
  13. 2>Kirikaza - можно ли её с Латехом интегрировать или не нужно?
    конкретно с Латехом можно, но не нужно, имхо проще рисовать с pgf/TikZ, примеры http://www.texample.net/ например такая диаграмма http://www.texample.net/tikz/examples/tcp-state-machine/

    ОтветитьУдалить
  14. @ Алексей Плутахин пишет...
    Для GTD, по-моему, проще всего использовать технику mind map
    Я использую GraphViz для построения зависимостей между проектами на разных уровнях.

    @ Анонимный пишет...
    Из статьи не понял, можно ли её с Латехом интегрировать или не нужно.
    Скажем так, статья это не предполагает :-)

    @ Анонимный пишет...
    Спасибо, дядя! Сразу видно - подход профи.
    Без базара! Стараемся! :-)

    @ Kirikaza пишет...
    Можно!
    Вариантов как минимум два:

    Спасибо за ценное дополнение! Постараюсь внести это в пост.

    @ Vladimir пишет...
    Здравствуйте, Михаил. Изучил пресс-релиз Вашей диссертации.
    Я его писал в расчёте на ненаучную аудиторию, соответственно, были допущены упрощения.

    Я всегда полагал, что при "размытии" (и вообще при свёртке) часть информации теряется, и восстановить её не удастся.
    Да, это так. В примере с глубиной резкости это достаётся снижением сигнал\шума. В случае с шифрованием всё то же самое.


    Вы же, насколько я понял, пытаетесь опровергнуть этот тезис.
    Наверное, я не правильно выразил свою мысль в пресс-релизе: восстановленное (декодированное) изображение хуже оригинала (и это понятно: свёртка регистрируется оптически).

    Можете привести количественные соображения, подтверждающие возможность восстановления?
    В комментарии будет мало места. Если кратко - отличие по СКО между восстановленным и оригинальным изображением около 0.30 - то есть довольно значительное. Но идентифицировать можно.

    Ваша рукопись готова?
    На мой взгляд да, но пока не защищу - нет :-)

    Вы уже защитились?
    Если бы. Тут всё очень забюрократизировано, так что защита не раньше конца февраля.

    Если что - пишите на мой рабочий адрес konnik-собака-pico.mephi.ru
    Постараюсь ответить, но быстро ответить не обещаю.

    @ Анонимный пишет...
    конкретно с Латехом можно, но не нужно, имхо проще рисовать с pgf/TikZ,
    Спасибо за ссылки, добавлю в пост.

    ОтветитьУдалить
  15. Здравствуйте))
    Спасибо за пособие - классно написано!
    Только вот назрел вопрос: как неориентированные графы рисовать?

    ОтветитьУдалить
  16. Добрый день!
    С помощью Xenu определил структуру сайта. В программе есть экспорт в gv-файл. При попытке построить картинку в GraphViz получатся вот что: http://i10.fastpic.ru/big/2010/1002/4b/8bda3245d2372262d5d20755f1a0e14b.png

    Подскажите, что надо добавить в код или какие настройки изменить чтобы график был большим и читаемым?
    Спасибо.

    ОтветитьУдалить
  17. Можно использовать для отображения динамических данных? Типа нарисовать схему и поверх нее отображать данные телемеханики, к примеру? Типа схемы электрической сети, чтобы картинка обновлялась сама по мере поступления данных.

    ОтветитьУдалить
  18. Хочу где-то выразить своё негодование over9000!!!111
    Три часа пытаюсь запустить на винде эту разработку адских гномов с русским языком - и всё даром.
    Не подхватывает оно у меня русский текст, хоть ты тресни!
    Аргх!

    ОтветитьУдалить
  19. требуется формат исходного файла UTF8.
    я тоже часа 3 бился чтобы победить русские крокозябры на винде.
    в конце концов бросил dotty, так и не заставив его отображать корректно, а вот dot в png нормально конвертирует.

    ОтветитьУдалить
  20. Есть ли желающие заиметь DOT редактор графов?

    Занимаюсь разработкой прототипа пока, может кому будет полезен?

    ОтветитьУдалить