Создание графиков в gnuplot: пример построения графика

Согласно официальной документации, gnuplot имеет интерактивный и поточный режим. В интерактивном режиме вы вводите команду за командой, задавая параметры строительства графика. Для изучения возможностей или чтобы построить единственный график это, наверное, нужно, но обычно проще написать скрипт и скармливать его гнуплоту. Об этом далее.

Как в gnuplot построить график
Gnuplot использует скриптовой язык, который описывает строительство графика функции или рядов данных. В скрипте задаются параметры графика: шрифты осей, пределы по осям, расположение легенды. После этого скрипт передаётся по конвейеру гнуплоту, и он выдаст файл PostScript, который и содержит график.

Чтобы быстро посмотреть и, при необходимости, перестроить график, нужно открыть его программой просмотра PS-файлов. Например, kghostview. Её вообще хорошо держать открытой на другом виртуальном рабочем столе, а скрипт построения графика изменять - и при перестраивании график в kghostview автоматически обновится.

Небольшой пример. Имеются ряды данных в обычном текстовом файле, нужно построить график. Вот исходные данные, файл RAWSTDmeasurementresult

1.6593991e+00 1.6523134e+00 1.6407763e+00
1.8986703e+00 1.8667678e+00 1.8595763e+00
2.6304331e+00 2.5340401e+00 2.4999678e+00
4.2843754e+00 4.0227936e+00 4.0423230e+00
7.6136102e+00 7.0438436e+00 7.1057056e+00

Нужно построить цветной график, с подписями на осях по-английски, шрифт Helvetica. Вот что у меня получилось:

#! /usr/bin/gnuplot -persist
set terminal postscript eps enhanced color solid
set output "~/matlab/programs/kmvdecoder/plots/1NoiseRAWSTDtoISOnoisecomparing.ps"
set xlabel "ISO number" font "Helvetica,18"
set bmargin 4
set ylabel "Pixels standard deviation" font "Helvetica,18"
set yrange [0:50]
set key top left
set xtics ("100" 0,"200" 1,"400" 2,"800" 3,"1600" 4)
set style line 1 lt 1 pt 9
set style line 2 lt 3 pt 7
set style line 3 lt 2 pt 5
plot "~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using 1 title "RAW data, Red channel" with linespoints linestyle 1,"~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using 2 title "RAW data, Green channel" with linespoints linestyle 3,"~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using 3 title "RAW data, Blue channel" with linespoints linestyle 2


Ничего сложного в этом нет, сейчас я эти иероглифы прокомментирую.

Даже беглый взгляд на текст при некотором знании английского позволяет догадаться, какие строчки что примерно делают. Понятно, что команда set что-то устанавливает - а устанавливает она параметры построения графика. А команда plot как нетрудно догадаться, что-то строит. Так что всего-то навсего две команды и немного параметров к ним. Не так страшно, как выясняется - для настоящего джигитапользователя никс-систем это не должно быть проблемой.


Пример скрипта построения графика на gnuplot с пояснениями
Итак, разбираем скрипт.

#! /usr/bin/gnuplot -persist
Это обычный заголовок скриптов, только указывает он на gnuplot а не на, скажем, perl или bash. Если вы когда-нибудь видели скрипты, то сразу почувствуете себя как дома.


set terminal postscript eps enhanced color solid
Устанавливает постскрипт-вывод, расширенный - можно использовать греческие символы, цветной - графики будут цветными.


set output "~/matlab/programs/kmvdecoder/plots/1NoiseRAWSTDtoISOnoisecomparing.ps"
Это путь к будущему графику и имя графика. Можно сваливать их в текущий каталог или куда захотите.


set xlabel "ISO number" font "Helvetica,18"
Подпись по оси Х будет "ISO number", шрифтом Helvetica и размером 18 пунктов.


set bmargin 4
Устанавливаем нижнее поле равное 4 относительным единицам, чтобы не обрезалась подпись к оси Х (этот досадный косяк имеет место быть у меня, у вас его может и не быть).


set ylabel "Pixels standard deviation" font "Helvetica,18"
Подпись по оси Y будет "Pixels standard deviation", шрифтом догадайтесь каким :-)



set yrange [0:50]
Пределы по оси Y составляют от 0 до 50.



set key top left
Легенда (обозначение рядов данных) сверху слева.



set xtics ("100" 0,"200" 1,"400" 2,"800" 3,"1600" 4)
Отсчёты по оси Х будут 100, 200, 400 800 и 1600.


set style line 1 lt 1 pt 9
set style line 2 lt 3 pt 7
set style line 3 lt 2 pt 5
Здесь задаётся номер линии (чтобы на неё сослаться при построении конкретного ряда данных), тип линии и тип точки (квадратик, кружочек, ромбик...).


plot "~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using
1 title "RAW data, Red channel" with linespoints linestyle
1,
"~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using 2
title "RAW data, Green channel" with linespoints linestyle
3,
"~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using 3
title "RAW data, Blue channel" with linespoints linestyle 2
Вся эта конструкция предписывает строить график, который будет состоять из трёх линий (типа 1, 2 и 3). График строится по данным, которые лежат в одном текстовом файле тут: ~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult

Заголовок у каждой ветви графика разный, он задаётся после
title, а слово using означает, что в файле несколько рядов данных: для первой ветви - первый столбик, для второй ветви - второй столбик и так далее.

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

Следует отметить, что, вопреки распространённому заблуждению, gnuplot выдаёт графики с publication-ready качеством, которые без вопросов принимаются в любом зарубежном (и уж тем более местном) научном журнале. Например, NASA с помощью gnuplot создаёт карты погоды, а некоторые математические системы (типа MATLAB) просто используют куски кода gnuplot чтобы отрисовывать графики. Так что график, который выдаст гнуплот, в любом случае на порядок краше того, на что способен ексель или Openoffice.org Calc.

Для тренировки можно немного поиграть с параметрами и посмотреть, к чему это приводит. Смотреть удобнее всего в kghostview или любой другой программе, способной открыть PostScript-файлы.

28 комментариев: |высказаться!| RSS-лента дискуссии.|
Michael комментирует...

В приведённом скрипте два раза встречаются set yrange и set key. Первый из вариантов можно убрать. set encoding нафик не нужен, ибо русские символы не используются. set border - это воплощение Мирового Зла. Для postscript терминала все бордеры лучше выставить в 0, чтобы рабочее поле графика (то, что ограничено рамкой) всегда имело одинаковый размер. Иначе, вставленные в текст один над другим рисунки будут иметь мерзостный вид. BoundingBox слетит, конечно, но gnuplot его всё равно правильно только в самых простых случаях определяет. Я просто делаю постобработку рисунка таким скриптом:

[code]
#!/bin/bash
script='function roundd(i){return i%5==0?i-5:int(i/5)*5}
function roundu(i){return int(i/5)*5+5}
/%%BoundingBox/ {print "%%BoundingBox: " roundd($2) " " roundd($3) " " roundu($4) " " roundu($5)}'

newbbox=`gs -dBATCH -sDEVICE=bbox -dNOPAUSE -q $1 2>&1 | gawk "$script"`
script="BEGIN {com=1}
{if(!com) print}
!/%/ {if(com) print \"$newbbox\"; com=0}
/%/ {if(com) {if(\$1==\"%%BoundingBox:\") {print \"$newbbox\"; com=0} else print}}"
mv $1 temp
gawk "$script" temp>$1
rm temp
[/code]

vyazovoi комментирует...

Как всегда четкое руководство к действию, спасибо.
Я даже жалею что мне построение графиков никаким боком как программисту и не студенту ненужно, очень люблю применять такие гикнутые штучки на практике.

virens комментирует...

2 Michael пишет...
В приведённом скрипте два раза встречаются set yrange и set key. Первый из вариантов можно убрать.
У меня часто эти значения переопределяются в зависимости от графиков. Взял рабочий экземпляр. Но для наглядности ущербно, согласен, поправил, спасибо.

set encoding нафик не нужен, ибо русские символы не используются.
Здесь - да, но на всякий случай ставлю.

set border - это воплощение Мирового Зла.
Бордер ставлю только снизу, иначе подписи обрезаются. Другого способа не знаю. Твой код попробую.


Я просто делаю постобработку рисунка таким скриптом
Спасибо, обязательно попробую!

2 vyazovoi пишет...
Как всегда четкое руководство к действию, спасибо.
Я ж обещал про гнуплот написать :-)
На самом деле, я таким образом заставил себя прочитать к нему документацию. И мне это очень помогло.

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

Michael комментирует...

Бордер ставлю только снизу, иначе подписи обрезаются. Другого способа не знаю. Твой код попробую.


В первом посте ошибся, имел в виду bmargin.

Немного о том, как работают эти самые margin'ы. Дело в том, что гнуплот интерпретирует set size (в том числе и дефолтный) как размер всего рисунка, а не только области рисования. Размер области рисования рассчитывается, исходя из предполагаемых размеров подписей тиков и подписей осей. Правильно эта вещь рассчитывается только в самом тривиальном случае, поэтому иногда необходимо указывать гнуплоту явно расстояние между областью рисования и границей рисунка. В вашем случае, неправильное определение границ связано, по-видимому, с тем, что гнуплот рассчитывает значение поля исходя из дефолтного размера шрифта (14, вроде бы), а не явно указанного вами 18. Получается заниженный результат, что приводит к обрезке. Для терминалов типа png это было бы критично, однако для постскрипта - нет, потому что для него размер рисунка - это просто BoundingBox и ничего более. Реальной обрезки там не делается. BoudingBox легко поправить руками или предложенным скриптом. Кстати, если будете им пользоваться, добавьте гнуплоту команду set origin 0.2, 0.2, иначе иногда рисунок уходит в отрицательные значения PostScript-координат и скрипту рвёт крышу.

Кстати, какой версией гнуплота пользуетесь?

Анонимный комментирует...

для того чтобы не повторять имя файла несколько раз можно писать так

plot "~/matlab/programs/kmvdecoder/plots/RAWSTDmeasurementresult" using
1 title "RAW data, Red channel" with linespoints linestyle 1,
'' u 2 t "RAW data, Green channel" with linespoints linestyle 3,
'' u 3 t "RAW data, Blue channel" with linespoints linestyle 2

virens комментирует...

2 Michael пишет...
В первом посте ошибся, имел в виду bmargin.
Я так и понял. Нашёл такой финт по другому поводу, и решил применить у себя, т.к. задолбало обрезание подписей снизу.

Немного о том, как работают эти самые margin'ы.
Спасибо, интересно. Не возражаешь, если включу фрагменты этого ответа в пост?

В вашем случае, неправильное определение границ связано, по-видимому, с тем, что гнуплот рассчитывает значение поля исходя из дефолтного размера шрифта (14, вроде бы), а не явно указанного вами 18.
Ко мне лучше на "ты", а то чувствую себя академиком с тросточкой :-)

Насчёт шрифта: странно, что гнуплот не учитывает тот шрифт, что я явно указываю.

BoudingBox легко поправить руками или предложенным скриптом.
Только скрипты: у меня счёт графикам идёт на десятки и сотни.

Кстати, какой версией гнуплота пользуетесь?
Той, что в etch. У меня кодировка КОИ8, мне большего и не надо.

2 Анонимный Анонимный пишет...
для того чтобы не повторять имя файла несколько раз можно писать так
О, спасибо, надо будет применить!

Michael комментирует...

Спасибо, интересно. Не возражаешь, если включу фрагменты этого ответа в пост?
Включай, конечно.


Ко мне лучше на "ты", а то чувствую себя академиком с тросточкой :-)

Понял :-)


Насчёт шрифта: странно, что гнуплот не учитывает тот шрифт, что я явно указываю.

Третья ветка не учитывает точно. Четвёртая - фиг знает, вроде что-то там шаманили. А какая версия в etch? Я просто не дебианщик.

virens комментирует...

2 Michael пишет...
Четвёртая - фиг знает, вроде что-то там шаманили. А какая версия в etch?
Версия Version 4.0 patchlevel 0

Меня она всем устраивает и менять её пока не планирую. Руководствуюсь старым принципом "не сломалось - не чини". :-)

Yuriy Volkov комментирует...

хай

set key top left
Легенда (обозначение рядов данных) сверху справа.

наверное сверху слева? ;-)

Михаил Саушкин комментирует...

Как я понял, по умолчаию все графики (рисунки) получаются одного размера 127 x 89 мм (кажется так, точно не помню).
Вопрос: Можно ли задать другой размер рисунка? Если да, то как?

Михаил Саушкин комментирует...

Вопрос снят, разобрался. Спасибо

Анонимный комментирует...

График в gnuplot:
splot [80:200] [50:160] x>y ? (sqrt((0.1*y+337000**2/((x-y)*625*10**4))**2+4*0.45*(0.55*y**2+x*337000**2/((x-y)*625*10**4)))-(0.1*y+337000**2/((x-y)*625*10**4)))/(2*0.45) : 1/0

Далее тоже в калькуляторе при x=200, y=160:
(sqrt((0,1*160+337000^2/((200-160)*625*10^4))^2+4*0,45*(0,55*160^2+200*337000^2/((200-160)*625*10^4))) - (0,1*160+337000^2/((200-160)*625*10^4)))/(2*0,45) = 188,966559485

Визуально в программе gnuplot - другое значение функции при этой комбинации x, y.
Попробуйте, посмотрите.
Не могу разобраться, подсобите.

Анонимный комментирует...

А как насчет логарифмических осей? и чтобы засечки на осях корректно отображались?

Владимир

gummybear117 комментирует...

Можно ли текст писать разным шрифтом в одной метке (label)? Например, одну букву выделить курсивом.

gummybear117 комментирует...

Ура, нашёл. Может кому ещё пригодится. Для того, чтобы выделить часть метки другим шрифтом нужно применить такую конструкцию {/font text}. Например, мне нужно было выделить буковку V курсивом в подписе к координате: set xlabel 'Voltage {/Times-Italic V}, Volts'

zhevak комментирует...

Найдена описка:

>> set key top left
>> Легенда (обозначение рядов данных) сверху справа.

слева или справа?

virens комментирует...

@gummybear117 комментирует...
Может кому ещё пригодится.
А как же! Спасибо, Медведь, что поделился. Когда у автора будет чуток больше времени, он это запузырит в пост.


@zhevak комментирует...
Найдена описка:
О, точно! Как же я это. Спасибо, исправлено!

dlinyj комментирует...

У меня назрел вопрос, что-то не могу найти на него найти ответ. Как сделать так, чтобы окно гнуплота не закрывалось после отработки скрипта.

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

pause 1 не очень подходит для этого, т.к. внешняя программа (или скрипт) не знает, что у гнуплота стоит пауза, и продолжает слать комманды. Можете что-нибудь порекомендовать?

virens комментирует...

@dlinyj комментирует...
Как сделать так, чтобы окно гнуплота не закрывалось после отработки скрипта.
Короткий ответ - по ходу, никак. Гнуплот не предназначен для интерактивного строительства графиков.

Долгий ответ: можно попробовать воспользоваться командами multiplot и replot. Можно ещё спросить у автора этого ресурса, он вообще большой дока по гнуплоту.

Поясню, в чём заключается вопрос. Я хочу сделать динамическое отображение графика на экране, т.е. последовательно скармливать гнуплоту разные файлы данных, и чтобы он менял картинку.
Есть костылястое решение: делать графики с последовательными именами в PNG для разных данных, а потом склеить в видеопоследовательность. Я так делаю иллюстрации динамического поведения деформируемых зеркал в своём симуляторе.

И да, отпишитесь, если найдёте решение. Автору тоже интересно.

dlinyj комментирует...

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

Решение первое
http://guidolan.blogspot.com/2009/09/gnuplot-tips-and-tricks-part-1.html

Заполняется файл двумерными данными, с помощью простой комманды:
while true; do echo -ne "$RANDOM\t$RANDOM\n" >>random.dat ; sleep 1; done
Обращаю внимание, что комманда немного отличается от того, что в статье. Строка из статьи у меня не заработала.

И выполняем гнуплотовский скрипт:


#!/usr/bin/gnuplot
plot "random.dat" ps 0.5 pt 7
pause 1
replot
reread


Он будет бесконечно обновляться раз в секунду.

Этот вариант не очень удобен при использовании в программе, т.к. хочется осуществлять обновление по нужде и прекращать работу построения графиков.

Решение второе

Работа из си-программы http://stackoverflow.com/questions/9403474/gnuplot-and-usleep-on-c

Я не буду тут постить весь код, но обращу внимание, что чтобы окно гнуплота не закрывалось, как мне кажется, его надо запустить с параметром "-", как здесь

gp = popen("gnuplot -","w");

Далее по аналогии:


usleep(100000);//задержка
fprintf(gp, "replot \n");//перерисовка


Я даже попробовал перенести эту программу на Nokia n800, работает почти без нареканий. Можно поглядеть тут: http://dlinyj.livejournal.com/577579.html

З.Ы. Специально подробно написал, мало ли кому будет полезно. Возможно стоит это оформить отдельным постом в данном блоге.

iv_vl комментирует...

@dlinyj комментирует...
Я хочу сделать динамическое отображение графика на экране, т.е. последовательно скармливать гнуплоту разные файлы данных, и чтобы он менял картинку.
Использование гнуплота тут не обязательно, есть решение, которое можно применять с любым графопостроителем. Например, я использую Asymptote, даю команду
asy test-1.asy -o botva.eps
При этом файл botva.eps открыт в Evince, который сам перезагружает его содержимое после изменения. Т.е., заменяя test-1 на text-2 и так далее, я увижу последовательно сменяющиеся картинки с результатом.

dlinyj комментирует...

При этом файл botva.eps открыт в Evince, который сам перезагружает его содержимое после изменения.

Я так и делал, но это происходит достаточно медленно. В особенности если точек очень много (больше 10 000). По этому хотелось иметь сразу возможность делать это средствами гнуплота.

Плюс, задача использовать это на n800, т.к. она будет обрабатывать экспериментальные данные на лету.

Igor Voronin комментирует...

А как на андроиде просмотреть график из Гнуплота? Есть мысли? Пост - отличный, я его уже больше года использую как доку ))))

virens комментирует...

@Igor Voronin комментирует...

А как на андроиде просмотреть график из Гнуплота?
Эмм... Игорь, я не думаю, что это классная идея - смартфоны несколько не для этого. В конце концов, это как бы телефон :-)

Тем не менее, есть приложение для андроида, которое может строить и просматривать графики Гнуплота. Я не использовал пока что.

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

dlinyj комментирует...

Вы совершенно не правы. Давно на андройде используется как octave, так и gnuplot http://habrahabr.ru/post/168411/

virens комментирует...

@dlinyj комментирует...

Вы совершенно не правы. Давно на андройде используется как octave, так и gnuplot
Я к тому, что пытаться строить графики на, скажем 4" экране - не то, чтобы слишком удачная идея. Но я не вижу в этом ничего плохого.

Вопрос тов. dlinyj: а как насчёт устойчивости работы \ фичей портированных октавы и гнуплота? Из поста на хабре видно, что "оно таки работает" - а как насчёт "насколько хорошо"?

dlinyj комментирует...

Я же не говорю, что это 100% альтернатива. Другое дело, что в дороге приходит гениальная мысль и её можно легко проверить, достав из широких штанин свой телефон. Да и потом, я постоянно с собой планшет таскаю, а там уже далеко не 4 дюйма.

Анонимный комментирует...

Доброго времени!
На графике есть необходимость отобразить часть данных из одной из колонок таблицы в определенном диапазоне.
Есть таблица записи температуры во времени с нескольких датчиков. И я точно знаю, что один из датчиков корректно работал только какую -то часть времени, а остальные данные - мусор. Я хочу вывести на график температуру со всех датчиков во всём интервале времени, а с неисправного только в том, в котором он работал.
Я умею это делать единственным способом: создаю вторую таблицу из первой, оставляю в ней только корректные данные (срезаю лишние строки до и после), а потом вывожу её на тот же график, при этом из исходной прошу вывести все колонки, кроме неисправной, а из второй добавляю только ту, какую нужно.
А нельзя ли это же сделать указав, например параметры времени, для которого нужно выводить данные из конкретной колонки? Или диапазон значений этой колонки, который нужно отображать (а остальные нет) ?

Отправить комментарий

Подписаться на RSS-ленту комментариев к этому посту.