Собственно, этот пост есть коллекция трюков, которая набралась в процессе подготовки двух последних конференций. Проблема в том, что нужно вместить много данных в небольшой график, и делается это порой весьма нетривиальным образом. Как известно, с помощью gnuplot можно сделать всё, что угодно, но мало кто знает как именно. Демонстрация чёрной магии в стиле Gnuplot с полным разоблачением ниже.
В инженерии мало наснимать кучу данных или наделать симуляций - важно все эти данные наглядно и красиво представить. Так и самому проще анализировать полученные результаты, и проще убеждать других в их, результатах, полезности. И потом, красиво построенный информативный график смотрится намного лучше (и куда компактнее) унылой таблицы с вереницей цифр.
Следующая по сложности задача - запихнуть побольше данных в график, так как статья не резиновая и часто имеет жёсткие ограничения по объёму. Вот тут-то и начинается чёрная магия.
Если читатель внимательно присмотрится к вертикальным осям слева и справа, то увидит, что эти оси означают разные, хотя и взаимосвязанные, данные. Смысл в том, что два набора данных влияют друг на друга, и в тексте статьи это довольно подробно излагается со ссылками в тексте на график, подсвеченные тем же цветом, что и данные на графике. Так что автор злоупотребляет цветовой раскраской не только в блоге, но и в консервативных научных журналах - не в ущерб содержанию, по возможности.
Теперь серьёзно - вот код для графика:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #! /usr/bin/gnuplot -persist set terminal postscript "NimbusSanL-Regu" eps color enhanced fontfile "/usr/share/texmf-texlive/fonts/type1/urw/helvetic/uhvr8a.pfb" set output "./plots/threshold_sparsity.ps" set key bottom right set xtics 10 set mxtics 10 set ytics 10 set mytics 10 set yrange [60:100] set y2range [60:100] set xrange [10**-5:1] set style line 1 lt 1 pt 6 ps 1.2 set style line 2 lt 1 pt 9 ps 1.4 set logscale x set format x "10^{%L}" set grid xtics ytics mxtics set y2tics 5 set xlabel "Threshold value for the matrix" font "NimbusSanL-Regu,18" set ylabel "Matrix sparsity, \% percentage" font "NimbusSanL-Regu,18" set y2label "Output disturbance attenuation, \% percentage" font "NimbusSanL-Regu,18" plot ".data" using 6:8 title "Percentage of sparsity of the Hessian matrix" with points linestyle 1 lc rgb "blue" axis x1y1 , ".data" using 6:((1- $4/$2)*100) title "Output disturbance attenuation performance, \% of rejection" with points linestyle 2 lc rgb "dark-red" axis x1y2 |
Ключевые для понимания строки:
set y2range [60:100] set y2tics 5set y2label "Output disturbance attenuation, \% percentage" font "NimbusSanL-Regu,18" |
и далее присваиваем оси другую (независимую) подпись set y2label
Обратите внимание на параметры команды на строчках 23-24: мы строим два ряда данных и указываем, какие оси использовать. Сначала это axis x1y1 а потом axis x1y2 для того, чтобы два массива данных строились относительно двух разных осей.
В строительстве подобных графиков нам поможет инструкция set multiplot
в gnuplot. Вот как выглядит код для этого графика:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #! /usr/bin/gnuplot -persist set terminal postscript "NimbusSanL-Regu" eps color enhanced fontfile "/usr/share/texmf-texlive/fonts/type1/urw/helvetic/uhvr8a.pfb" set output "./plots/plotOutputDisturbancePlant.ps" set lmargin 10 set rmargin 2 set grid set bmargin 4 set style line 1 lt 1 pt 5 ps 0.3 set style line 2 lt 2 pt 7 ps 0.5 set style line 3 lt 3 pt 9 ps 0.3 set style line 4 lt 4 pt 11 ps 0.3 set style line 5 lt 5 pt 13 ps 0.3 set xrange [300:600] set multiplot set nokey set size 1,0.35 set xlabel "Sampling instant" set ylabel "Control input" set origin 0.0,0.0 set bmargin 3 set tmargin 0 plot "./data.data" index 23 using 3 title " " with linespoints linestyle 1 lc rgb "red", "./data.data" index 22 using 3 title " " with linespoints linestyle 2 lc rgb "dark-red", "./data.data" index 18 using 3 title " " with linespoints linestyle 3 lc rgb "dark-green", "./data.data" index 14 using 3 title " " with linespoints linestyle 4 lc rgb "blue", "./data.data" index 12 using 3 title " " with linespoints linestyle 5 lc rgb "green" set ylabel "Output disturbance" set size 1,0.3 set origin 0.0,0.39 set xlabel " " set bmargin 0 set tmargin 1 set nokey plot "./data.data" index 23 using 2 title " " with linespoints linestyle 1 lc rgb "red", "./data.data" index 22 using 2 title " " with linespoints linestyle 2 lc rgb "dark-red", "./data.data" index 18 using 2 title " " with linespoints linestyle 3 lc rgb "dark-green", "./data.data" index 14 using 2 title " " with linespoints linestyle 4 lc rgb "blue", "./data.data" index 12 using 2 title " " with linespoints linestyle 5 lc rgb "green" set size 1,0.3 set origin 0.0,0.7 set bmargin 0.1 set xlabel " " set tmargin 1 set ylabel "Residual disturbance" plot "./data.data" index 23 using 1 title " " with linespoints linestyle 1 lc rgb "red", "./data.data" index 22 using 1 title " " with linespoints linestyle 2 lc rgb "dark-red", "./data.data" index 18 using 1 title " " with linespoints linestyle 3 lc rgb "dark-green", "./data.data" index 14 using 1 title " " with linespoints linestyle 4 lc rgb "blue", "./data.data" index 12 using 1 title " " with linespoints linestyle 5 lc rgb "green" set nomultiplot |
До строки 17 всё в общем вполне очевидно - задаём стили для линий разных типов, диапазоны на осях и сетку. Кроме того, строка 2 явно указывает гнуплоту внедрять шрифты, о чём уже была заметка ранее.
Чёрная магия начинается дальше: с помощью инструкци set multiplot на строке 17 мы просим строить gnuplot несколько графиков на одном.
График начинаем строить с конца, то есть с нижнего графика. Для этого мы меняем размер и позицию графика:
на строках 17-22. После этих танцев с бубном строим график:set size 1,0.35 set origin 0.0,0.0 set bmargin 3 set tmargin 0
plot "./data.data" index 23 using 3 title " " with linespoints linestyle 1 lc rgb "red"
из файла данных ./data.data, используя блок с результатами номер 22 (ибо index 22 а нумерация идёт с блока 0), в котором строим 3-ю колонку (using 3) стилем линий linestyle 1 используя красный цвет линии lc rgb "red"
Вообще полезно продумать структуру данных для строительства графиков заранее. Например, размещать данные для разных случаев блоками (ряды данных, отбитых двумя пустыми строками), чтобы использовать возможности gnuplot и особенно инструкции index.График, расположенный в середине (строки 32-43 ), строится точно так же, только:
Ну и наконец верхний график в примере это строки 44-54, после чего выключаем multiplot и скармливаем всё gnuplot для графопостроительства. Всё легко и просто :-)set size 1,0.3 set origin 0.0,0.39
Рецепт подсмотрен на блоге одной суровой японской девушки, и далее будет адаптирован для ещё более мозголомного случая. Но всё по порядку.
Собственно, требуется построить простой график, но чтобы оси были разрывные. Выглядит график вот так:
Код для построения графика, взятый с сайта Gnuplot Surprising, приводится далее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | reset set term png font "Times,15" set output "broken_axes1.png" f(x)=exp(-x)*sin(500*x) #plotting function set yrange [-1:1] #The later two plot have same yrange set sample 400 set tics nomirror set tmargin at screen 0.9 #the later two plots will share this tmargin set bmargin at screen 0.1 #------------------------------------b------ set multiplot #begin multiplot mode #axes broken line set arrow 1 from screen 0.5,0.08 to screen 0.52,0.12 nohead set arrow 2 from screen 0.52,0.08 to screen 0.54,0.12 nohead set arrow 3 from screen 0.5,0.88 to screen 0.52,0.92 nohead set arrow 4 from screen 0.52,0.88 to screen 0.54,0.92 nohead #x,y axis label and title label set label 1 "Time: t(s)" at screen 0.475,0.025 set label 2 "Signal:U(mV)" at screen 0.025,0.44 rotate by 90 set label 3 center "U=exp(-t)sin(500t)" at screen 0.5,0.95 #The left part set border 1+2+4 #the right border is not plotted set lmargin at screen 0.1 #the left-part's location set rmargin at screen 0.51 set xtics 0,0.02,0.08 plot [0:0.1] f(x) w l lt 1 lw 2 notitle #unset the labels and arrows, otherwise they will be plot #for the second time unset label 1 unset label 2 unset label 3 unset arrow 1 unset arrow 2 unset arrow 3 unset arrow 4 #the right part set border 1+4+8 #the left border is not plotted set lmargin at screen 0.53 #the right-part's location set rmargin at screen 0.94 #ytics is not plotted, as the second plot will share it with the first one unset ytics set xtics 0.9,0.02,1.0 plot [0.9:1] f(x) w l lt 1 lw 2 unset multiplot |
Выглядит, конечно, устрашающе. Тем не менее, это тот же самый set multiplot только сбоку. В отличие от предыдущего примера, здесь мы строим левую и правую части графиков отдельно, с разными осями и диапазонами, а потом сшиваем вместе.
Новизна тут в засечках на осях, которые строятся с плачем и рыданиями - реально, это самая сложная и занудная часть графика (строки 13-17):
#axes broken line set arrow 1 from screen 0.5,0.08 to screen 0.52,0.12 nohead set arrow 2 from screen 0.52,0.08 to screen 0.54,0.12 nohead set arrow 3 from screen 0.5,0.88 to screen 0.52,0.92 nohead set arrow 4 from screen 0.52,0.88 to screen 0.54,0.92 nohead |
Далее идёт построение левой части (строки 24-29):
Сложность здесь в том, чтобы угадать с полями ( set lmargin и set rmargin ). У японской девушки всё легко и просто, но если нужно построить что-то посложнее, угадывание может отнять время.
#The left part set border 1+2+4 #the right border is not plotted set lmargin at screen 0.1 #the left-part's location set rmargin at screen 0.51 set xtics 0,0.02,0.08 plot [0:0.1] f(x) w l lt 1 lw 2 notitle
После убираем засечки:
и строим левую часть:#unset the labels and arrows, otherwise they will be plot #for the second time unset label 1 unset label 2 unset label 3 unset arrow 1 unset arrow 2 unset arrow 3 unset arrow 4
Всё, выключаем мультиплот unset multiplot и вставляем эффектный график в статью.#the right part set border 1+4+8 #the left border is not plotted set lmargin at screen 0.53 #the right-part's location set rmargin at screen 0.94 #ytics is not plotted, as the second plot will share it with the first one unset ytics set xtics 0.9,0.02,1.0 plot [0.9:1] f(x) w l lt 1 lw 2
Здесь показано время работы алгоритма для трёх случаев. В одном из случаев (верхний) время распределено очень неравномерно, и нужно не просто отобразить три графика, но и сделать это с разрывными осями, которые к тому же имеют разный масштаб.
Оценивая критически, этот график перегружен материалом, и автор это отлично понимает. Но проблема в том, что сделать эти графики по отдельности невозможно ввиду ограничений на число страниц, а разбить статью на две нельзя (автор в абстракте наобещал золотые горы полгода назад, абстракт рецензенты конференции приняли, поезд ушёл, и в максимум 12 страниц текст и так влезает с диким скрипом). В общем, другого нет у нас пути, в руках у нас винтовка (с).
Код для графика:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #! /usr/bin/gnuplot -persist set terminal postscript "NimbusSanL-Regu" eps color enhanced fontfile "/usr/share/texmf-texlive/fonts/type1/urw/helvetic/uhvr8a.pfb" set output "./decoupled.ps" set lmargin 10 set rmargin 2 set grid set bmargin 4 set multiplot set xrange [0.16:0.22] set yrange [0:2000] set xtics 0.005 set grid set key top left set size 1,0.35 set xlabel "Mean computation time, milliseconds" set origin 0.0,0.0 set bmargin 3 set tmargin 0 plot ".data" using ($2*10**3):1 title "No coupling, DM with 4 actuators" with boxes fs pattern 2 lc rgb "blue" set ylabel "Frequency of occurrences (out of 4000 data points)" set size 1,0.3 set origin 0.0,0.39 set bmargin 0 set tmargin 1 set xtics 0.1 set xrange [0.2:1.7] set yrange [0:130] set xlabel "" set grid set key top left plot ".data" using ($2*10**3):1 title "No coupling, DM with 49 actuators" with boxes fs pattern 1 lc rgb "dark-red" set xtics 0.5 set size 1,0.3 set origin 0.0,0.7 set bmargin 0.1 set tmargin 1 set xrange [0:8] set xlabel "" set ylabel "" set arrow 3 from screen 0.52,0.98 to screen 0.50,0.94 nohead set arrow 4 from screen 0.57,0.98 to screen 0.55,0.94 nohead set arrow 1 from screen 0.52,0.72 to screen 0.50,0.68 nohead set arrow 2 from screen 0.57,0.72 to screen 0.55,0.68 nohead set border 1+2+4 set lmargin at screen 0.1 set rmargin at screen 0.51 set xtics 0.1 set yrange [0:80] plot [0.3:1.5] ".data" using ($2*10**3):1 title "No coupling, DM with 100 actuators" with boxes fs pattern 5 lc rgb "dark-green" unset arrow 1 unset arrow 2 unset arrow 3 unset arrow 4 set border 1+4+8 set lmargin at screen 0.56 set rmargin at screen 0.975 set nokey set xtics 0.05 set yrange [0:1000] plot [7:7.5] ".data" using ($2*10**3):1 title " " with boxes fs pattern 5 lc rgb "dark-green" set nomultiplot |
Собственно, здесь объёдинены два предыдущих примера. Только вместо точек строятся гистограммы, на что намекает строчка with boxes fs pattern 5 для команды plot.
Строчки 45-52 прямо вытащены из примера про разрывные оси.
Обращаю внимание на то, что в коде наличествуют странные на первый взгляд инструкции вида set xlabel " " они тут совершенно не случайно: при таком строительстве графиков подписи на осях будут красть ценное место, так что лучше спроектировать график так, чтобы оси были одна под другой.
Выражение
plot [7:7.5] ".data" using ($2*10**3):1
тоже требует пояснений: это на самом деле один из самых простых примеров пред-обработки данных перед строительством графика. Здесь мы просим умножить данные в столбце 2 в тысячу раз: $2*10**3 . Если приглядеться графику, то ось Х содержит данные в милисекундах, но сняты данные были в долях секунды. Удобнее умножить их в гнуплоте, чем пытаться переделать потом.
Усвоенный урок лично автором этих строк: такие мозговыносящие графики стоит создавать поэтапно - сначала нижний график, потом средний, потом верхний. Так проще отлавливать ошибки в инструкциях гнуплоту.
Заключение
У автора
На самом деле, такие графики довольно удобны: вместо того, чтобы городить десяток графиков, пугая и сбивая с толку читателя, проще и лучше сделать один напичканный данными график, а потом по ходу текста его со смаком обсуждать. К тому же, такой упиханный данными до зубов график позволяет сэкономить место, сохранив при этом читабельность статьи.
Огромное количество разобранных примеров можно найти на замечательном блоге Gnuplot Surprising. Блог на английском, и ведёт его японская девушка, которая с помощью гнуплота умеет делать разрывные оси, отображать статистику на графиках, использовать прозрачность, и вообще удалять гланды автогеном через отверстия, о которых вы даже не подозреваете :-)
Но ещё дальше пошёл суровый Zoltán Vörös aka Gnuplotter. В своём блоге Gnuplot tricks и авторским сборником Impossible gnuplot graphs он
Раскраска кода выполнена с помощью веб-сервиса http://hilite.me/ который, оказывается, знает и гнуплот.
12 комментариев: |высказаться!| RSS-лента дискуссии.|
После прочтения этой статьи у меня похоже тоже "...окончательно двинулась крыша на почве гнуплота....". Однозначно в memories.
Кстати, вопрос на засыпку: вот как Вы нашли вышеуказанные блоги. Я сколько не искал - не получается найти что-либо стоящее. И вообще, на разные блоги я вообще случайно прихожу по ссылкам на форумах, например... :(
Что тут скажешь? Gnuplot прекрасен! Мне так и захотелось парочку графиков в нём замутить.
Автор тут.
@Basil Orlov комментирует...
После прочтения этой статьи у меня похоже тоже "...окончательно двинулась крыша на почве гнуплота....".
В общем да, автор что-то в самом деле перебарщивает с грибами :-)
Серьёзно: гнуплот может (почти) всё, и самое сложное - найти, как гнуплоту объяснить, что нужно построить.
Кстати, вопрос на засыпку: вот как Вы нашли вышеуказанные блоги.
Басиль, ты не поверишь! ;-)
А вообще стараюсь искать на stackexchange - там много дельного предлагают. В других постах хожу по ссылкам, и часто попадаю на то, что искал. В комментариях, если их пролистать, встречаются дельные ссылки (в том числе и в этом блоге, кстати).
Я сколько не искал - не получается найти что-либо стоящее.
Мусора стало определённо больше. Повылезали индийские капитаны очевидности, чтоб им пусто было. Дорвеи, сеошники, прочая нечисть - всё это загаживает поисковую выдачу.
@iv_vl комментирует...
Что тут скажешь? Gnuplot прекрасен!
Он действительно хорош - один из немногих продуктов опенсорца, которым я пользуюсь с удовольствием. Это тот самый опенсорц, когда разработчики на самом деле пользуются своим продуктом, а не издеваются над пользователями.
Ещё один продукт, про который тут скоро будет пара постов, коим я в последнее время пользуюсь всё более, называется Tcl. Дошёл до того, что подключил его к SQLite и дёргаю оттуда скриптом данные, конвертируя их в латеховские списки через pandoc.
[b]Басиль, ты не поверишь! ;-)
А вообще стараюсь искать на stackexchange - там много дельного предлагают. В других постах хожу по ссылкам, и часто попадаю на то, что искал. В комментариях, если их пролистать, встречаются дельные ссылки (в том числе и в этом блоге, кстати).
Мусора стало определённо больше. Повылезали индийские капитаны очевидности, чтоб им пусто было. Дорвеи, сеошники, прочая нечисть - всё это загаживает поисковую выдачу.[/b]
Я с этого блога уже много где побывал. Кстати и вышел я на него из googl'a, так что хоть немного, но гуглом пользуюсь :)
Я имел в виду как раз море пустой информации в поисковой выдаче. Порой чтобы найти что-то стоящее в поисковике приходится сидеть несколько дней, что изрядно напрягает. Учитывая, что по некоторым вопросам я совсем ничего не знаю - этот труд титанический. Я просто думал, что есть какие-то хитрости при составлении запросов, дабы отсечь хотя бы сразу СЕО-шный мусор (ну или хотя бы часть)... За наводку на Stack Exchange спасибо.
@Basil Orlov комментирует...
Я имел в виду как раз море пустой информации в поисковой выдаче.
Это проблема, да. Некоторое время назад гугель позволял блокировать пользователю сайты из выдачи. Но они эту лавочку прикрыли.
Я просто думал, что есть какие-то хитрости при составлении запросов,
Ну, по гнуплоту (возвращаясь к топику) можно найти не так уж много мусора - но да, топик специфический. Я постоянно пользуюсь advanced search в гугле - там можно задать ключевые слова, которые должны встречаться в определённом порядке. Помогает.
Ну и трюки: если ищещь книжку в PDF, то я лично делаю запрос вида "Пупкин Об Истории булшита PDF Mb". То есть я хочу, чтобы в выдаче были упоминания о размере PDF-файла - больше шансов, что на странице есть ссылки для качания.
За наводку на Stack Exchange спасибо.
Всегда пожалуйста. Самая маковка в стэкэксчендж - возможность downvote капитанов очевидностей.
Кстати, о птичках. Конкретно по Zoltán Vörös aka Gnuplotter нашёл ещё один его сайт: http://www.gnuplotting.org
Там как для начинающих, я так понял, так и для хардкора (ссылки как раз и ведут на его блог на blogspot и т.п.) Последняя запись датирована 22-м августа сего года.
Я "добавил" подсветку синтаксиса гнуплота в geany, сохраняю файлы с командами gnuplot с расширением .graph и сделал, чтобы их geany воспринимала как sh-скрипт.
А есть ли другие способы подсвечивать синтаксис? Или существует текстовые редакторы где уже все добавлено?
@neon1ks комментирует...
Я "добавил" подсветку синтаксиса гнуплота в geany ..
А есть ли другие способы подсвечивать синтаксис?
Отличный вопрос!
Редактор, которым пользуюсь я (Kate), до некоторой степени подсвечивает синтаксис гнуплота. В Kate можно воткнуть свою подсветку - вот пост про то, как подсвечивать markdown.
>А есть ли другие способы подсвечивать синтаксис?
>Или существует текстовые редакторы где уже все добавлено?
Для gnuplot'a более или менее подходит подсветка для Bash Script. По крайней мере та, которая используется в KWrite.
Интересно, в maxima с гнуплотом подобные трюки пройдут? Кто-нить пробовал?
Прекрасная информация, спасибо автору.
Проблему с выводом части данных из одной колонки таблицы решил. Так что спасибо!
Отправить комментарий