トップページに戻る

Numpyの基礎(5)要素の参照

In [1]:
import numpy as np
import matplotlib.pyplot as plt

インデックス参照

Numpyでは、部分配列の取り出し方に様々な種類があり、使いこなせると便利です。

ただ、取り出し方がコピーであるかビュー(参照)であるかを混乱することがあるので、チェックしておいてください。

スライス

Pythonのリストやタプルにも実装されているスライスをndarrayに対してするとその部分配列がビュー(参照)で返ってきます。
つまり、その取り出した部分配列を書き換えると元々の配列も変わるということです。
スライスは高速に動作するので、使えるときはガンガン使っていきましょう。
一例を見てみます。

In [2]:
arr = np.arange(16).reshape(4, 4)
arr
Out[2]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11],
       [12, 13, 14, 15]])
In [3]:
# スライスした部分配列(行1, 2)の要素に全て-1を代入する
arr[1:3] = 0

# オリジナルの配列
arr
Out[3]:
array([[ 0,  1,  2,  3],
       [ 0,  0,  0,  0],
       [ 0,  0,  0,  0],
       [12, 13, 14, 15]])
In [4]:
# 全て0に初期化(配列を再生成せず、要素のみ書き換えている)
arr[:]  = 0
arr
Out[4]:
array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]])

使い道としては、各軸の値をなんらかの計算をしたあと、再び代入するのに使います。

In [5]:
# 一様乱数
arr = np.random.rand(3, 4)
arr
Out[5]:
array([[0.22890502, 0.68784233, 0.26970968, 0.81251457],
       [0.38607569, 0.69188688, 0.82089348, 0.6418809 ],
       [0.25985675, 0.93964556, 0.25518254, 0.11833234]])
In [6]:
# 3行目を書き換える
arr[2] = arr[2] - 1
arr
Out[6]:
array([[ 0.22890502,  0.68784233,  0.26970968,  0.81251457],
       [ 0.38607569,  0.69188688,  0.82089348,  0.6418809 ],
       [-0.74014325, -0.06035444, -0.74481746, -0.88166766]])
In [7]:
# 2列めを書き換える
arr[:, 1] = arr[:, 1] + arr[:, 2]
arr
Out[7]:
array([[ 0.22890502,  0.95755201,  0.26970968,  0.81251457],
       [ 0.38607569,  1.51278037,  0.82089348,  0.6418809 ],
       [-0.74014325, -0.8051719 , -0.74481746, -0.88166766]])

オリジナルの配列が変わってほしくないときは、copy関数で同じ配列を複製できます。

In [8]:
arr_copied = arr.copy()
arr_copied
Out[8]:
array([[ 0.22890502,  0.95755201,  0.26970968,  0.81251457],
       [ 0.38607569,  1.51278037,  0.82089348,  0.6418809 ],
       [-0.74014325, -0.8051719 , -0.74481746, -0.88166766]])

代入時の注意点

配列に値や配列を代入するときには、意識的にスライスを使うようにした方がいいです。
例えばスカラーを配列の全要素に代入しようとしたとき、スライスを使わないといけません。

In [9]:
a = np.arange(10)
a
Out[9]:
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
In [10]:
# ↓-100をaの全要素に代入するつもりで書いたら
# 配列ですらなくなっている!?
a = -100
a
Out[10]:
-100
In [11]:
# 正しくは
a = np.arange(10)
a[:] = -100
a
Out[11]:
array([-100, -100, -100, -100, -100, -100, -100, -100, -100, -100])

配列として扱いたい場合は、スライスを使った代入を心がけましょう。

ファンシーインデックス参照

スライスよりも柔軟に部分配列を取り出せるインデックス参照がファンシーインデックス参照です。
配列(ndarrayやリスト、タプルなど)をインデックスに入れると、部分配列(コピー)を返すという機能です。
Numpyの使いやすさを格段に上げる機能であり、使う機会も多くなると思います。  
インデックスに配列を入れることで、対応する軸を抜き出すということをしてくれます。

In [12]:
a = np.arange(12).reshape(4, 3)
a
Out[12]:
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11]])
In [13]:
# ファンシーインデックス参照の一例(行1, 3を抜き出す)
a[[1, 3]]
Out[13]:
array([[ 3,  4,  5],
       [ 9, 10, 11]])
In [14]:
# ファンシーインデックス参照の一例(列0, 1を抜き出す)
a[:, [0, 1]]
Out[14]:
array([[ 0,  1],
       [ 3,  4],
       [ 6,  7],
       [ 9, 10]])
In [15]:
# ファンシーインデックス参照を使った代入の例(列0, 1をそれぞれ10倍する)
a[:, [0, 1]] = a[:, [0, 1]] * 10
a
Out[15]:
array([[  0,  10,   2],
       [ 30,  40,   5],
       [ 60,  70,   8],
       [ 90, 100,  11]])

ファンシーインデックス参照は直接代入するときは上のようにビューとして振る舞いますが、変数に格納するときはコピーされます。

In [16]:
a
Out[16]:
array([[  0,  10,   2],
       [ 30,  40,   5],
       [ 60,  70,   8],
       [ 90, 100,  11]])
In [17]:
# 列0, 1を抜き出す(コピーになる)
b = a[:, [0, 1]]
b
Out[17]:
array([[  0,  10],
       [ 30,  40],
       [ 60,  70],
       [ 90, 100]])
In [18]:
# 全要素を0
b[:] = 0
b
Out[18]:
array([[0, 0],
       [0, 0],
       [0, 0],
       [0, 0]])
In [19]:
# もともとの配列は書き換わらない(コピーされている!)
a
Out[19]:
array([[  0,  10,   2],
       [ 30,  40,   5],
       [ 60,  70,   8],
       [ 90, 100,  11]])

boolによるインデクシング

ある条件の値だけを抜き出すといったとき、boolによるインデクシングが利用できます。

In [20]:
# 10個の正規分布乱数
rand_values = np.random.randn(15)
rand_values
Out[20]:
array([ 1.40562262,  2.18500993, -1.28739425, -1.84938439,  0.1062642 ,
        2.07009035, -0.5709186 ,  0.64799021, -1.04017722, -1.31383057,
       -0.48778579, -0.87887693, -0.80728957, -1.52304387,  1.05330011])

1より大きい値を抜き出したい場合、以下のような真偽値の配列を作り、それをインデックスにわたすことでTrueの場所だけを抜き出すことができます。

In [21]:
rand_values >= 1
Out[21]:
array([ True,  True, False, False, False,  True, False, False, False,
       False, False, False, False, False,  True])
In [22]:
# 1より大きい値を抜き出す
rand_values[rand_values >= 1]
Out[22]:
array([1.40562262, 2.18500993, 2.07009035, 1.05330011])

これを応用すると、2次元配列で特定の条件の列(行)を抜き出すといったことが簡単にできます。

In [23]:
rand_2d = rand_values.reshape(3, 5)
rand_2d
Out[23]:
array([[ 1.40562262,  2.18500993, -1.28739425, -1.84938439,  0.1062642 ],
       [ 2.07009035, -0.5709186 ,  0.64799021, -1.04017722, -1.31383057],
       [-0.48778579, -0.87887693, -0.80728957, -1.52304387,  1.05330011]])
In [24]:
# 1より大きい値を少なくとも1つ含む列を抜き出す
rand_2d[:, (rand_2d > 1).any(axis=0)]
Out[24]:
array([[ 1.40562262,  2.18500993,  0.1062642 ],
       [ 2.07009035, -0.5709186 , -1.31383057],
       [-0.48778579, -0.87887693,  1.05330011]])

any関数は、一つでもTrueがあればTrueを返す関数です。
これにaxis=0を指定すると、列にTrueが含まれているかを真偽値で返す関数になります。

In [25]:
(rand_2d > 1).any(axis=0)
Out[25]:
array([ True,  True, False, False,  True])

基礎編

応用編