necoplot; 少しだけmatplotlibを簡潔に書けるラッパー
記事の背景
necoplot
というライブラリを作ったのでそのことについて記事にします。
matplotlibを少しだけ簡潔に書くことができます。
Githubでも公開しています。 -> Github necoplot
この記事の想定読者
- Pythonでmatplotlibを使う
- matplotlibの作図をもう少し簡潔にやりたい
- 人がつくったライブラリの話に興味がある(※)
※ 筆者は独学の非エンジニアです。
「そういう人でもこういうのつくれるんだ〜」
くらいに思っていただければ幸いです。
結論
- ものぐさな筆者が自分のためにつくりました
- 引数をわりと適当に入れてもわりと大丈夫なようにつくりました
- matplotlibの引数やメソッドはだいたいそのまま使えるはずです
ライブラリの特徴とメリット
- wiht文を使って一部のコード記述を簡略化してます
- 図の設定を引数を介して行うことで、コードの行数を削減してます
- デフォルト値を設定でき、引数入力の手間を省けるようにしています
インストール
pip install necoplot
でインストールできます。
(作りたてなので今後挙動の変更がいろいろ生じるかもしれませんm(_ _)m)
使用例
下準備
import necoplot as neco
としておきます。[1]
import necoplot as neco
import numpy as np
xx = np.linspace(-5,5,20)
yy = xx*xx
以降xの2乗のグラフを題材にしていきます。
最もシンプルな例
with neco.plot() as ax:
という形式で使います。[2]
まずは引数を何も入れないプレーンな状態から。
# Basic
with neco.plot() as ax:
ax.plot(xx, yy)
簡素な図ですが、2行で作図できました。
処理としては、with文に入る時にfigureを作って、出る時にplt.show()しています。
figureの設定
いわゆるfig = plt.figure()
の部分です。
necoplotではneco.plot()
の引数に設定する値を投入できます。
画像の縦横比や色などを変更してみます。
# Config figiure
with neco.plot(figsize=(4,4), dpi=80, facecolor='silver') as ax:
ax.plot(xx, yy)
地味な図ではありますが、こちらも2行で作図できました。
axの設定1: figureの設定と一緒に
figureの次はax(図)の設定をします。
axもfigureと同じようにneco.plot()
の引数として設定することができます。
本来は別々に扱う引数ですが、ちょっとした設定で行追加するのが面倒だったので、
figureと同じところから引数を代入できるようにしています。
今度はfigureの設定と同時にaxの設定もします。
xの範囲を(-5〜0)の範囲に限定した図をつくります。
# Config ax by plot()
with neco.plot(figsize=(6,4), xlim=(-5,0)) as ax:
ax.plot(xx, yy)
裏側では投入された引数を選り分けて、figure用とax用に割り振ったりしています。
余談: figureとax
figureとaxの違いは、matplotlibの少しややこしいところです。
筆者の雑な理解でいうと、たとえるならfigureは画板です。
その上に具体的な図(ここではax)が載るイメージです。
よって基本的にfigureは1つですが、axは複数使うことができます。
実際にこのあと複数のaxを使うケースも見ていきます。
axの設定2: 関数を使って設定する
先ほどはfigureと同じneco.plot()
からaxの設定を行いましたが、
今度はneco.config_ax()
を使ってaxの設定を行います。
neco.plot()
だと少し煩雑になる場合には、こちらのほうがスッキリします。
この場合は、neco.config_ax()
の結果をneco.plot()
に渡してあげます。
ここではxの範囲の指定に加えて、タイトルをつけて、xのスケールを対数スケールに変更しています。
(図の設定に深い意味はありません)
# Config ax by using config_ax()
ax0 = neco.config_ax(xlim=(1,5), title='title', xscale='log')
with neco.plot(ax0, figsize=(6,4)) as ax:
ax.plot(xx, yy)
実は、ここでのax0
は関数なので、with文の中でそのまま使うことはできません。
neco.config_ax()
は関数をつくる関数ということになります。
with文に入る前はax(図)を貼るfigure(画板)が準備できないので、こういう仕様になりました。
axの設定3: axに直接設定を加える
with neco.plot() as ax:
のaxは、matplotlibのAxesクラスです。
よって、matplotlibのaxでやっていたことはだいたい再現できるはずです。
(例: ax.plot(), ax.bar(), ax.set_xlim()…etc)
この場合もaxを使って追加の設定をいろいろしています。
# Config ax directry
with neco.plot() as ax:
ax.plot(xx, yy, label='x squared')
ax.legend()
ax.hlines(y=25, xmin=-5, xmax=5)
なんとなくドラえもんのポケットみたいですね。
図の保存
図の保存にはneco.save()
を使います。
show
オプションをFalseにすると、図の描写はせずに画像の保存だけ実行してくれます。
# Save figure
with neco.plot() as ax:
ax.plot(xx, yy)
neco.save('sample.png', show=False)
show=False
なので画像も省略します。
複数のax(図)を1つのfigure(画板)に描写する
複数の図を描写する際にはneco.mplot()
を使います。
multiplotの略でmplotです。[3]
複数の図を描写するので、画板のどこに配置するかの「番地」を指定する必要があります。
最初の引数の121
や122
がそれにあたります。
この3桁で(行数,列数,順番)が表現されています。
121は1行2列の1番目、122は1行2列の2番目を指定しています。
# Plot multiple with mplot()
ax0 = neco.config_ax(121, xlim=(-5, 0),title='Left side')
ax1 = neco.config_ax(122, xlim=(0, 5), title='Right side', yticks=[])
with neco.mplot([ax0, ax1]) as p:
p.axes[0].plot(xx, yy)
p.axes[1].plot(xx, yy)
見た目をスッキリさせるために、2つの図の間のy軸の目盛りを省略してみました。
この3桁の記法で最大9つの図を1枚の画板に収録できます。
よく使う引数をデフォルト値に設定する
いくつか図を作っていると、複数の図で共通して使う引数が出てくることがあります。
毎回同じ引数を書くのがめんどうなので、デフォルト値の引数を設定できるようにしました。
あくまでデフォルト値を設定しているだけなので、
個別に他の値を代入すればそちらが優先して反映されます。
以下の図は直接neco.plot()
に何も引数を与えなくても、
neco.config_user_parameters()
で設定したタイトルが反映されている例です。
# Config default values
neco.config_user_parameters(title='New default title!')
with neco.plot() as ax: # 引数を設定していない
ax.plot(xx, yy)
タイトルを設定していなくても、新しい画像にタイトルがつきました。
デフォルト値の再設定にはデコレータなどを使っています。
設定をリセットする
設定したデフォルト値をすべて取り消したい場合は、neco.reset()
を使います。
# Reset config
neco.reset()
ちなみに、猫リセットといえば…[4]
もう少し手の込んだ例
最後に、matplotlib公式を参考にもう少し手の込んだ作図をしてみます。[5]
グルーブごとの棒グラフ
データの準備
import numpy as np
title = 'Scores by group and gender'
labels = ['G1', 'G2', 'G3', 'G4', 'G5']
ylabel = 'Scores'
x = np.arange(len(labels))
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
width = 0.35
作図
ax0 = neco.config_ax(title=title, ylabel=ylabel, xticks=x, xticklabels=labels)
with neco.plot(ax0) as ax:
ax.bar(x - width/2, men_means, width, label='Men')
ax.bar(x + width/2, women_means, width, label='Women')
ax.legend()
散布図と棒グラフ
データの準備
np.random.seed(0)
x0, y0 = np.random.normal(size=(2, 200))
x1 = np.arange(5)
y11, y12 = np.random.randint(1, 25, size=(2, 5))
width = 0.25
作図
ax0 = neco.config_ax(121)
ax1 = neco.config_ax(122, xticks=range(5), xticklabels=['a', 'b', 'c', 'd', 'e'])
with neco.mplot([ax0, ax1]) as p:
p.axes[0].plot(x0, y0, 'o')
p.axes[1].bar(x1, y11, width)
p.axes[1].bar(x1+width, y12, width)
ずっと放物線のグラフばっかり紹介していましたが、
それ以外のグラフもきちんと描けることが確認できました。
雑感
- エンジニアじゃなくても、その気があればライブラリをつくって公開できることがわかった
- 自分でライブラリを作るとPythonの色んな仕様を調べる機会になるので、よい勉強になった
まとめ
-
necoplot
を使って(比較的)簡潔に作図できるようになった! - ライブラリを自分でつくると色々勉強になる!
-
nekoではなくnecoなのは、
nekoplot
が先に存在していて、名前が被ったためです。 ↩︎ -
コンテキストマネージャーを使う方法はあきとしのスクラップノートさんの以下の記事を参考にさせていただきました。
[python] context manger を使ってmatplotlibの図を大量生産する
あきとしさんの作成されたライブラリcontextplt
は以下のリンクをご参照ください。
https://toshiakiasakura.github.io/contextplt/index.html ↩︎ -
短いほうが書きやすいしかわいいので、可読性を少し犠牲にしました。 ↩︎
-
お気づきの方もいるかもですが、「ずっと真夜中でいいのに。」の『猫リセット』をモチーフ(?)にしています。
筆者はずとまよのファンなので、この関数を実装することをモチベーションに開発がんばりました。
ずっと真夜中でいいのに。『猫リセット』MV (ZUTOMAYO - Neko Reset) ↩︎
Discussion