Tentative NumPy Tutorial ノート

本稿は(現在アドレス不明の)Tentative NumPy Tutorial を読んだノートだ。かつて一度は目を通したハズだが、今ザッとリンク先を読んでみたら記憶から抜けている事項が妙に多いので、テキストの形でノートを残すことにした。

Prerequisites

これは問題ない。

The Basics

NumPy の基本は何と言ってもクラス ndarray である。便宜上ここでは配列と呼ぶが、任意次元のテーブルと思ったほうがよい。以下 a をこのクラスのオブジェクトとする。

  • array という別名が付いている。

  • そのテーブルの寸法 (dimensions) を axes と呼ぶ。

    • 対応するプロパティーは a.shape である。

      • 型は tuple であり、どの要素も正の整数である。

      • 例えば三次元空間の横ベクトルならば (3,) となる。縦ベクトルならばなんと (3, 1) になる。

      • テーブルの外側から内側に向かって axis の個数が列挙されている。

  • axes の個数を rank と呼ぶ。対応するプロパティーは a.ndim であり、値は len(a.shape) と等しい。

An example

上述の配列オブジェクトのプロパティーのはたらきを示すコードなので、理解度を念のため確認しておけばよい。

Array Creation

配列オブジェクトを生成する方法はいくつもある。

  • クラス array のコンストラクターに Python の list または tuple のオブジェクトを渡して生成する。

    • 配列の dtype は各要素から推論 (deduce) されて決まる。またはキーワード引数 dtype で明示的に指定できる。

    • 生成する配列の rank に応じて、list または tuple を入れ子にして指定する。

  • 要素が具体的に決まっていないが、配列の dimensions だけから生成する方法としては、関数``ones``, zeros, empty あるいはその系統の関数を用いる。

    • 引数に (3, 4) のように指定する。

    • 要素の型を明示的にキーワード引数 dtype で指定することができる。指定しない場合は float64 になる。

  • 関数 arange を用いれば、Python の関数 range よろしく、いわゆる start-stop-step 方式で生成する配列要素を指定できる。

    • ただし、浮動小数点型に対しては色々と危ない。代わりに関数 linspace を使うのだ。

      • こちらは start-stop-length 方式になる。

      • デフォルトでは生成配列の最後の要素が stop 自身になる。

Printing Arrays

  • 高 rank の配列要素の出力結果の書式について述べている。基本ルールはこういう感じ:

    • 最後の axis の要素(最も内側)は左から右に配列される。

    • 最後から二番目の axis は上から下に配列される。

    • 残りは上から下に配列されるが、各 axis 間は一行空く。

  • あまりに巨大な配列は適宜出力を省略するようになっている。

  • 関数 set_printoptions でその辺りを調整できる。

Basic Operations

  • 配列同士の算術二項演算子による演算は element-wise になされる。

    • 特に operator* もそのルールが採用される。ベクトルの内積・外積、行列の積はそれぞれ専用の関数を用いること。

  • 配列にスカラーを作用させると、各要素にスカラーを作用させた配列が生成する。

    • a < 35 の例が面白かった。ブール型の配列ができる。

  • 異なる要素型のオブジェクト同士の算術演算について、

    • operator(lhs, rhs) タイプは、精度の高い要素のほうの要素型の配列が生成する。

    • .operator(other) タイプは、左辺値ほうの要素型の配列が生成する。

  • .sum(), .min() 等のメソッドはデフォルトでは全要素を対象として演算を行うが、キーワード引数 axis を明示的に指定することで、配列の axis に沿って演算を行わせることができる。

Universal Functions

配列を引数にとって、戻り値を配列とする(数学的な)関数のことを universal function と呼ぶ(ちなみに universal を辞書で引くと <common to all members of a class or a group> という意味がある)。

np.sinnp.exp に何か浮動小数点型要素の配列を渡すと言わんとすることがわかる。

Indexing, Slicing and Iterating

  • 一次元配列

    • 添字で要素にアクセス可能。左辺値としても使える。

    • スライス操作ができる。構文は a[start:stop:step]stop は開区間エンド。

      • a[start:stop:] も可。

      • a[:stop:step] も可。

      • a[::-1] も可。

    • for i in a: の構文で要素を順番にアクセスできる。右辺値。

  • 多次元配列

    • カンマで区切ることで axis ごとに添字を指定することで要素にアクセスできる。

      • a[axis0, axis1] は一番外側 axis0 番目の、その次の内側 axis1 番目の要素にアクセスする。

    • カンマ記法とスライス記法を組み合わせられる。例えば a[0:5, 1] ならば一番外側 axis の 0, 1, 2, 3, 4 番目の短冊を取って、一番内側 axis の左から一番目の要素を取って来られる。

    • さらに dots 記法というのもある。これは一つの axis を丸ごと指定するようなスライスの省略らしい。例えば rank 3 の配列に対して a[1, ...]a[1, :, :] (or a[1]) と同値。

    • for i in a: でループすると、一番外側から一個内側の axis を順番にアクセスすることになる。一方 fot i in a.flat としてループすると、一番内側の要素を一個一個順番にアクセスすることになる。

Shape Manipulation

Changing the shape of an array

  • 配列のメモリレイアウトは基本は C 言語スタイル。

  • メソッド ravel, reshape 等にはキーワード引数で FORTRAN スタイルのメモリレイアウトで新しい配列を返すように指示することができる。

  • メソッド reshape は自身は変更せずに新しいオブジェクトを返す。一方、メソッド resize は自身を変更する。

  • 変形メソッドに一部の dimension を -1 と指定することで、残りの引数から自動的に計算させることが可能。

Stacking together different arrays

  • 異なる配列オブジェクトを異なる axis でくっつけられる。関数 vstackhstack を用いる。

  • a[:, newaxis] という構文? がアレ? 横のものを縦にする?

    newaxis を指定した添字位置に対応する axis が追加された新しい配列が生成する。

  • 関数 column_stackrow_stack は縦横を入れ替えて並べた配列を生成する。

  • 配列全体ではなく、スライスを並べることもできる。

  • 関数 vstackhstack の強化版として r_[], c_[] という便利なものもある。

Splitting one array into several smaller ones

  • 関数 hsplitvsplit で、配列を分割できる。分割数または分割位置を指定する。

Copies and Views

SciPy の Cookbook でも少し触れたこと。

No Copy at All

  • 単純な代入操作、要素操作ならばコピーは発生しない。

  • 関数呼び出しの引数はコピーによる受け渡しではない。

View or Shallow Copy

ビュー、すなわち浅いコピーについて。

  • a.view() で内部データを共有する新しい配列オブジェクトを生成できる。

    • 例えばビューの shape だけを変形させておきつつ、元の shape を触らない、というようなことが可能。

    • どこかビューの要素を変更すると、対応する元配列の要素も同じ値に変更される。

  • スライスもビューである。

Deep Copy

a.copy() で、すべての要素が a のそれと等しくかつ新しい配列オブジェクトが生成する。

Functions and Methods Overview

NumPy Example List という別文書へのリンク集。

Less Basic

基本というほどではなく。

Broadcasting rules

異なる dimensions を持つ配列を universal function に与える時になされる演算の規則性を規定するもの。

  1. ランクの小さい方の配列の shape に対して、先頭(左)から 1 を追加していき、ランクの大きい方の配列のランクとまずは一致させる。

    例を示す:

    a.shape: (2, 3, 4, 5)
    b.shape: (6, 7)
    c.shape: (3, 1, 5)
    
    => a.shape (2, 3, 4, 5)    a が最大ランクの配列なので、基準になる。
    => b.shape (1, 1, 6, 7)    1 を左側に (a.ndim - b.ndim) 個入れた。
    => c.shape (1, 3, 1, 5)    1 を左側に (a.ndim - c.ndim) 個入れた。
    
  2. 対応する各 axis に対して、次の条件のいずれか一方が成り立つとき、broadcasting は成功する。

    • 両者の値が一致する。

    • どちらの値が 1 である。このとき、小さい方の配列を引き伸ばして、大きい方の配列の dimensions に合わせることになる。欠けていた要素の値は、元の一個しかなかったその値で埋める。

    先ほどの配列で考えてみる:

    (a + b).shape   ValueError: operands could not be broadcast together with shapes (2,3,4,5) (6,7)
                    (4, 5) != (6, 7) ゆえ。
    
    (a + c).shape   the same as a.shape
                    (2, 3, 4, 5) と (1, 3, 1, 5) で値が 1 の axis を除けば、
                    両者は一致しているので OK とみなす。
    

Fancy indexing and index tricks

NumPy の配列の添字システムは Python の list よりも多彩。

Indexing with Arrays of Indices

配列オブジェクトを別の配列オブジェクトの添字として利用できる。言わば添字型の配列。

Indexing with Boolean Arrays

ブーリアン型の配列オブジェクトを配列の添字として用いると、値が True の要素だけからなる配列が生成する。

The ix_() function

関数 ix_ を説明するのは面倒だ。相異なる n 個のベクトル(平坦な NumPy 配列と考えること)があり、これらから要素をひとつずつ取ってきて多項演算を行うという操作を一気に実行するための前準備をするものだ。

Indexing with strings

配列の要素に名前でアクセスする方法がある。

img = array([[(0,0,0), (1,0,0)],
             [(0,1,0), (0,0,1)]],
            [('r',float32), ('g',float32), ('b',float32)])

print(img['r'])

Linear Algebra

基本的な線形代数がここに来るとのこと。

Simple Array Operations

ファイル linalg.py を見ろとのこと。

The Matrix Class

  • クラス matrix というものがある。

    • コンストラクターに文字列を渡すようだ。

    • プロパティー .T で転置行列を得る。

    • プロパティー .I で逆行列を得る。

Indexing: Comparing Matrices and 2D Arrays

配列クラスと行列クラスでの添字の違いを説明する。

  • 同型の 2 次元配列と行列に対して、それぞれにスライス [:, 1] を得ようとすると、両者の結果の dimensions が異なる。配列の方はランク 1 の配列を生成し、一方行列の方はランク 2 の行列を生成する。

  • 行列クラスには .A というプロパティーがあり、これが行列成分の配列表現である。適宜利用するとよい。

Tricks and Tips

知っていると便利なコツ。

“Automatic” Reshaping

先述の配列オブジェクトの shape 変形にまつわる -1 記法について。

Vector Stacking

既に述べたことだが、同型の行ベクトルから二次元配列を生成する方法として、関数 vstack を用いる。

Histograms

  • 関数 np.histogram は配列からヒストグラムのビン(各個数と各点)を返す。

  • 一方、関数 pylab.hist はそれ相当の処理に加えてプロットまでを行う。

References

ここには 7 個の参考文書がリストされているが、おそらくどれも読まないで済ませそうだ。