NumPyで散布図を書く

NumPy + Matplotlibの環境構築がうまく行かなくてほったらかしにしていたのだけどいい加減ほったらかしにしてられなくなってきたので「明確な目標を立ててそれの達成のために必要最小限だけタイムトライアルで学ぶ」というアプローチで頑張ってみた。

ipythonとnumpyとmatplotlibはインストール済み。しかしmatplotlibの方のチュートリアルはshowを叩いても何も表示されなくてインストールできてるのかできてないのかよくわからん状況。前回はそこまでやって放置した。Pyplot tutorial — Matplotlib v1.1.0 documentation

まずはNumPyのチュートリアルをざっと眺める。Tentative NumPy Tutorial -

ndarrayはmatrixの親クラスで、N-dimension array。arrayで作る。import *とか、何がどこからきたかわかりにくくなるから嫌いなんだけどとりあえず従っておく。

In [1]: from numpy import *

In [2]: array([1, 2, 3])
Out[2]: array([1, 2, 3])

N次元の配列ということで、Pythonの普通のシーケンス型がxs[3]で値を取れるように、xs[3, 3]で値を取れるように拡張されている。そしてxs[:]ができるのと同様にxs[:, :]もvalid。面白い。

とりあえずベクトル演算とか試そうと思って array([1, 2, 3]).transpose() したら期待と違って変化せず。

In [3]: x = array([1, 2, 3])

In [4]: x.transpose()
Out[4]: array([1, 2, 3])

これはx.shapeを見れば理由がわかる。行列が2次元の構造でベクトルは1次元の構造だ、と思ったら間違いで、ベクトルに縦ベクトルと横ベクトルの2種類があるってのはやはりそれが2次元の構造だからなんだな。というわけで(3,)って1次元の構造だったのを(1, 3)にreshapeしたら期待通りの挙動をするようになった。

In [5]: x.shape
Out[5]: (3,)

In [6]: x.reshape(1, 3)
Out[6]: array([[1, 2, 3]])

In [7]: _.transpose()
Out[7]: 
array([[1],
       [2],
       [3]])

matrixを使えば解決なんだけどmatrixには罠がいっぱいあるという噂なので避けてみた。罠って具体的になんなんだろうね。ここらへんはc_とかr_とかで楽になるのかな。

In [14]: c_[[1, 2, 3]]
Out[14]: 
array([[1],
       [2],
       [3]])

arrayとスカラーで演算をするとスカラーがarrayの要素それぞれに対してブロードキャストされる。arrayの添字にはarrayを渡すこともできて、intのarrayならその添え字が選択され、boolのarrayならTrueの所が選択される。そしてそれに対して破壊的代入もできる。クール。

In [19]: x = array([1, 2, 3])

In [20]: x > 1
Out[20]: array([False,  True,  True], dtype=bool)

In [21]: x + 1
Out[21]: array([2, 3, 4])

In [22]: x[x % 2 == 1] -= 1

In [23]: x
Out[23]: array([0, 2, 2])

x.view()がシャローコピー、x.copy()がディープコピー。

max, min, sumがある。cov, mean, std, varがある。svdもある。楽チン。

さてそろそろ散布図を書こう。いろいろなチュートリアルやサンプルがいろいろ別なものをimportしていてよくわからないが、とりあえず一番シンプルなfrom pylab import *で試してみる。ref. pythonグラフライブラリ「matplotlib」覚書 - Pashango’s Blog ちなみにimport pylabしてpylab.でTABを押してどんなメンバがあるか一応眺めておいた。917個もあって萎えた。fromfunctionはこの中にあるのか。

In [27]: from pylab import *

In [28]: scatter(randn(100), randn(100))
Out[28]: <matplotlib.collections.CircleCollection at 0x103fabe90>

randnは標準正規分布に従う乱数のarrayを作る。で、ここでshow()するとグラフのウィンドウが出る環境もあるらしいのだが、僕の環境では出ない。困って調べていると、そうかファイルに出力すればいいか。ref. matplotlib でグラフを描画する。 - loumo.jp

In [30]: import matplotlib

In [31]: matplotlib.pyplot.savefig('test.png')

In [32]: !open test.png

できたできた。ちなみにいい忘れたけども僕の環境はMacOSX 10.6.8で、openはMacOSXのコマンド。ファイルを適当なアプリで開いてくれる。僕の場合プレビューが開く。で、プレビューは開いているファイルが変更された場合自動的に再読み込みをしてくれる。ん、じゃあshow()動かなくてもたいして困らないか。

ここでパラメータを変えてグラフを描き直してみたくなると思うんだけど、このまま描き直しても前のグラフに追記されてしまう。対話的にいろいろ試したいならグラフを消して書きなおす関数がclf()なのを覚えておく必要がある。 (clear figureの略だろうな)

まとめ

In [1]: from numpy import *
In [27]: from pylab import *
In [30]: import matplotlib

In [28]: scatter(randn(100), randn(100))
Out[28]: <matplotlib.collections.CircleCollection at 0x103fabe90>

In [31]: matplotlib.pyplot.savefig('test.png')
In [32]: !open test.png

# パラメータを変えて描き直してみる
In [33]: clf()

In [39]: data = c_[randn(100), randn(100)]

In [40]: data = data.dot(array([[1, 1], [0, 1]])) # 斜めにしてみる

In [42]: scatter(data[:, 0], data[:, 1])
Out[42]: <matplotlib.collections.CircleCollection at 0x104411290>

In [43]: matplotlib.pyplot.savefig('test.png')

追記: scatterのオプション引数でmarker="+"とかできるの書き忘れた

追記: alpha=0.5を指定するとかっこいい