6.11. リダクション

リダクション(reduction)は、合計・平均・最小値の取得などによって、1つ以上の軸に沿って配列を縮約します。各リダクションは配列全体に対する単一のライブラリ呼び出しであり、同等の Python ループよりもはるかに高速です。numpy は日常的に使うものを網羅しています:

  • sum() -- すべての要素の合計

  • mean() -- 算術平均(合計を要素数で割った値)

  • std() -- 標準偏差。ddof= で除数(N - ddof)を調整します

  • min() / max() -- 最小要素と最大要素

  • median() -- 要素をソートしたときの中央の値(50パーセンタイル)

  • argmin() / argmax() -- 最小要素または最大要素のインデックス

  • all() / any() -- ブール配列に対する真偽値のリダクション

6.11.1. axis キーワードなしの場合

axis= を指定せずに呼び出すと、リダクションは配列全体を対象としたスカラーを返します:

a = np.array([1, 2, 3, 4], dtype=np.float)
np.sum(a)           # 10.0
np.mean(a)          # 2.5
np.std(a)           # 1.118...
np.median(a)        # 2.5

b = np.array([40, 10, 30, 20], dtype=np.float)
np.max(b)           # 40.0
np.argmax(b)        # 0  (index of the maximum)

6.11.2. axis キーワードありの場合

axis= は指定した1つの軸を縮約し、その他の軸はそのまま残します。結果は入力よりもランクが1つ低い配列になります:

m = np.arange(12, dtype=np.float).reshape((3, 4))

np.sum(m)               # 66.0          - scalar
np.sum(m, axis=0)       # length-4      - column sums
np.sum(m, axis=1)       # length-3      - row sums

同じ形状の規則がすべてのリダクションに適用されます。axis=0 は最初の軸を縮約し、axis=1 は2番目の軸を縮約する、というようになります。たとえば行に沿った平均/標準偏差は np.mean(m, axis=1) および np.std(m, axis=1) と書きます。結果はもう一方の軸の長さを持ちます。

keepdims=True キーワードは、縮約された軸を削除する代わりに長さ1のまま残します。この違いは、縮約結果を元の配列に対してブロードキャストし直す必要がある場合に重要になります。keepdims はランクを保持するため、ブロードキャストの規則が軸ごとに揃った状態に保たれます。

各行からその行の平均を引くのが代表的な用途です:

m = np.arange(12, dtype=np.float).reshape((3, 4))
row_means = np.mean(m, axis=1, keepdims=True)
# row_means has shape (3, 1)
centred = m - row_means
# (3, 4) - (3, 1) -> (3, 4), each row centred on its own mean

keepdims がない場合、np.mean(m, axis=1) は形状 (3,) の1次元結果を返します。(3, 4) - (3,) のブロードキャストでは、ランクの前置によって (3,)(1, 3) として揃えられますが、これは (3, 4) と互換性がありません。最後の軸が一致せず(4 と 3)、どちらも1ではないため、numpyValueError を送出します。keepdims=True がこの減算を有効に保ちます。

6.11.3. レイアウトが重要

形状とストライド で説明した行優先(row-major)レイアウトと組み合わせると、最後の軸に沿った縮約が最も安価なケースになります。このリダクションはデータブロックを格納されている方向にたどり、行から行への飛び越しが発生しません:

m = np.arange(2000, dtype=np.float).reshape((2, 1000))
np.sum(m, axis=1)       # cheap - long axis is the inner one
np.sum(m, axis=0)       # has to jump rows on every step

アプリケーションがバッファのレイアウトを選択できる場合は、長い軸を最後に配置すると、その軸に沿ったリダクションが高速な方向で実行されます。

6.11.4. 入力としてのイテラブル

ほとんどのリダクションは、ndarray の代わりに Python のイテラブル(listrange、タプル)を受け付けます。この利便性は暗黙の変換のために数マイクロ秒のコストがかかり、ループ内ではすぐに積み重なります。同じデータを複数回縮約する場合は、ndarray を一度作成して、それを使い回してください。