[NumPy] 部分配列へのアクセス

[NumPy] 部分配列へのアクセス

スライシング

1 次元配列のスライシング

 Python のリストと同じように、配列も次の構文でスライシングを行なって部分配列を抽出することができます。

 x[start=0 : stop=size : step=1]

 start には開始インデックス、stop には終了インデックス + 1 を渡します。
 step は start から何個おきに要素を取り出すかという引数です。
 引数はすべてオプションなので省略できます。たとえば、x[:5] は x[0:5] と等価です(インデックス 0 から 4 まで取り出すという意味です)。

# リストA-1

# 1次元配列のスライス

# x = [0 1 2 3 4 5 6 7 8 9]
x = np.arange(10)

# インデックス0~2
print("x[:3] ⇒", x[:3])

# インデックス7以降すべて
print("x[7:] ⇒", x[7:])

# インデックス5~8
print("x[5:9] ⇒", x[5:9])

# 2個おきの要素
print("x[::2] ⇒", x[::2])

# インデックス4から2個おきの要素
print("x[4::2] ⇒", x[4::2])
x[:3] ⇒ [0 1 2]
x[7:] ⇒ [7 8 9]
x[5:9] ⇒ [5 6 7 8]
x[::2] ⇒ [0 2 4 6 8]
x[4::2] ⇒ [4 6 8]

 
 step に負の値を渡すと start と stop が入れ替わって、逆順に要素を取り出します。

# リストA-2

# 1次元配列のスライス

# x = [0 1 2 3 4 5 6 7 8 9]
x = np.arange(10)

# 末尾から逆順にすべての要素
print("x[::-1] ⇒", x[::-1])

# 末尾から逆順に3つおきの要素
print("x[::-3] ⇒", x[::-3])

# インデックス5から逆順にすべての要素
print("x[5::-1] ⇒", x[5::-1])
x[::-1] ⇒ [9 8 7 6 5 4 3 2 1 0]
x[::-3] ⇒ [9 6 3 0]
x[5::-1] ⇒ [5 4 3 2 1 0]

 

多次元配列のスライシング

 多次元配列をスライシングする場合は、軸ごとにカンマ (,) で区切りながらスライス位置を指定します。

 x[start:stop:step, start:stop:step, ...]

 実例を見てみましょう。準備として、乱数を使って 5 × 5 の配列を作成しておきます(大きな配列が必要なときに乱数は便利です)。

# リストB-1

# NumPyをインポート
import numpy as np

# 乱数seedを10に設定
np.random.seed(10)

# 乱数を使って5×5の配列を作成
x = np.random.randint(1, 100, (5, 5))

# seedを固定しているので、必ず次の配列が生成されます

#[[10 16 65 29 90]
# [94 30  9 74  1]
# [41 37 17 12 55]
# [89 63 34 73 79]
# [50 52 55 78 70]]

 1 ~ 2 行目, 1 ~ 3 列目をスライシングして部分配列を取り出してみます。

# リストB-2

# 配列x
# [[10 16 65 29 90]
#  [94 30  9 74  1]
#  [41 37 17 12 55]
#  [89 63 34 73 79]
#  [50 52 55 78 70]]

# 1~2行目,1~3列目をスライシング
print(x[:2, :3])
[[10 16 65]
 [94 30  9]]

 2 ~ 3行目, 3 ~4 列目をスライシングします。

# リストB-3

# 配列x
# [[10 16 65 29 90]
#  [94 30  9 74  1]
#  [41 37 17 12 55]
#  [89 63 34 73 79]
#  [50 52 55 78 70]]

# 2~3行目, 3~4列目をスライシング
print(x[1:3, 3:4])
[[ 9 74]
 [17 12]]

 
 最初の行を取り出すコードです。

# リストB-4

# 配列x
# [[10 16 65 29 90]
#  [94 30  9 74  1]
#  [41 37 17 12 55]
#  [89 63 34 73 79]
#  [50 52 55 78 70]]

# 最初の行をスライシング
print(x[0, :])
[10 16 65 29 90]

 
 最初の列を取り出すコードです。

# リストB-5

# 配列x
# [[10 16 65 29 90]
#  [94 30  9 74  1]
#  [41 37 17 12 55]
#  [89 63 34 73 79]
#  [50 52 55 78 70]]

# 最初の列をスライシング
print(x[:, 0])
[10 94 41 89 50]

 

独学プログラマー Python言語の基本から仕事のやり方まで

中古価格
¥1,537から
(2019/7/30 20:25時点)

ファンシーインデックス

 ファンシーインデックスはインデックスのリストまたは配列を渡して複数の要素に同時アクセスします。スライシングよりも複雑な参照を可能にしますが、copy を作成するので実行速度は遅くなります。

 最初に 1 次元配列のファンシーインデックスを試してみましょう。
 次のような素数の並ぶ配列をつくっておきます。

# リストC-1

# NumPyをインポート
import numpy as np

# 1次元配列を定義
x = np.array([2, 3, 5, 7, 9, 11, 13, 17, 19])

print(x)
[2  3  5  7  9 11 13 17 19]

 
 この配列からインデックス 7, 2, 4 の要素を取り出すときには、次のようなコードを書きます。

# リストC-2

# 配列x
# [ 2  3  5  7  9 11 13 17 19]

# インデックスのリストを作成
x_index = [7, 2, 4]

print(x[x_index])
[17 5 9]

 
 新しい配列の形状を自由に設定することができます。

# リストC-3

# 配列x
# [ 2  3  5  7  9 11 13 17 19]

# インデックス配列を作成
x_index = np.array([[5, 1],
                    [8, 3]])

print(x[x_index])
[[11 3]
 [19 7]]

 
 次は多次元配列のファンシーインデックスの実例を見てみます。
 準備として seed = 20 の疑似乱数で配列をつくっておきます。

# リストD-1

# NumPyをインポート
import numpy as np

# 乱数seedを20に設定
np.random.seed(20)

# 乱数を使って5×5の配列を作成
x = np.random.randint(1, 100, (5, 5))

# seedを固定しているので、必ず次の配列が生成されます

#[[91 16 96 29 91]
# [10 21 76 23 72]
# [35 97 41 86 91]
# [27 84 17 63 17]
# [ 8 99  7 27 14]]

 
 この配列のインデックス (3, 1) の要素は 84, インデックス (4, 4) の要素は 14 です。これらの要素を並べた部分配列を作成するときは次のようなコードを書きます。

# リストD-2

# 配列x
# [[91 16 96 29 91]
#  [10 21 76 23 72]
#  [35 97 41 86 91]
#  [27 84 17 63 17]
#  [ 8 99  7 27 14]]

# 行方向のインデックスリスト
x_row = [3, 4]

# 列方向のインデックスリスト
x_col = [1, 4]

y = x[x_row, x_col]

print(y)
[84 14]

 
 ファンシーインデックスは普通のインデックスと組合わせて使用することができます。たとえば、配列の 2 行目のインデックス 4, 0, 2 の要素を参照したい場合は次のように書くことができます。

# リストD-3

# 配列x
# [[91 16 96 29 91]
#  [10 21 76 23 72]
#  [35 97 41 86 91]
#  [27 84 17 63 17]
#  [ 8 99  7 27 14]]

# 2 行目のインデックス4,0,2の要素を参照
print(x[1, [4, 0, 2]])
[72 10 76]

 
 スライシングとファンシーインデックスを組合わせることもできます。2 行目以降のインデックス 4, 0, 2 の列を参照する場合は次のようなコードを記述します。

# リストD-4

# 配列x
# [[91 16 96 29 91]
#  [10 21 76 23 72]
#  [35 97 41 86 91]
#  [27 84 17 63 17]
#  [ 8 99  7 27 14]]

# 2 行目以降のインデックス4,0,2の列を参照
print(x[1:, [4, 0, 2]])
[[72 10 76]
 [91 35 41]
 [17 27 17]
 [14  8  7]]

 

numpy.take()

 numpy.take() は任意の軸 (axis に指定した軸) に沿って要素を抽出する関数です。
 seed = 31 の乱数配列をつくって関数の機能を確認してみましょう。

# リストE-1

# NumPyをインポート
import numpy as np

# 乱数seedを31に設定
np.random.seed(31)

# 乱数を使って5×5の配列を作成
x = np.random.randint(1, 100, (5, 5))

# seedを固定しているので、必ず次の配列が生成されます

#[[83 88 17 99 24]
# [59 29 94 93 43]
# [47  7 83 19 26]
# [82 24 29 11 85]
# [45 76 55 19 25]]

 
 axis に 0 を渡すと、第 2 引数のインデックスリストに対応する行が抽出されます。

# リストE-2

# 配列x
# [[83 88 17 99 24]
#  [59 29 94 93 43]
#  [47  7 83 19 26]
#  [82 24 29 11 85]
#  [45 76 55 19 25]]

# 4,1,3行目を取り出す
y = np.take(x, [3, 0, 2], axis = 0)

print(y)
[[82 24 29 11 85]
 [83 88 17 99 24]
 [83 88 17 99 24]

 
 axis に 1 を渡すと、第 2 引数のインデックスリストに対応する列が抽出されます。

# リストE-2

# 4,1,3列目を取り出す
z = np.take(x, [3, 0, 2], axis = 1)

print(z)
[[99 83 17]
 [93 59 94]
 [19 47 83]
 [11 82 29]
 [19 45 55]]

 
 axis を省略すると、最初の行からインデックスリストに対応する要素が抽出されます。

# リストE-3

# 配列x
# [[83 88 17 99 24]
#  [59 29 94 93 43]
#  [47  7 83 19 26]
#  [82 24 29 11 85]
#  [45 76 55 19 25]]

# 4,1,3行目を取り出す
w = np.take(x, [3, 0, 2])

print(w)
[99 83 17]