プロット

はじめに

Python によるプロット(グラフ描画)には Matplotlib(マットプロットリブ)というライブラリを使うのが基本です。より高レベルのライブラリとして seaborn がありますが、これについては seabornによるプロット のページで説明します。

Google Colaboratory や Jupyter Notebook での利用

まずはセルに次のように打ち込んで Shift + Enter します:

import matplotlib.pyplot as plt

次のセルに次のように打ち込めば、何やらグラフが表示されます:

plt.plot([-1, 1])

グラフの上に [<matplotlib.lines.Line2D at 0x…………>] のような行が表示されます。気にしなければいいのですが、気になるなら、セルの最後に plt.show() という行を追加します:

plt.plot([-1, 1])
plt.show()

別の方法として、セルの最後の行の最後にセミコロン ; を付けるというハックもあります:

plt.plot([-1, 1]);

Google Colaboratory のデフォルトのフォントでは日本語(いわゆる全角文字)が出ません。うまくいかない例:

plt.plot([-1, 1])
plt.xlabel("横軸")
plt.ylabel("縦軸")
plt.show()

エラーがいくつか出て、文字が □□ のような豆腐になります。自分のマシンで Jupyter Notebook を動かしている場合は、下の「コマンドでの利用」を見て、フォントを設定してください。でも、Google Colaboratory の場合は、そもそも日本語対応のフォントがないので、やや面倒です。

日本語を使わなければいいのですが、使いたい場合は、簡単な解決策として pip install して import するだけで matplotlib を日本語表示対応させる で紹介されている japanize-matplotlib というライブラリを使うのが簡単です。新しいセルに

!pip install japanize-matplotlib

と打ち込むと、メッセージがいろいろ出て、最後に Successfully installed japanize-matplotlib-X.X.X のように出れば、インストール完了です。完了したら、新しいセルに

import japanize_matplotlib

と打ち込んでください。そして、さきほどうまくいかななった例

plt.plot([-1, 1])
plt.xlabel("横軸")
plt.ylabel("縦軸")
plt.show()

の中にカーソルを持っていき、再度 Shift + Enter します。ちゃんと「横軸」「縦軸」の文字が出たでしょうか。

Google Colaboratory の仮想マシンはときどきリセットされます。import japanize_matplotlib がうまくいかなくなったら、!pip install japanize-matplotlib をやりなおしてください。

Google Colab や Jupyter Notebook では、余白を含めた図の大きさのデフォルトは 6.0 × 4.0 インチ、dpi(dots per inch)値は 72 なので、ピクセル数にすれば最大 432 × 288 です(ダウンロードした画像ファイルは余白を含まないのでこれより小さくなります)。画像サイズを変えるには次のように横・縦のインチ数で指定します。

plt.figure(figsize=(8, 6))
plt.plot([-1, 1])

図はドラッグ&ドロップでダウンロードできます。デフォルトでは PNG 形式の画像です。

自分のマシンでコマンドで実行した場合と同じ図の大きさにデフォルトを変えるには次のようにします:

plt.rcParams["figure.figsize"] = [6.4, 4.8]
plt.rcParams["figure.dpi"] = 100.0

画像をSVG形式にすることもできます:

from IPython import display

display.set_matplotlib_formats("svg")

このほうが画像がくっきり表示されますが、ドラッグ&ドロップで画像をダウンロードすることはできなくなります。

コマンドでの利用

Google Colab や Jupyter Notebook と違って、コマンドで実行する場合のデフォルトの図の大きさ plt.rcParams["figure.figsize"][6.4, 4.8]、dpi 値 plt.rcParams["figure.dpi"]100.0 に設定されています。つまり、デフォルトの図の大きさは余白込みで 640 × 480 ピクセルです。

コマンドで ipython --matplotlib として IPython(対話モードの Python)を起動した場合、

import matplotlib.pyplot as plt

plt.plot([-1, 1])

でグラフが出るはずです。単に python または python3 として起動したときや IDLE というツールを使った場合には、これだけではグラフが出ないと思いますので、この後に

plt.show()

を補ってください。ただし、おそらくグラフの窓を閉じないと次のコマンドが打てないと思います。この状態を「ブロック」といいます。ブロックしないようにするには plt.show(block=False) のようにオプションを付ける方法がありますが、もっと楽な方法は、あらかじめ

plt.ion()

と打ち込んで、対話モード(interactive mode)を on にしてから、プロットする方法です。これなら IPython とほぼ同様な対話モードになります。このコマンドは、IPython を使っている場合にも、うまくグラフが更新されない場合に効果があるようです。

IPython には、例えば ?plt.plot のように打ち込めばヘルプが現れるなどの便利な機能がいろいろあります。

対話モードでは、グラフを閉じなくても次のコマンドを打ち込むことができます。続けて打ち込んだ描画命令は、同じグラフに追記されます。グラフをいったん消去したいときは、グラフの窓を閉じるか、plt.clf() と打ち込みます。グラフの窓を閉じるのは、コマンド plt.close() でもできます。複数のグラフの窓を全部閉じるには plt.close("all") とします。

描画がうまくいかない場合は、設定ファイル matplotlibrc(Mac と Windows では ~/.matplotlib/matplotlibrc、Linux では ~/.config/matplotlib/matplotlibrc)をチェックします。このファイルがないか、あっても backend: という設定がなければ、自動でバックエンドが選ばれるはずですが、もし間違えた指定になっていたら、backend: の行を消してください(その場合、MacOSX QtAgg Gtk4Agg Gtk3Agg TkAgg WxAgg Agg の中からうまくいく最初のものが選ばれるはずです)。

デフォルトのフォントでは日本語が出ません。日本語を出したいなら、システムにインストールされている適当な TrueType または OpenType フォント名を上記設定ファイル matplotlibrc に書き加えます。Mac なら

font.family: Hiragino Sans
font.weight: 400

Windows なら

font.family: MS Gothic

または

font.family: Meiryo

などでよさそうです(TrueType Collection(*.ttc)は昔は使えませんでしたが Matplotlib 3.1.0 で使えるようになりました)。いろいろな OS で共通のフォントを使いたいなら、こちらから IPAex ゴシックをいただいてきてシステムにインストールし、次のように書き加えます:

font.family: IPAexGothic

Source Han Sans の類も使えます。

新しいフォントをシステムにインストールしたら、フォント名のキャッシュ(Mac と Windows では ~/.matplotlib/fontlist-*.json、Linux では ~/.cache/matplotlib/fontlist-*.json)をいったん削除します。次回 matplotlib 使用の際に自動生成されます。

なお、font.family のデフォルト値は sans-serif で、DejaVu Sans が使われます。serif と指定すれば DejaVu Serif になります。DejaVu は Bitstream Vera ベースの良質なフォントです。

ユーザの matplotlibrc を作る前の、システムの matplotlibrc の位置は、ターミナルに次のコマンドを打ち込めば表示されます:

python3 -c "import matplotlib; print(matplotlib.matplotlib_fname())"

Mac なら ~/Library/Python/3.x/lib/python/site-packages/matplotlib/mpl-data/matplotlibrc のように出ると思います。

Linux では /usr/local/share/fonts の下に(適当にサブディレクトリを作って)ipaexg.ttf を入れて、フォント名キャッシュをいったん消せば、認識されます。あるいは、ディストリビューションによりますが、yum install ipa-gothic-fonts あるいは apt-get install fonts-ipafont-gothic などとしても IPA フォント(ex でないもの)がインストールできます。この場合は matplotlibrc には font.family: IPAGothic と書きます。

改めて次のようにやってみて、日本語の部分も出るか確認してください:

import matplotlib.pyplot as plt

plt.plot([-1, 1])
plt.xlabel("横軸")
plt.ylabel("縦軸")
plt.show()

日本語も軸のマイナスも化けずに出れば合格です。もし日本語が出てもマイナスが化ける場合は、フォントに MINUS SIGN (U+2212) がないのでしょう。マイナスを HYPHEN-MINUS (U+002D) で代用するために ~/.matplotlib/matplotlibrc に次の1行を追加します:

axes.unicode_minus: False

プロットのスタイルはいろいろ選べます。例えば

plt.style.use("ggplot")

とすれば R の ggplot のスタイルになります。スタイルは plt.style.available で一覧できます。もっと細かい設定は plt.rcParams と打ち込めば一覧できます。フォントも

plt.rcParams["font.family"] = "IPAexGothic"
plt.rcParams["font.size"] = 12

あるいは同じことですが

plt.rc("font", "family"="IPAexGothic", "size"=12)

のようにして変更できます。デフォルト状態に戻すには plt.rcdefaults() と打ちます。

タイトルやラベルに日本語のフォントを使う程度なら、特にデフォルトのフォントを変えなくても、次のように関数にオプションで与えることもできます:

plt.title("日本語", fontfamily="Yu Gothic", fontsize=20, fontweight=700)

ファイルへの保存

ファイルへの保存は、メニューからも、プログラムでもできます。アクティブなプロット(最後に描いたプロット)を保存しますので、例えば「Figure 2」を保存したいなら、あらかじめ plt.figure(2) と打ち込んでアクティブにしておきます:

plt.savefig("filename.png", bbox_inches="tight")  # デフォルト: dpi=100
plt.savefig("filename.pdf", bbox_inches="tight")
plt.savefig("filename.svg", bbox_inches="tight")
plt.savefig("filename.svgz", bbox_inches="tight")
plt.savefig("filename.pgf", bbox_inches="tight")

画像テストページ

最後のPGF(Portable Graphics Format)はTeXのTikZのベースとなるグラフィック言語です。これは次のような形のLaTeXファイルを xelatexlualatex でコンパイルすることによってPDFに変換できます:

\documentclass{standalone}
\usepackage{pgf}
\usepackage{fontspec}
\begin{document}
\input{filename.pgf}
\end{document}

PDFの場合は、次のようにすることもできます:

from matplotlib.backends.backend_pdf import PdfPages

with PdfPages("foo.pdf") as pdf:
    # ここで図を描く
    pdf.savefig(fig)

SVG 形式で保存してから、rsvg-convert コマンド(Mac では brew install librsvg、CentOS 7 では yum install librsvg2-tools で入る)を使って、ターミナルで

rsvg-convert --format=pdf --output=filename.pdf filename.svg

で PDF に変換できます。この場合、文字はアウトライン化され、文字情報はなくなります。plt.rcParams["svg.fonttype"] = "none" としておけば、SVG、PDF とも文字情報が保たれます(デフォルトは "path")。文字情報を保って PDF にする別の方法として、後述の PGF 経由で出力する方法があります。

文字だけLaTeXを使う

軸の文字やタイトル・キャプションなどの数式は、LaTeX風に $ で囲んで書けば、ちゃんと解釈してくれます。その際に、文字列は r"..." のように r を付けて書けば、\ を特別扱いしない「生の」(raw)文字列として扱われます。

latexdvipngdvipsgs コマンドが使える環境では、次のように text.usetexTrue にすれば、本当に文字部分だけLaTeXを起動して、きれいな出力を得ることができます。

import matplotlib.pyplot as plt
import numpy as np

plt.rcParams["text.usetex"] = True

x = np.linspace(-5, 5, 101)
y = 1 / (1 + np.exp(-x))
plt.figure(figsize=(5, 3))
plt.plot(x, y, "k")
plt.xlabel(r"$x$")
plt.ylabel(r"$y$")
plt.title("Sigmoid Function")
plt.legend([r"$y = \frac{1}{1+e^{-x}}$"])
plt.savefig("sigmoid.pdf", bbox_inches="tight")

デフォルトではComputer Modernフォント(数式はcmrやcmmi、タイトルはcmss)になります(PDFにはフォントがType 1形式で埋め込まれます)が、次のようにしてフォントを指定できます:

plt.rcParams["font.family"] = "serif" # あるいは "sans-serif" など
plt.rcParams["font.serif"] = "times"  # あるいは "palatino" など
plt.rcParams["font.sans-serif"] = "helvetica"

Timesでは \usepackage{mathptmx}、Palatinoでは \usepackage{mathpazo}、Helveticaでは \usepackage{helvet} がプリアンブルに書き込まれます。実際に処理されるLaTeXファイルは例えば次のようになります:

\documentclass{article}
\usepackage{type1cm}
\usepackage{mathpazo} % Palatino指定で入る
\usepackage{helvet}   % Helvetica指定で入る
\usepackage{textcomp}
\usepackage[utf8]{inputenc}
% ここに別途指定したプリアンブルが入る
\usepackage[papersize={72in,72in},body={70in,70in},margin={1in,1in}]{geometry}
\pagestyle{empty}
\begin{document}
\fontsize{10.000000}{12.500000}{\rmfamily $y = \frac{1}{1+e^{-x}}$}
\end{document}

次のようにしてプリアンブルを直接指定することもできます:

plt.rcParams["text.latex.preamble"] = r"\usepackage{tgtermes},\usepackage{newtxmath}"

プリアンブルを指定する場合は、他のコードと衝突してエラーにならないように、十分注意が必要です。上の例では、\usepackage{newtxtext} を指定するとエラーになったので、\usepackage{tgtermes} にしました。

~/.matplotlib/tex.cache にLaTeXファイルやdviファイルがたくさんできます。消してもかまいませんが、同じ文字列が出てきたら再利用されますので、特に消す必要はありません。

その他

図の大きさを指定する:

plt.figure(figsize=(6.4, 4.8))  # デフォルトは6.4×4.8インチ

あるいはデフォルトを変更する:

plt.rcParams["figure.figsize"] = (6.4, 4.8)

(Jupyter Notebook などの場合)図を PNG でなく SVG にする(拡大しても荒くならない):

from IPython import display
display.set_matplotlib_formats("svg")

軸の最小値・最大値を指定する:

plt.xlim(最小値, 最大値)
plt.ylim(最小値, 最大値)

あるいは両者をまとめて

plt.axis([xの最小値, xの最大値, yの最小値, yの最大値])

軸の目盛位置と目盛ラベルを指定する:

plt.xticks([-np.pi, 0, np.pi], [r"$-\pi$", "$0$", r"$\pi$"])

対数目盛:

plt.xscale("log")
plt.yscale("log")

グリッド描画:

plt.grid()
plt.gca().set_axisbelow(True) # グリッドを背面に

どちらかの軸の範囲を拡大して両軸の目盛を等しくする:

plt.axis("equal")

プロット領域の縦横比を調節して両軸の目盛を等しくする:

plt.axis("scaled")

水平線、垂直線を引く:

plt.axhline()  # 引数を省略すればy=0の水平線
plt.axvline()  # 引数を省略すればx=0の水平線
plt.axhline(0.5, linewidth=1, color="black")

プロットをクリア:

plt.clf()

全プロットを閉じる:

plt.close("all")

別の流儀

より高度なことをするには、まず次のように図と軸を別に作ります:

fig, ax = plt.subplots()

あるいは次のようにサイズを指定することもできます:

fig, ax = plt.subplots(figsize=(6.4, 4.8))

この流儀のほうがいろいろ細かいことがしやすくなります。例えば時系列の軸ラベルの設定:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

fig, ax = plt.subplots()
ax.xaxis.set_major_formatter(mdates.DateFormatter("%m-%d"))

さらに Matplotlib 3.1 で追加された ConciseDateFormatter を使った時系列の簡潔な軸ラベル:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates

locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator)
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)

ax.plot(dates, y) # いろいろプロットする
ax.set_ylim(0.1, 10)
ax.set_yscale("log")
ax.grid()
ax.legend(...)

fig.savefig("filename.svg", bbox_inches="tight")

ax.clear() # 図をクリアして次の図を描く

参考