S-JIS[2018-01-13] 変更履歴

Python NumPy ndarray

Pythonの拡張モジュールNumPy(1.14.0)のndarrayのメモ。


概要

ndarrayは多次元配列(N-dimensional array)を扱うクラス。

import numpy as np

ndarrayは全要素が同じデータ型になる。
(Pythonのlistは異なる型でも入れることは出来る)

値を効率よく保存したり演算したりする為だと思われる。


生成方法

ndarrayを生成する例。

結果 説明
# 1次元配列
a = np.array([1, 2, 3])
array([1, 2, 3])
Python標準のリストを指定して生成する。
# 2次元配列
a = np.array([[1, 2], [3, 4]])
array([[1, 2],
       [3, 4]])
# 1次元配列
a = np.zeros(4)
array([0., 0., 0., 0.])
要素が全て0の配列を生成する。
第1引数には各次元の要素数を(2次元以上の場合はタプルで)指定する。

デフォルトではfloatの0になるので、型を指定したい場合はdtypeを付ける。

# 2次元配列
a = np.zeros((2, 3))
array([[0., 0., 0.],
       [0., 0., 0.]])
a = np.zeros((2, 3), dtype=int)
array([[0, 0, 0],
       [0, 0, 0]])
# 1次元配列
a = np.ones(4)
array([1., 1., 1., 1.])
要素が全て1の配列を生成する。
第1引数には各次元の要素数を(2次元以上の場合はタプルで)指定する。

デフォルトではfloatの1になるので、型を指定したい場合はdtypeを付ける。

a = np.ones((2, 3), dtype=int)
array([[1, 1, 1],
       [1, 1, 1]])
a = np.empty((2, 3), dtype=int)
  中身を初期化しないで配列を生成する。
後から値を設定するのが前提で、初期化しない分高速らしい。
emptyを実行した直後は、各要素にはゴミが入ってる。
(が、emptyという名前は駄目だと思う…。allocとかの方が分かりやすかったのでは)
a = np.identity(3, dtype=int)
array([[1, 0, 0],
       [0, 1, 0],
       [0, 0, 1]])
単位行列を生成する。
第1引数には要素数を指定する。(単位行列なので、行数と列数は等しいため、1つだけ指定すればよい)
a = np.arange(4)
b = np.arange(6).reshape(2, 3)
c = np.arange(0, 1, 0.2)
array([0, 1, 2, 3])
array([[0, 1, 2],
       [3, 4, 5]])
array([0. , 0.2, 0.4, 0.6, 0.8])
rangeと同様に連番の値を生成する。
生成されるのは1次元配列なので、多次元にしたければreshapeで次元数を変更する。

rangeは整数しか使えないが、ndarrayのarangeは小数も可。

a = np.linspace(0, 1, 5)
array([0. , 0.25, 0.5 , 0.75, 1. ])
指定された範囲の、指定された要素数の1次元配列を生成する。
arangeと異なり、終端の値も含まれる。
(linspaceはlinear space、線形空間?)
a = np.random.rand(2)
a = np.random.random((3, 2))
array([0.65551951, 0.78661365])
array([[0.55355804, 0.23041181],
       [0.92495902, 0.59653668],
       [0.79859922, 0.38682015]])
0〜1の範囲(1は含まない)のランダムな値を生成する。
a = np.random.randn(3, 2)
array([[ 1.06148116, 1.43489637],
       [ 0.74288435, 1.79641704],
       [-0.06247452, 0.12258382]])
正規分布に則った乱数を生成する。
a = np.random.randint(1, 10, 4)

 

array([2, 7, 1, 8])
指定された範囲のランダムな整数を生成する。
randint(low, high, size)で、生成される乱数にはlowは含まれ、highは含まれない。

列数が1の行列を生成する方法

np.array([[1, 2], [3, 4]])」は2次元配列(行列)である。
行数が1の行列は「np.array([[1, 2, 3]])」となる。角括弧が二重になっているように見えるだけで、簡単に書ける。
列数が1の行列は「np.array([[1], [2], [3]])」となる。各要素に角括弧を付けなくてはならず、煩わしい。
そこで、通常のリスト[1, 2, 3]からarray([[1], [2], [3]])に変換する方法を考えてみる。

(なお、「np.array([1, 2, 3])」は1次元配列(ベクトル)であり、行列ではない)

説明
a = np.array([[1], [2], [3]])
これを生成したい。
a = np.array([[n] for n in [1, 2, 3]])
形式的に各要素を変換する方法。
a = np.array(list(map(lambda n: [n], [1, 2, 3])))
a = np.array([1, 2, 3]).reshape(3, 1)
reshapeを使うと、要素数を保ったまま、行数・列数を変更することが出来る。
2次元に変換する場合、引数に(行数, 列数)を指定する。
今回は列数を1にしたいので、(要素数, 1)を指定すればよい。
a = np.array([1, 2, 3]).reshape(-1, 1)
reshapeで行数を指定すると、リストの要素数を変えたときに合わせて変更しないといけないので面倒。
-1を指定すると、自動的に個数を算出してくれる。
a = np.array([1, 2, 3])[:, np.newaxis]
スライス記法を使って変換する方法。
角括弧内でカンマ区切りで出来る項の個数の次元の配列に変換される。([s1, s2]は2項なので2次元配列に変換される)
スライス記法でコロン「:」のみを指定した場合、全要素を意味する。
その他の次元の箇所には「np.newaxis」を指定する。
つまり「:, np.newaxis」は1次元目(行)が全要素数の2次元配列になる。
a = np.array([[1, 2, 3]]).T
a = np.array([[1, 2, 3]]).transpose()
a = np.transpose(np.array([[1, 2, 3]]))
転置行列を生成する(行と列を入れ替える)方法。
「行数が1(1行)の行列」を転置すると「列数が1(1列)の行列」になる。
(転置行列は[i, j]の位置を[j, i]に変換するので、元が行列(2次元配列)でなければならない)
 

操作

ndarrayの操作。

例の結果 説明 ビューかどうか
a = np.array([[1, 2, 3], [4, 5, 6]])
d = a.ndim # 次元数
s = a.size # 全要素数
2
6
配列の情報を取得する。  
a = np.array([[1, 2, 3], [4, 5, 6]])
s = a.shape
(2, 3)
各次元の要素数を取得する。  
a = np.array([[1, 2, 3], [4, 5, 6]])
b = a.reshape(3, 2)
array([[1, 2],
       [3, 4],
       [5, 6]])
reshapeは全体の要素数を変えずに
次元数や各次元の要素数を変えることが出来る。
ビュー
a = np.array([[1, 2], [3, 4]])
b = a.flatten()
b = a.ravel()
array([1, 2, 3, 4])
フラット化する。(1次元配列にする)
flattenは常に新しいndarrayを返す。
ravelはビューを返す。
 
flattenはコピー
ravelはビュー
a.dtype
dtype('int32')
要素の型(データの型)を取得する。  
a.itemsize
4
要素の型のサイズ(バイト数)を取得する。  
a = np.array([1, 2, 3])
b = np.array(a, dtype=float)
b = a.astype(float)
array([1., 2., 3.])
要素の型を変更した配列を生成する。 (コピー)
a = np.empty((2, 3), dtype=int)
a.fill(2)

 

array([[2, 2, 2],
       [2, 2, 2]])
全要素を指定した値にする。  
a = np.array([1, 2, 3])
b = np.zeros_like(a)
array([0, 0, 0])
次元数が同じで全要素が0の配列を生成する。
zeros
(コピー)
a = np.array([1, 2, 3])
b = np.ones_like(a)
array([1, 1, 1])
次元数が同じで全要素が1の配列を生成する。
ones
(コピー)
b = a.copy()
  ディープコピーされた新しい配列を生成する。 コピー
b = a.view()
  ビューを返す。 ビュー
a = np.array([[1, 2, 3], [4, 5, 6]])
l = a.tolist()
[[1, 2, 3], [4, 5, 6]]
listを返す。 コピー
a = np.array([[1, 2, 3], [4, 5, 6]])
sum = a.sum() # 全合計
s0 = a.sum(axis=0)
s1 = a.sum(axis=1)
21
array([5, 7, 9])
array([ 6, 15])
全要素を合計する。
axisを指定すると行毎や列毎の合計を算出する。

minやmax等もある。

 
xs = np.linspace(0, np.pi, 100)
ys = np.sin(xs) 
  三角関数なども用意されている。
各要素に対して三角関数を適用する。
 
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.vstack([a, b])
array([[1, 2],
       [3, 4],
       [5, 6],
       [7, 8]])
下側に行列を結合する。(行を追加する)
(→分割はスライスを使用する)

行を追加する場合は1次元配列でも良いようだ。

(コピー)
a = np.array([[1, 2], [3, 4]])
b = np.array([5, 6])
c = np.vstack([a, b])
array([[1, 2],
       [3, 4],
       [5, 6]])
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.hstack([a, b])
array([[1, 2, 5, 6],
       [3, 4, 7, 8]])
右側に行列を結合する。(列を追加する)
(→分割はスライスを使用する)
(コピー)
a = np.array([[1, 2], [3, 4]])
b = np.array([[5], [6]])
c = np.hstack([a, b])
array([[1, 2, 5],
       [3, 4, 6]])

ビュー

ndarrayを返す操作の場合、返されたndarrayがビューかどうかに注意する必要がある。
ビューの場合、そのndarrayの値を更新すると、元のndarrayも更新される。

  copyメソッド viewメソッド スライス記法
備考 copyメソッドはディープコピーする。 viewメソッドはビューを返す。 ndarrayのスライスはビューを返す。
a = np.array([[1, 2], [3, 4]])
b = a.copy()
a = np.array([[1, 2], [3, 4]])
b = a.view()
a = np.array([[1, 2], [3, 4]])
b = a[:]
b.flags.owndata True False False
b.base is a False True True
b.base None (aのオブジェクト) (aのオブジェクト)
b更新後のa
b[0, 0] = 9
array([[1, 2],
       [3, 4]])
array([[9, 2],
       [3, 4]])
array([[9, 2],
       [3, 4]])

スカラー演算

ndarrayは数値(数学で言えばスカラー)と演算することが出来る。

a = np.array([[1, 2], [3, 4]])
b = a * 10

↓bの結果

array([[10, 20],
       [30, 40]])

乗算以外の演算も各要素への演算になる。
数学の行列ならスカラーとの加算は出来ないが、ndarrayは出来るようになっている。

a = np.array([[1, 2], [3, 4]])
b = a + 10

↓bの結果

array([[11, 12],
       [13, 14]])

四則演算どころか、比較演算も行うことが出来る(笑)

a = np.array([[1, 2], [3, 4]])
b = a >= 4

↓bの結果

array([[False, False],
       [False,  True]])

比較演算の結果は、boolの配列になる。

&, |~(not相当)も使うことが出来る。
ただし演算子の優先順位は==や>=より&,|の方が高いので、組み合わせる場合は注意。(比較演算の項を丸括弧で囲む 必要がある)

a = np.array([[1, 2], [3, 4]])
b = (a <= 1) | (a >= 4)

↓bの結果

array([[ True, False],
       [False,  True]])

boolの配列(ndarray)を添字にするとTrueの箇所の要素だけ取り出せるので、意外と使われるらしい。

a = np.array([[1, 2], [3, 4]])
b = (a <= 1) | (a >= 4)
c = a[b]
a = np.array([[1, 2], [3, 4]])

c = a[a % 2 == 0]
↓cの結果 ↓cの結果
array([1, 4])
array([2, 4])

配列同士の演算

普通の演算子によるndarray同士の演算は、各要素同士の演算となる。
加減算ならそれでいいが、乗算は数学とは異なった結果になる。

a = np.array([[2, 3], [0, 4]])
b = np.array([[5, 0], [6, 7]])
c = a * b

↓cの結果

array([[10,  0],
       [ 0, 28]])

行列同士の乗算を行いたい場合はdotメソッドを使用する。

a = np.array([[2, 3], [0, 4]])
b = np.array([[5, 0], [6, 7]])
c = a.dot(b)

↓cの結果

array([[28, 21],
       [24, 28]])

また、「@」演算子でも行列同士の乗算が出来る。(Python標準の数値演算では「@」演算子の使い道は無いが、ndarrayでは使用できる)

c = a @ b

添字による操作

ndarrayも添字「a[n]」による値の取得や設定が出来る。

a = np.array([11, 22, 33])
print(a[1])
a[1] = 9
print(a)

ビューのndarrayの場合、その値を更新すると元のndarrayまで更新されるので注意。


Pythonのlistで2次元配列を扱う場合、以下のように添字の角括弧を並べる。

a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
a[1][2] = 10

ndarrayでもそのような書き方は出来るが、1つの角括弧内にカンマ区切りで全次元の添字を指定する書き方がよく使われる。
(Pythonではこの書き方が文法的に許されているが、listは対応していない)

a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
a[1, 2] = 10

boolの配列(ndarray)を添字に指定すると、Trueに該当する値だけ取得できる。

a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([[True, False, False], [False, True, False], [False, False, True]])
c = a[b]

↓cの結果

array([1, 5, 9])

スライス操作

ndarrayもスライス操作を行うことが出来る。

なお、スライスによって取得したndarrayはビューなので、それに対する更新は元のndarrayにも反映される。
listではa[:]で新しいオブジェクトが作られ、それを更新しても元のlistには影響 は無いが、ndarrayは異なる)
(内容が同じで新しいndarrayが欲しい場合はcopyを使う)

例の結果 説明
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[0:2]
array([[1, 2, 3],
       [4, 5, 6]])
指定行を取得する例。
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[0:2, 1:2]
array([[2],
       [5]]
指定された行・列の範囲を取得する例。
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[:, 0:2]
array([[1, 2],
       [4, 5],
       [7, 8]])
指定列を取得する例。
「コロン「:」のみ」は、全要素を表す。
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[:, 0]
array([1, 4, 7])
添字指定との組み合わせ。
応用例 応用例の結果 説明
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[0:1]
array([[1, 2, 3]])
一番上の行を取得する。
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[1:]
array([[4, 5, 6],
       [7, 8, 9]])
一番上以外の行を取得する。
(→上下方向の結合はvstack
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[:, 0:1]
array([[1],
       [4],
       [7]])
一番左の列を取得する。
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = a[:, 1:]
array([[2, 3],
       [5, 6],
       [8, 9]])
一番左以外の列を取得する。
(→左右方向の結合はhstack
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
a[1] = 10
array([[ 1,  2,  3],
       [10, 10, 10],
       [ 7,  8,  9]])
指定行の一括更新。
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
a[:, 1] = 10
array([[ 1, 10, 3],
       [ 4, 10, 6],
       [ 7, 10, 9]])
指定列の一括更新。

NumPyへ戻る / Pythonへ戻る / 技術メモへ戻る
メールの送信先:ひしだま