データを標準出力からそのままグラフ化したい
シェル芸に長けた仕事の仕方をしてる人は往々にしてそういう状況に巡り合いますよね?
私はあります。特に環境が整っていない組込系の仕事をしていると特に。
(それ以外の人は素直にmatplotlib/seabornでもしてください)
なので、標準入力からデータを受け取ってgnuplotからグラフ化までを行うbash(awk)を作りました。
環境
gnuplot 5.4 patchlevel 0
GNU Awk 5.1.0, API: 3.0 (GNU MPFR 4.1.0, GNU MP 6.2.0)
GNU bash, バージョン 4.4.12(3)-release (x86_64-unknown-cygwin)
グラフ表示するウィンドウはx11を指定していますが、環境によって適当に変更してもらえれば良いと思っています。
使い方
とりあえず表示
$ seq 10 | awk '{print $1,$1**2}'
1 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 81
10 100
$ seq 10 | awk '{print $1,$1**2}' | gplt
optionを使う
一応使えるoptionはhelpで表示されるようにしていますが、メモ書き程度なので、なんとなく使ってみてoption内容を理解するようにしてください。
基本的にはoption指定の後ろに数字なりテキストなりを入れて、どんどん設定入力していくようなイメージになります。
$ gplt --help
[x|y|y2]range,[x|y|y2]r -X X : range setting
[x|y|y2]label,[x|y|y2]l X : label text setting
[x|y|y2]tics,[x|y|y2]t X : scale setting
[x|y|y2]format,[x|y|y2]f X : axis decimal digit num. e.g. X=2->1.00
title,t X : graph title
key,k X : w lp key X. legend label
axis : , u X:X axis. y2axis
font X : font size ratio(default 14)
out : png file output
file X : png file name
size X : png iamge pix size ratio(default:640,480)
例
pngファイルに出力する
$ seq 10 | gplt out
$ ls
001.png
fileでファイル名を指定しなければ001.pngからの連番で作成されていきます。
scriptと絡めて使うと便利です。
X軸のレンジを指定する
$ seq 10 | gplt xrange 0 5
複数データを表示し、第二軸に設定する
$ seq 10 | awk '{print $1,$1*2,$1^2}' | gplt , u 1:3 axis
よく使うのはこのあたりです。
最終的なoutputとしてグラフデータを使う場合はlabel/key/title設定とかもしれ整形したりします。
$ seq 20 | awk '{print $1,$1*2,$1^2,$1/2}' | gplt w lp pt 5 k data1 , u 1:3 axis k y2axis yr 0 50 y2r 0 500 , u 1:4 w p pt 7 ps 4 k data3 xl xaxis yl yaxis t test_graph
コード
内容のほとんど(引数のoption解析)がawkなのでawkでシンタックスハイライトしています。
長いので折りたたみ
#!/bin/bashfilename='tmp_01.dat'optionfile='tmp_02.gp'# 引数無しの場合と場合分けif[$# -eq 0 ]; thencat>$filenamegnuplot-p<<EOFsettermx11font',14'setnokeysetgridplot'$filename'wlppt6EOFrm$filenameelse# help dispif[$1="--help"]||[$1="--h"]||[$1="-h"]||[$1="-help"]||[$1="help"];thenecho"[x|y|y2]range,[x|y|y2]r -X X : range setting"echo"[x|y|y2]label,[x|y|y2]l X : label text setting"echo"[x|y|y2]tics,[x|y|y2]t X : scale setting"echo"[x|y|y2]format,[x|y|y2]f X : axis decimal digit num. e.g. X=2->1.00"echo"title,t X : graph title"echo"key,k X : w lp key X. legend label"echo"axis : , u X:X axis. y2axis"echo"font X : font size ratio(default 14)"echo"out : png file output"echo"file X : png file name"echo"size X : png iamge pix size ratio(default:640,480)"exit;ficat>$filenameop=`echo$@|\awk'\functionadb(s,n1,n2,n3){out=sprintf(s,n1,n2,n3)out}functionad(s,n1,n2,n3){out=outsprintf(s,n1,n2,n3)}functionadop(s,n1,n2){w=wsprintf(s,n1,n2)}functionrange_set(s){min=""if($(i+1)!~/[a-zA-Z,]/)min=$(i+1)max=""if($(i+2)!~/[a-zA-Z,]/)max=$(i+2)gsub("range","",s)gsub("r","",s)ad("set %srange [%s:%s];",s,min,max)}functiontics_set(s){gsub("tics","",s);gsub("t","",s)ad("set %stics %s;",s,$(i+1))}functionlabel_set(s){gsub("label","",s)gsub("l","",s)ad("set %slabel \"%s\" noenhanced;",s,$(i+1))}functionformat_set(s){gsub("format","",s)gsub("f","",s)format[s]=$(i+1)}{fname="001"graph_size=1font_size=1default_font_size=14default_png_width=640default_png_height=480ad("set nokey;")for(i=1;i<=NF;i++){if($i=="title"||$i=="t"){ad("set title \"%s\" noenhanced;",$(i+1))}if($i=="file"){fname=$(i+1)}if($i=="size"){graph_size=$(i+1)}if($i=="xtics"||$i=="xt"||$i=="ytics"||$i=="yt"||$i=="y2tics"||$i=="y2t"){tics_set($i)}if($i=="xrange"||$i=="xr"||$i=="yrange"||$i=="yr"||$i=="y2range"||$i=="y2r"){range_set($i)}if($i=="xlabel"||$i=="xl"||$i=="ylabel"||$i=="yl"||$i=="y2label"||$i=="y2l"){label_set($i)}if($i=="key"||$i=="k"){gsub("set nokey;","",out);adop(" title \"%s\" ",$(i+1))}if($i=="font"){font_size=$(i+1)}if($i=="xformat"||$i=="xf"||$i=="yformat"||$i=="yf"||$i=="y2format"||$i=="y2f"){format_set($i)}if($i==","){gsub("set nokey;","",out);N=split(w,T)t_flag=w_flag=0;for(k=N;k>=1;k--){if(T[k]=="title")t_flag++if(T[k]=="w")w_flag++if(T[k]==",")break}keyn++if(!t_flag)adop(" title \"%d\" ",keyn);if(!w_flag)adop(" w lp pt 6 ")adop(" , \"\" ");}if($i=="axis"){adop(" axis x1y2 ");}if($i=="u"){adop(" u %s ",$(i+1));}if($i=="w"){if($(i+1)=="lp")adop(" w lp pt 6 ");elseif($(i+1)=="p")adop(" w p pt 6 ");elseif($(i+1)=="l")adop(" w l ");}if($i=="pt"){N=split(w,T)flag=0for(k=N;k>1;k--){if(T[k]=="pt"){T[k+1]=$(i+1)flag++break}if(T[k]==",")break}if(flag){w=""for(k=1;k<=N;k++){adop(sprintf(" %s",T[k]));}}}if($i=="ps"||$i=="lt"||$i=="lw"){N=split(w,T)flag=0for(k=N;k>1;k--){if(T[k]=="p"||T[k]=="lp"||T[k]=="l"){flag++break}if(T[k]==",")break}if(flag)adop(" %s %d ",$i,$(i+1))}if($i~/hasen/){N=split(w,T);flag=0for(k=N;k>1;k--){if(T[k]=="lp"||T[k]=="l"){flag++break}if(T[k]==",")break}if(!flag)adop(" w lp pt 6 ")adop(" dt \"-\" ")}}for(iinformat){adb("set format %s \"%%.%df\";",i,format[i])}if(/out/){# exist file checkexst=fname".png"if(getline<exst>0){close(exst);# あったら末尾に_tを追加したファイル名を候補にするexst=sprintf("%s_t.png",fname)# もし数字だけのファイル名なら連番処理を行うif(fname!~/[a-zA-Z]/){while(getline<(exst=sprintf("%03d.png",++fname))>0)close(exst)close(exst)}}adb("set out \"%s\";",exst)adb("set term png size %s, %s font \"Arial, %d\";",int(default_png_width*graph_size),int(default_png_height*graph_size),int(default_font_size*font_size))}if(w!~/ w /){adop(" w lp pt 6 ");}else{S=wnw=gsub(" w ","",S);S=wnc=gsub(" , ","",S);if(nw<nc+1){adop(" w lp pt 6 ")}}if(w~/axis/){ad("set y2tics;")}if(w~/,/){N=split(w,T)flag=0;for(k=N;k>=1;k--){if(T[k]=="title"){flag++break}if(T[k]==",")break}if(!flag)adop(" title \"%d\" ",++keyn);}}END{printout>optfile;printw;}'optfile=$optionfile`gnuplot-p<<EOFsettermx11font',14'load'$optionfile'setgridplot'$filename'$opEOFrm$filename$optionfilefi
おわりに
gnuplotとawkしかないような環境でログデータ分析を行う場合に、これひとつつっこんでおけば分析が捗るような気がします。