コードを書く

Todo

  1. 全面改訂予定。

  2. SciPy ノートと内容が重複するトピックがどうしても残る?

ヘルプ

NumPy Reference の Numpy-specific help functions セクションを見ておくこと。これらのヘルプ関数を覚えておくと IDLE での作業時には重宝するだろう。

キーワードから機能を探す

関数 lookfor を利用すると、NumPy 内の docstring からそれらしい機能をリストアップしてくれる。

>>> import numpy as np
>>> np.lookfor('least square')
Search results for 'least square'
---------------------------------
numpy.polyfit
    Least squares polynomial fit.
numpy.ma.polyfit
    Least squares polynomial fit.
numpy.linalg.lstsq
    Return the least-squares solution to a linear matrix equation.
...

NumPy のバージョンを知る

version.py の変数 version を参照する。

>>> np.version.version
'1.11.1'

関数またはオブジェクト array

ヘルプの使い方を習得したら、まずは array 周辺から攻略する。なお、本テキストで array と言うときは、同名の関数を指すか、同名の関数が返す値の型を指すかの両方があることをことわっておく。

array オブジェクトを生成する

NumPy Reference の Array creation routines のセクションできれいにまとめてある。

  • array オブジェクトの生成方法の基本は関数 array 呼び出しだ。関数 array はたいていの場合 ndarray 型のオブジェクトを返すようだ。

    import numpy as np
    
    # Create a vector.
    v = np.array([0., 0., 1.])
    
    # Create a matrix.
    m = np.array([[1., 0., 0.],
                  [0., 1., 0.],
                  [0., 0., 1.]])
    
  • zeros_like, ones_like, empty_like をワンセットで習得すること。既存の array_like オブジェクトから同じ shape の array を生み出す関数だ。

  • よく利用するのは ndarray だが、コンストラクターを直接利用せずに、関数 array, zeros, empty 等からオブジェクトを作成すること。

  • copy 関数で array_like オブジェクトのコピーオブジェクトを同一あるいは別の array オブジェクトとして作成することができる。

    >>> a = [1., 2., 3.]
    >>> np.copy(a)
    array([ 1.,  2.,  3.])
    
  • 変わったところでは arange 関数で「連番」配列を生成できる。Python の range 関数の array 版といったところだ。

    >>> np.arange(3.0)
    array([ 0.,  1.,  2.])
    

場合によっては array オブジェクトを生成したくない場合

もっと優れているのは関数 array のキーワード引数 copy を使い分けることだろう。例えば自作関数で array-like な引数をとる場合、「それが本当に array ならそのまま参照し、実は組み込み型のコンテナーオブジェクトならば array オブジェクトを新たに生成したい」ことがままある。そのような状況にはうってつけである。

def orthonormalize(center, eye, up):
    zdir = np.array(eye, copy=False) - np.array(center, copy=False)
    zdir /= np.linalg.norm(zdir)

    ydir = np.array(up)
    ydir /= np.linalg.norm(ydir)

    xdir = np.cross(ydir, zdir)

    return (xdir, ydir, zdir)

array の要素の型を明示的に指定する

キーワード引数 dtype を用いる。値の指定方法は色々あるが、次の方式を推奨する:

>>> x_axis = np.array([1, 0, 0], dtype=np.float64)
>>> x_axis[:]
array([ 1.,  0.,  0.])

既存の array の型をキャストした array を得る

メソッド .astype を用いる。キーワード引数 copy も併用可能。

>>> x_axis.astype(np.int8)
array([1, 0, 0], dtype=int8)

array の shape を変える

NumPy Reference の Array manipulation routines のセクションで表にまとめてある。

  • 1-D array_like オブジェクトを多次元化するには reshape メソッドまたは同名の関数を利用する。order 引数でメモリレイアウトを指示できる。

  • 多次元 array を「一次元配列化」するには flatten メソッドまたは関数 ravel を利用する。

    確実にコピーオブジェクトが欲しい場合は flatten を利用するのがよい?

    引数が order を表すので、PyOpenGL の行列系関数に渡すときに調整できるかも。

    >>> a = np.arange(16).reshape(4, 4)
    >>> a
    array([[ 0,  1,  2,  3],
           [ 4,  5,  6,  7],
           [ 8,  9, 10, 11],
           [12, 13, 14, 15]])
    >>> a.flatten()
    array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15])
    >>> a.flatten('F')
    array([ 0,  4,  8, 12,  1,  5,  9, 13,  2,  6, 10, 14,  3,  7, 11, 15])
    

同型 array オブジェクト同士に算術演算子を作用させる

+, - 等の二項演算子を同型 array オブジェクト同士に作用させることができる。各演算の定義は、成分ごとの算術演算のようだ。また、同型でなくとも broadcasting rule が適用できる場合は二項演算が実現できる。特にスカラーを作用させる場合は常に可能と考えていい。

array オブジェクトのスライシング

行列を表現する array オブジェクトから部分ベクトルを得るようなときには、 Python の list 同様、スライシングの技法を利用する。

>>> a = np.arange(24).reshape(3, 8)
>>> a
array([[ 0,  1,  2,  3,  4,  5,  6,  7],
       [ 8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23]])
>>> a[:,0]
array([ 0,  8, 16])
>>> a[0,:]
array([0, 1, 2, 3, 4, 5, 6, 7])

NumPy はより高性能なスライスをサポートしているが、深みにはまると危ないのでこの辺で。

線形代数関連

スカラー積、ベクトル積

スカラー積、ベクトル積を求めたい場合、それぞれ関数 dot, cross を利用すること。

>>> x = [1., 0., 0.]
>>> y = [0., 1., 0.]
>>> np.dot(x, y)
0.0
>>> np.cross(x, y)
array([ 0.,  0.,  1.])

dot については引数の shape さえ適合すれば行列の乗算もサポートする。

>>> x = [100., 200.]
>>> M = np.array([[1., 2.],
...               [3., 4.]])
>>> np.dot(x, M)
array([  700.,  1000.])
>>> np.dot(M, x)
array([  500.,  1100.])

ベクトルの長さ

二項演算が幅広くサポートされているので、1-D array オブジェクトをベクトルとみなすのが楽だ。が、ベクトルならば「長さ」を計算する関数が欲しい。ここでは dot を利用する。

>>> import math, numpy as np
>>> v = array([1., 1., 1.])
>>> math.sqrt(np.dot(v, v)) # By the way, sqrt is also provided in np.
1.7320508075688772

あるいは linalg パッケージにある norm 関数も有用だ。デフォルト引数をそのまま利用すれば 2-norm を計算してくれる。

>>> # v is the same as above example.
>>> vlen = np.linalg.norm(v)
>>> vlen
1.7320508075688772

ベクトル正規化

正規化とは長さが 1 になるようにベクトルの成分を定数倍することとする。それには、ベクトルの長さを上述の方法で得てから、長さが非ゼロであることを確認後、array オブジェクトに対して /= する。

>>> # v is the same as above example.
>>> v /= vlen
>>> v
array([ 0.57735027,  0.57735027,  0.57735027])

ベクトルの一致に関数 allclose を用いる

同次元空間にある 2 ベクトル v1, v2 が等しいか否かのテストをする。要するに、アプリケーション定義のトレランスを与えて、両者の差ベクトルの長さがそれ以内に収まっているかどうかを調べる。

関数 allclose をアプリケーション由来のトレランスを明示的に与えた上で適用するのがよかろう。デフォルトのトレランスではモデリング等で利用するには厳しすぎる。

ベクトルのなす角

これも自分でコードを書く。二ベクトルのスカラー積 (dot) とそれぞれの長さからなす角の cos が求まる。

二ベクトルの位置関係判定

二ベクトルが平行なのか、または直交するのかをテストしたい場合、dotcross を組み合わせれば何とかなる。

一発で identity matrix を生成する関数 eye, identity

関数 eye を利用する。ずばり identity という関数も存在するが、タイプし易いほうを選ぶ。

>>> np.eye(4)
array([[ 1.,  0.,  0.,  0.],
       [ 0.,  1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]])

対角行列を生成するには関数 diag を利用する

関数 diag を利用すると、手軽に対角行列を作成できる。

>>> A = np.diag([1.,2.,3.])
>>> A
array([[ 1.,  0.,  0.],
       [ 0.,  2.,  0.],
       [ 0.,  0.,  3.]])

行列から対角成分を抽出するのも関数 diag を利用する

やはり関数 diag を利用する。引数に二次元の array オブジェクトを渡すこと。

>>> # A is the same as above example.
>>> np.diag(A)
array([1., 2., 3.])

統計関連

NumPy は標本を表現するデータ構造としても array を利用している。 NumPy Reference の Statistics のセクションでまとめてある。

  • 統計関連の機能がまれにメソッドの形式で提供されていることがある?

  • 個人的な用途では 1-D array オブジェクトを主に対象とする(このノートでも)が、当然 n-D array オブジェクトについても各種統計関数を適用できる。サンプリングの対象は array オブジェクトの各要素でも、 along on axis でも OK だ。

  • 確率と統計 も参照して欲しい。

総和

Python 組み込み関数の sum を含む、色々な選択肢がある。ここは np.sum に統一したい。

>>> v = np.arange(100.)
>>> np.sum(v)
4950.0

最小値、最大値

np.min, np.max 関数がそれぞれ array オブジェクトの最小値、最大値を検索できる。

>>> # v is the same as above example.
>>> np.amin(v)
0.0
>>> np.amax(v)
99.0

平均値

細かいことを言えば平均値の定義によるが、np.averagenp.mean が利用できる。個人的には、相加平均を使うという意味を表現したいので mean を用いる。

>>> # v is the same as above example.
>>> np.mean(v)
49.5

ヒストグラム

関数 np.histogramarray からヒストグラムを作成することができる。ビンの与え方もその気になれば非等幅ビンを指定することもできる。

  • ビンの個数はデフォルトで 10 らしい。サンプル数からそれらしいビン数を計算するのは自力でやれということか。

  • 各ビンは半開区間(左閉)なのだが、最後のビンのみ閉区間になることに注意が必要。

    >>> sample = np.arange(16.)
    >>> hist, binedges = np.histogram(sample)
    >>> hist
    array([2, 1, 2, 1, 2, 1, 2, 1, 2, 2])
    >>> binedges
    array([  0. ,   1.5,   3. ,   4.5,   6. ,   7.5,   9. ,  10.5,  12. ,
            13.5,  15. ])
    

乱数関連

  • NumPy Reference の Random sampling (numpy.random) セクションに多数。

  • 乱数は奥が深い。追究し出すときりがない。目的を達成するのに十分見合いそうな関数を見つけたら、他の乱数関数を調べないこと。

  • 分布モノは一通り網羅しているようなので、業務上必要になったら文献を当たろう。

内容がランダムな整数 array を得る

np.random.randint(low, high, size) を利用する。引数リストが独特なので、すべての引数に明示的に実引数を渡した方がよいだろう。

>>> np.random.randint(0, 500, 4)
array([210, 332, 476, 488])
>>> np.random.randint(0, 500, 4)
array([149, 183, 182,  40])

内容がランダムな float 型数 array を得る

np.random.random_sample(size) を利用する。こいつは \({[0, 1)}\) の値を size 回ランダムに抽出するだけなので、欲しい値の範囲には、自分で線形変換をかけて値を得る。

>>> np.random.random_sample(15)
array([ 0.54368538,  0.65534826,  0.42464352,  0.21621149,  0.55229361,
        0.15027351,  0.23596445,  0.04811345,  0.11326923,  0.36599603,
        0.32611298,  0.29099913,  0.6946677 ,  0.51569253,  0.25698767])

array の内容をランダムにシャッフルする

C++ で言うところの std::random_shuffle(first, last) と同等のことをしたい(現代では非推奨だが)。それには np.random.shuffle(x) を利用すればよい。

>>> a = np.arange(5)
>>> a
array([0, 1, 2, 3, 4])
>>> np.random.shuffle(a)
>>> a
array([2, 4, 0, 1, 3])

どうも shape のある array には効き目が薄いようだ(確かめろ)。