NumPy ndarray assignment, vector operation, indexing, slicing, bool indexing, transposition

大規模高速計算を前提にC言語との接続を前提にしていて、配列処理に寄せることになる。
ndarrayで確保するメモリはPythonとは別(プロセス?)で確保される。

一通り流してみる。
shape()で配列の形を応答する。2行3列。


import numpy as np

data = np.random.randn(2,3)
shape = data.shape
print(shape)
print(data)

# (2, 3)
# [[ 0.79004157  0.45749364  0.90854549]
#  [-1.91791968  2.80050094 -0.60338724]]

ndarrayを作る

ndarrayを作る方法は以下。


data1 = [1,2,3,4,5]
data2 = [6,7,8,9,10]
data = np.array([data1,data2])
print(data)
# [[ 1  2  3  4  5]
#  [ 6  7  8  9 10]]

rng = np.arange(5)
print(rng)
# [0 1 2 3 4]

ones = np.ones((5,5))
print(ones)
# [[1. 1. 1. 1. 1.]
#  [1. 1. 1. 1. 1.]
#  [1. 1. 1. 1. 1.]
#  [1. 1. 1. 1. 1.]
#  [1. 1. 1. 1. 1.]]

# 零行列
zeros = np.zeros((3,5))
print(zeros)
# [[0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0.]
#  [0. 0. 0. 0. 0.]]

# 未初期化の配列確保
empties = np.empty((5,3))
print(empties)
# [[-1.72723371e-077 -1.72723371e-077  2.24419447e-314]
#  [ 2.24421423e-314  2.24421423e-314  2.24563072e-314]
#  [ 2.24421559e-314  2.24563072e-314  2.24421570e-314]
#  [ 2.24563072e-314  2.24421558e-314  2.24563072e-314]
#  [ 2.24421562e-314  2.24563072e-314  2.24421577e-314]]

# 指定値で埋める
fulls = np.full((2,3),5)
print(full)
# [[5 5 5]
#  [5 5 5]]

# 単位行列
identities = np.identity(5)
print(identities)
# [[1. 0. 0. 0. 0.]
#  [0. 1. 0. 0. 0.]
#  [0. 0. 1. 0. 0.]
#  [0. 0. 0. 1. 0.]
#  [0. 0. 0. 0. 1.]]

ndarrayのデータ型

ndarrayで確保されるメモリのデータ型。
実際に型に従ってメモリが確保されているため、簡単にCに渡せる。


ary = np.array((1,2,3),dtype=np.float64)
print(ary)
# [1. 2. 3.]

# float64をint32でキャスト
ary_int = ary.astype(np.int32)
print(ary_int)
# [1 2 3]

# キャストできないとコケる
ary_str = np.array(['hoge','fuga'])
ary_str_int = ary_str.astype(np.int32) # ValueError: invalid literal for int() with base 10: 'hoge'

ベクトル演算

配列に寄せる醍醐味。Pythonに数値計算用のオペランドが用意されていることがあって、
割と自然に書ける。


ary = np.array([[1,2,3],[4,5,6]])
print(ary * ary)
# [[ 1  4  9]
#  [16 25 36]]
print(ary - ary)
# [[0 0 0]
#  [0 0 0]]

print(ary * 2)
# [[ 2  4  6]
#  [ 8 10 12]]
print(ary ** 2)
# [[ 1  4  9]
#  [16 25 36]]

スライスとView

巨大なメモリへのアクセス高速化のために、np.arrayに対するスライスによるアクセスは、
同じメモリを指すViewを返す。Viewに対する操作は元のメモリを変更する。
Copyする場合は明示的にCopyをする必要がある。


ary = np.arange(10)
print(ary) # [0 1 2 3 4 5 6 7 8 9]
ary[5] = 500
print(ary) # [  0   1   2   3   4 500   6   7   8   9]
ary[3:5] = 999
print(ary) # [  0   1   2 900 900 500   6   7   8   9]
copied = ary.copy()
print(copied) # [  0   1   2 900 900 500   6   7   8   9]

2次元のnp.array。要素へのアクセスの仕方は2通り。


ary2d = np.array([[1,2,3],[10,20,30]])
print(ary2d)
# [[ 1  2  3]
#  [10 20 30]]
print(ary2d[1]) # [10 20 30]
print(ary2d[1][0]) # 10
print(ary2d[1,0]) # 10

n次元arrayへスカラーでインデックス参照するとn-1次元が戻る。
スライス参照はn次元が戻る。


ary2d = np.array([[1,2,3],[10,20,30],[100,200,300]])
print(ary2d[1])
# [10 20 30]
print(ary2d[:2])
#  [[ 1  2  3]
#   [10 20 30]]
print(ary2d[1,:2])
# [10,20]

Viewの選択

ndarrayから欲しいViewを選択するために色々と条件をつけられる。
例えば、bool index参照。


data = np.random.randn(7,4)
print(data)
# [[-0.69179761 -1.30790477  1.7224557  -0.67436315]
#  [ 0.45457462  0.24713663 -0.84619583 -0.31182853]
#  [-1.36397651  0.51770088 -1.8459593  -1.75146057]
#  [ 2.38626251 -0.4747874  -0.49951212  0.61803437]
#  [ 1.00048197  1.21838773 -0.4828001   0.9952139 ]
#  [ 0.17838262  1.687342    0.81501139 -1.12800811]
#  [ 0.65216988 -2.57185067  0.29802975  0.28870091]]

recs = np.array(['apple','orange','banana','mountain','river','moon','snow'])
print(recs=='mountain')
# [False False False  True False False False]

print(data[recs=='mountain'])
# [[ 2.38626251 -0.4747874  -0.49951212  0.61803437]]

reshape

reshape()を使って行列の形を変える。例えば1×15のndarrayを3×5のndarrayに変換。
もちろんCopyではなくView。これは頻出っぽい。
ちなみに、転値は専用のメソッド(T)が用意されている。


data1 = np.arange(15)
print(data1)
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14]
data2 = data1.reshape(3,5)
print(data2)
# [[ 0  1  2  3  4]
#  [ 5  6  7  8  9]
#  [10 11 12 13 14]]
data3 = data2.T
print(data3)
# [[ 0  5 10]
#  [ 1  6 11]
#  [ 2  7 12]
#  [ 3  8 13]
#  [ 4  9 14]]