シェルスクリプト + gnuplot で快適なグラフ作成ライフ

# この記事は？

この記事では、シェルスクリプトと gnuplot を用いてグラフ作成をする方法、及び応用例の紹介をします。
gnuplot 自体の詳しい使い方やインストールについては触れていません。

シェルスクリプトにあまり詳しくなくてもある程度は分かる程度に仕上げました (多分)。
また、本記事では以下を前提に話を進めて行きます。

* gnuplot がインストールされている
* SH シェル (Unix/Linux シェル) が動かせる環境である

# Excel とかでもグラフ描けるけど･･･

Excel 等の表計算ソフトでもグラフは描けます。
決して悪いアプリでは無いですし、グラフの質が悪いこともありません。

ですが、以下のようなシチュエーションのとき、gnuplot + シェルスクリプトは力を発揮します。

* 同じ書式ですぐにグラフを作りたいとき
* いくつものデータのグラフを作るとき
* 外部プログラム等により、頻繁にデータが更新され、再描画の必要があるとき

# シェルスクリプトの実行手順

シェルスクリプトの実行手順を軽く説明。

```sh
# お好みのコードエディタでコードを書く
vim Plot.sh

# 実行権限を付与して実行
chmod +x Plot.sh
./Plot.sh
```

満足いく仕上がりになるまで、スクリプトを編集して実行してと繰り返します。

# シェルスクリプトから gnuplot を動かす

以下は、リダイレクションを用いて gnuplot を動かす方法です。

```Plot.sh
#!/bin/sh

gnuplot << EOF
set terminal png
set output "Plot.png"
plot sin(x)
exit
EOF
```

「`gnuplot`起動 -> 各コマンドを実行」という手動操作と同様の操作ができます。

他にも、`-e`オプションで引数にコマンドを打つ方法、`-c`オプションでコマンドを書いたテキストファイルを読み込む方法がありますが、リダイレクションを用いる方法をオススメします。

# gnuplot 最低限のコマンド

通常、gnuplot のグラフ描画結果はウィンドウに表示されます。
なので、`plot`等だけでは、描画結果が見れずにすぐに gnuplot が終了してしまいます。
そこで、描画結果が画像ファイルに保存されるようにしてやります。

上記の`Plot.sh`は、最低限のコマンドで構成した例です。
各コマンドについて、軽く説明します。

## `set terminal <terminal-type>`。

出力先ターミナルの指定です。
早い話、ここでは出力するファイルフォーマットの設定と思ってもらえば差し支えありません。
PNG、JPEG、GIF、SVG 等のフォーマットを指定できます。
利用可能なフォーマットは、`help set terminal`から確認できます。

## `set output "<File Name>"`

出力ファイル名の指定です。
ダブルクォート`""`で囲んでいないとエラーになります。
また、拡張子は自動では付けてくれないので、出力フォーマットに合わせて拡張子も書きましょう。

## `plot`

文字通り、グラフを描画するコマンドです。
`sin(x)`のような関数を記述、またはデータの書かれたテキストファイルを指定します。
データは、以下のように書かれたテキストファイルを指定できます。
左の列は横軸、右の列は縦軸に反映されます。

```txt
0 2.0
1 4.2
2 1.5
3 6.4
```

`set output`指定後に`plot`を 2回以上実行すると、出力ファイルが変になります。
`set output`1回の後に`plot`1回というような形で組みましょう。
複数のグラフを 1枚に出力するには、関数又はファイル名をコンマ区切りで指定します。

```sh
# 例
plot sin(x), cos(x), "data.csv"
```

## `exit`
gnuplot を終了するコマンドです。
シェルスクリプト (リダイレクション使用) で動かす場合、あっても無くても gnuplot は終了してくれるので良いのですが、一応書いておきます。(作法的なソレとして。)

# シェルスクリプトの力を応用した例

## 入力 / 出力ファイル名指定でプロット

`$1` `$2`と書くと、シェルスクリプト実行時の引数を取得できます。
それを応用し、引数で指定された入力ファイル名と出力ファイル名を反映してプロットしてくれるスクリプトです。
(エラーチェックとかは省いています。)

```Plot.sh
#!/bin/sh

gnuplot << EOF
set terminal png
set output "$2"
# 必要に応じ書式設定を加える
plot "$1"
exit
EOF
```

```sh
./Plot data.txt Plot.png
```

## CSV ファイルをプロット

CSV ファイルは、データがコンマ等で区切られて記述されています。
gnuplot でテキストファイルの数値データをグラフに起こすには、データがスペース区切りで記述されている必要があります。
そこで、`sed`コマンドを用いて、コンマをスペースに置換してグラフ描画をしてくれるようにします。

```Plot.sh
#!/bin/sh

sed -e "s/,/ /g" data.csv > tmp.txt

gnuplot << EOF
set terminal png
set output "Plot.png"
# 必要に応じ書式設定を加える
plot "tmp.txt"
exit
EOF

rm tmp.txt
```

## 最初の行 (カラム) を無視してプロット

初めの行に、例えば`x y`というように、列のカラムが書いてあることもあります。
数字でない行があると、gnuplot にエラーを吐かれます。
1行程度なら手動で取り除いても良いのですが、シェルスクリプトで元ファイルをいじることなく解決してみます。

```Plot.sh
#!/bin/sh

sed -e "1d" data.txt > tmp.txt

gnuplot << EOF
set terminal png
set output "Plot.png"
# 必要に応じ書式設定を加える
plot "tmp.txt"
exit
EOF

rm tmp.txt
```

## 最初の行 (カラム) を軸のラベルにしてプロット

上記について、カラムを破棄ではなくグラフのラベルに自動で入れてくれるように改良してみます。

```Plot.sh
#!/bin/sh

sed -e "1d" data.txt > tmp.txt
col=`sed -n -e "1p" data.txt`
col_x=`echo "$col" | awk '{print $1}'`
col_y=`echo "$col" | awk '{print $2}'

# カラム含め、データがカンマ区切りで記述されていた場合
#sed -e "1d" data.txt | sed -e "s/,/ /g" > tmp.txt
#col=`sed -n -e "1p" data.txt`
#col_x=`echo "$col" | awk -F ',' '{print $1}'`
#col_y=`echo "$col" | awk -F ',' '{print $2}'

gnuplot << EOF
set terminal png
set output "Plot.png"
set xlabel "$col_x"
set ylabel "$col_y"
# 必要に応じ書式設定を加える
plot "tmp.txt"
exit
EOF

rm tmp.txt
```

## 複数のデータをまとめてプロット

`sh`の`for`文と変数内文字列置換を用いて、複数のファイルをそれぞれプロットします。
シェルスクリプトと同一ディレクトリ上の`.txt`ファイル全てが対象になります。

```Plot.sh
#!/bin/sh

for f in *.txt; do
gnuplot << EOF
set terminal png
set output "${f%.*}.png"
# 必要に応じ書式設定を加える
plot "$f"
exit
EOF
done
```

`f=data1.txt`のとき、`${f%.*}` -> `data1`
`f=data2.txt`のとき、`${f%.*}.png` -> `data2.png`