PGFPlotsでキホン的なグラフを書く

> この記事は TeX & LaTeX Advent Calendar 2015 の19日目の記事です。
> 昨日は [VoD](http://qiita.com/VoD/items/addb623ac0ceea78eb3f) さんでした。明日は [hak7a3](http://hak7a3.hatenablog.com/entry/2015/12/20/153805) さんです。

この記事には，PGFPlots，gnuplot，TikZの成分とちょっとだけpgfmath，TeX言語のスパイスが含まれています．

## 解説動画

<iframe width="560" height="315" src="https://www.youtube.com/embed/RfD7qcM3o04" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

## リポジトリ

https://github.com/umireon/umireon-pgfplots-samples

## まえがき

LaTeXで論文を書く場合，PDF形式の高品位なグラフの生成が必須となります[^footnote-noeps]．グラフを生成する代表的なソフトウェアとしては，gnuplotなどが挙げられ，なかでも`pdflatex`や`cairolatex`ドライバにより生成される図の品質は，本文とのフォントの整合性なども勘案しても，極めて高いと考えられます．以下に示すのは，gnuplotの`cairolatex`ドライバにより生成されるグラフの一例です．

![gnuplot-cairolatex.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35580/685d38de-9dff-97d3-ce9e-c96b1205b7b9.png)


上図に示すように，生成されたグラフの見た目には何の問題もありません．しかしながら，同グラフを生成するgnuplotプログラムは，お世辞にも美しいとは言えません．

```gnuplot-cairolatex.gp
set term cairolatex pdf standalone size 8.5cm,6cm linewidth 3
set output "gnuplot-cairolatex.tex"

set border back
set key reverse Left samplen 5 at 38,0.5 spacing 1.2

set log y
set xrange [0:40]; set yrange [1e-5:1]
set xtics 10 scale 2,1; set ytics scale 2,1 add ("1" 1)
set mxtics 2; set mytics 10
set format y "$10^{%L}$"
set xlabel "Avarage CNR [dB]"; set ylabel "Bit Error Rate"

set label 1 "\\footnotesize\\begin{tabular}{l}QPSK\\\\Rayleigh fading\\\\Perfect channel estimation\\end{tabular}" at 2,6e-5 left

plot \
  (1-1/sqrt(1+2/(10**(x/10))))/2 title "Theory" linetype -1, \
  "data.txt" with lines linecolor "red" dashtype (10,10) notitle, \
  "data.txt" with points pointtype 9 linecolor "white" notitle, \
  "data.txt" with points pointtype 8 linecolor "red" notitle, \
  "dummy.txt" with linespoints pointinterval -1 pointtype 8 linecolor "red" dashtype (10,10) title "Simulation"
```

いろいろ言いたいことはありますが，プロットの白抜きを実現するために[^footnote-whiteout]，1つの同じデータに対して4回もプロットを行う必要があるのはいただけません[^footnote-multipleplot]．
そこで，本項では，TeXでグラフ生成できるのPGFPlotsを用いて，きれいなグラフを生成する方法について説明します．

## PGFPlots: まずは作ってみる
PGFPlotsには，サンプルを交えた読みやすいマニュアルが付属しています．普通のTeXの環境であれば，`texdoc pgfplots`でそのマニュアルを閲覧することができます．PGFPlotsのマニュアルは504ページしかなく，初めにチュートリアルがあるというような，初学者に優しい構成となっております．チュートリアルに従いコードを書くことにより，以下に示すようなグラフを簡単に作成することができます[^cite-1]．

![pgfplots-lualatex-simple.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35580/0378d146-93ec-310a-0c16-d56caec10a10.png)


上図のグラフを生成するPGFPlotsプログラムは，以下のようになります．なお，このプログラムは，`gnuplot`にパスを通した状態で，`--shell-escape`付きで`lualatex`か`pdflatex`によりコンパイルする必要があります．`platex`はよした方が良いと思います．

```tex:pgfplots-lualatex-simple.tex
\documentclass[tikz]{standalone}
\usepackage{pgfplots}
\pgfplotsset{
  compat=1.17,
  major tick length=0.2cm,
  minor tick length=0.1cm,
}
\begin{document}
\begin{tikzpicture}
\begin{semilogyaxis}[
  width=85mm, height=65mm,
  domain=0:40,
  xmin=0, xmax=40,
  ymin=1e-5, ymax=1,
  minor x tick num=1,
  xlabel={Avarage CNR $\Gamma$ [dB]},
  ylabel={Bit Error Rate},
  legend entries={Theory,Simulation},
]
  \addplot[smooth] gnuplot {(1-1/sqrt(1+2/(10**(x/10))))/2};
  \addplot[sharp plot,red,mark=triangle*] table {data.txt};
\end{semilogyaxis}
\end{tikzpicture}
\end{document}
```

### プログラムの解説

```tex
\documentclass[tikz]{standalone}
\usepackage{pgfplots}
```

PGFPlotsを用いてPDF画像を生成する旨を宣言します．standaloneパッケージのtikzオプションにより，自動的に余白がいい感じになります[^cite-2]．

---

```tex
\pgfplotsset{
  compat=1.17,
  major tick length=0.2cm,
  minor tick length=0.1cm,
}
```

`compat=1.17`は，PGFPlots 1.17の機能セットを使用することを宣言します[^cite-3]．
また，`major tick length`と`minor tick length`は，それぞれグラフの大目盛りおよび小目盛りの長さを指定しています[^cite-4]．

---

```tex
\begin{semilogyaxis}[...]
  ...
\end{semilogyaxis}
```

`semilogyaxis`環境は，y軸片対数グラフを実現する環境です．同種の環境として，x軸側片対数グラフ を実現する`semilogxaxis`環境，両対数グラフを実現する`loglogaxis`環境，普通のグラフを実現する`axis`環境等が挙げられます[^cite-5]．

---

```tex
  width=85mm, height=65mm,
  domain=0:40,
  xmin=0, xmax=40,
  ymin=1e-5, ymax=1,
  xlabel={Avarage CNR $\Gamma$ [dB]},
  ylabel={Bit Error Rate},
  legend entries={Theory,Simulation},
```

`width`，`height`オプションは，グラフ全体のサイズを指定します[^cite-6]．
`domain`オプションは，後述する関数描画に与える`x`の値の範囲を指定します[^cite-7]．
`xmin`，`xmax`オプションは，x軸方向におけるグラフの描画範囲を指定します．`ymin`，`ymax`オプションは，y軸方向において同様の指定を行います[^cite-8]．
`xlabel`，`ylabel`オプションは，それぞれx軸およびy軸のラベルを指定します．上に示したように，軸ラベルには数式環境を含めることも可能です[^cite-9]．
`legend entries`オプションは，凡例に表示されるラベルを指定します．本オプションでは，グラフに表示される全てのプロットに対する凡例ラベルを，カンマ区切りで並べることにより一度に指定します[^cite-1]．

---

```tex
\addplot[smooth] gnuplot {(1-1/sqrt(1+2/(10**(x/10))))/2};
\addplot[sharp plot,red,mark=triangle*] table {data.txt};
```

`addplot`は，データの描画を指示するPGFPlotsの命令です．`addplot`を発行することにより，実際にグラフエリアにデータが描画され，凡例に項目が追加されます．
1つめの`addplot`は，指定した関数の曲線をグラフエリアに描画します．`smooth`オプションの指定により，関数のグラフはなめらかな曲線により示されるようになります．また，`gnuplot`コマンドを指定することにより，関数の計算を`gnuplot`プログラムが行うようになります．`gnuplot`に計算を委譲する利点としては，計算がTeXで行うより高速である点と`erfc`などの特殊関数が使用できるようになる点が挙げられます．
2つめの`addplot`は，外部のテキストファイルの内容をプロットします．
テキストファイルの形式は，2カラムタブ区切りであることが望ましいですが，`x`，`y`，`x index`，`y index`オプションにより，挙動を変更することもできます．`sharp plot`オプションの指定により，プロット間が直線でせつぞくされるようになります．`mark=triangle*`オプションにより，プロットの形が指定でき，`triangle*`では塗りつぶし三角形となります[^cite-10]．

## PGFPlots+TikZ: もっと調整したい！
前節で示したグラフは，gnuplotで作成したグラフと比べると，プロットが小さい，白抜きになっていない等の問題があり，gnuplotではないもので作成したことがすぐにばれてしまいます[^footnote-prohibited]．

そこで，PGFPlotsのマニュアルのReferenceを読むことにより，以下のようなグラフを実現することができます．

![pgfplots-lualatex-tikz.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35580/a2ce8a92-f819-699b-a5fb-14734661a2d1.png)


```tex:pgfplots-lualatex-tikz.tex
\documentclass[tikz]{standalone}
\usepackage{pgfplots}
\tikzset{% スタイルの作成
  pointtype triangle/.style={mark=triangle*,mark size=4pt},
  every mark/.style={fill=white,solid}
}
\pgfplotsset{% グラフ全体の見た目の設定
  compat=1.17,
  major tick length=0.2cm,
  minor tick length=0.1cm,
  every axis/.style={semithick},
  tick style={semithick,black},
}
\begin{document}
\begin{tikzpicture}[thick]
\begin{semilogyaxis}[% パラメータなどの設定
  width=85mm, height=65mm,
  domain=0:40,
  xmin=0, xmax=40,
  ymin=1e-5, ymax=1,
  minor x tick num=1,
  xlabel={Avarage CNR $\Gamma$ [dB]},
  ylabel={Bit Error Rate},
  legend entries={Theory,Simulation},
  legend style={draw=none,fill=none},
]
  % 理論特性
  \addplot[smooth] gnuplot {(1-1/sqrt(1+2/(10**(x/10))))/2};

  % 復調特性
  \addplot[sharp plot,pointtype triangle,dashed,red] table {data.txt};
\end{semilogyaxis}
\end{tikzpicture}
\end{document}
```

## PGFPlots+TeX: フックして強引に修正する
前節で示したグラフは，とても良い見た目になってきましたが，10^0が1となっていないという問題があり，gnuplotにはあと一歩及びません．そこで，マニュアルに記載のあるPGFPlotsのTeXフックを用い，内部の動作を書き換えることによりこの問題を解決します．

![pgfplots-lualatex-tikz2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35580/5b8c7a04-6bf2-49e7-88f9-7fae801c2e9e.png)


```tex:pgfplots-3.tex
\documentclass[tikz]{standalone}
\usetikzlibrary{matrix}
\usepackage{pgfplots}
\tikzset{% スタイルの作成
  pointtype triangle/.style={mark=triangle*,mark size=4pt},
  every mark/.style={fill=white,solid},
  south west label/.style={
    matrix,matrix of nodes,
    anchor=south west,at={(rel axis cs:0.01,0.01)},
    nodes={anchor=west,inner sep=0},
  },
}
\pgfplotsset{% グラフ全体の見た目の設定
  compat=1.17,
  major tick length=0.2cm,
  minor tick length=0.1cm,
  every axis/.style={semithick},
  tick style={semithick,black},
  legend cell align=left,
  legend image code/.code={%
    \draw[mark repeat=2,mark phase=2,#1]
      plot coordinates {(0cm,0cm) (0.5cm,0cm) (1.0cm,0cm)};
  },
  log number format basis/.code 2 args={
    \pgfmathsetmacro\e{#2}
    \pgfmathparse{\e==0}\ifnum\pgfmathresult>0{1}\else
    \pgfmathparse{\e==1}\ifnum\pgfmathresult>0{10}\else
    {$#1^{\pgfmathprintnumber{\e}}$}\fi\fi},
}
\begin{document}
\begin{tikzpicture}[thick]
\begin{semilogyaxis}[% パラメータなどの設定
  width=85mm, height=65mm,
  domain=0:40,
  xmin=0, xmax=40,
  ymin=1e-5, ymax=1,
  minor x tick num=1,
  xlabel={Avarage CNR $\Gamma$ [dB]},
  ylabel={Bit Error Rate},
  legend entries={Theory,Simulation},
  legend style={draw=none,fill=none},
]
  % 理論特性
  \addplot[smooth] gnuplot {(1-1/sqrt(1+2/(10**(x/10))))/2};

  % 復調特性
  \addplot[sharp plot,pointtype triangle,dashed,red] table {data.txt};

  \matrix[south west label] {
    QPSK \\
    Rayleigh fading \\
    Perfect channel estimation \\
  };
\end{semilogyaxis}
\end{tikzpicture}
\end{document}
```

## 結論
PGFPlotsにおいても，マニュアルをよく読むことによって，任意のスタイルに従う非常に高品位なグラフを生成することができます．PGFPlotsならば，こんなグラフもお手の物です．

![pgfplots-lualatex-snowman.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35580/bc01df9a-e7a5-9b58-e9bf-a8ca2b7742f6.png)



[^footnote-noeps]: 本記事は**高品位な**グラフに関する記事なので，EPS形式の図表については考慮しません
[^footnote-whiteout]: gnuplotは様々な出力ドライバをサポートしているため，だいたいの機能はドライバ非依存に実現される必要があり，その結果，非常に貧弱な白抜きしか実装されていません．
[^footnote-multipleplot]: 複数のデータを含める場合，プロットの色・形・線種・重なり順・凡例を制御するためにかなりカオスなプログラムが必要となり，作業量は4倍では済みません．マクロを用いた簡略化を試みましたが，原理的にほぼ実現不可能であることがわかり断念しました．
[^footnote-prohibited]: gnuplotであればいいのかというと，そういうわけではありませんが...
[^cite-1]: Christian F., Manual for Package pgfplots, Solving a Real Use–Case: Scientific Data Analysis, pp.23-28, Revision 1.12.1 (2015/05/02).
[^cite-2]: Christian F., The standalone Package, Usage of the standalone class, p.11, Version v1.2 – 2015/07/15.
[^cite-3]: Martin S., Manual for Package pgfplots, Upgrade remarks, p.8, Revision 1.12.1 (2015/05/02).
[^cite-4]: Martin S., Manual for Package pgfplots, Tick Fine-Tuning, p.293, Revision 1.12.1 (2015/05/02).
[^cite-5]: Martin S., Manual for Package pgfplots, The Axis-Environments, pp.39-40, Revision 1.12.1 (2015/05/02).
[^cite-6]: Martin S., Manual for Package pgfplots, Common Scaling Options, pp.237-238, Revision 1.12.1 (2015/05/02).
[^cite-7]: Martin S., Manual for Package pgfplots, Computing Coordinates with Mathematical Expressions, p.54, Revision 1.12.1 (2015/05/02).
[^cite-8]: Martin S., Manual for Package pgfplots, Configuration of Limits Ranges, p.270, Revision 1.12.1 (2015/05/02).
[^cite-9]: Martin S., Manual for Package pgfplots, Labels, p.200-201, Revision 1.12.1 (2015/05/02).
[^cite-10]: Martin S., Manual for Package pgfplots, Labels, The \addplot Command: Coordinate Input, p.40-72, Revision 1.12.1 (2015/05/02).