NumPyの「ファンシーインデックス」を使いこなす!高度な選択と代入
NumPy配列(ndarray)は、大量の数値データを効率的に扱うための強力なツールです。基本的なインデックス参照やスライスに加え、より複雑な要素の選択や代入を可能にするのが、ファンシーインデックス(Fancy Indexing) です。このテクニックをマスターすれば、データ処理の幅が大きく広がります。
ファンシーインデックスとは?
ファンシーインデックスは、整数の配列(リストやNumPy配列) をインデックスとして使用し、配列から特定の要素や行、列を非連続的に、または重複して選択するNumPyの機能です。一般的なスライスとは異なり、返される配列は常に元の配列のコピーであり、ビューではありません。
ファンシーインデックスの基本
一次元配列を使って、ファンシーインデックスの基本を見てみましょう。
import numpy as np
arr = np.array([10, 20, 30, 40, 50, 60])
# インデックス0, 3, 5の要素を選択
selected_elements = arr[[0, 3, 5]]
print(f"選択された要素: {selected_elements}") # 出力: [10 40 60]
このように、角括弧 [] の中にインデックスのリストを渡すだけで、非連続な要素を簡単に選択できます。
多次元配列でのファンシーインデックス
ファンシーインデックスは、多次元配列に対して使う場合に特に強力です。行や列をまとめて選択したり、不規則な位置にある要素を抽出したりできます。
特定の行の選択
リストでインデックスを渡すことで、特定の行をまとめて選択できます。
import numpy as np
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10, 11, 12]])
# 0番目と2番目の行を選択
selected_rows = arr_2d[[0, 2]]
print(f"選択された行:\n{selected_rows}")
# 出力:
# [[1 2 3]
# [7 8 9]]
特定の列の選択
列を選択する場合は、行のインデックスに : を使い、列のインデックスにリストを指定します。
import numpy as np
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 0番目と2番目の列を選択
selected_cols = arr_2d[:, [0, 2]]
print(f"選択された列:\n{selected_cols}")
# 出力:
# [[1 3]
# [4 6]
# [7 9]]
複数の次元を同時に選択
最も強力な使い方は、複数の次元に対してインデックス配列を同時に指定することです。この場合、指定されたインデックスのペアに対応する要素が抽出されます。
import numpy as np
arr_2d = np.array([[10, 20, 30],
[40, 50, 60],
[70, 80, 90]])
# (0,0), (1,2), (2,1) の要素を選択
# row_indices = [0, 1, 2]
# col_indices = [0, 2, 1]
elements = arr_2d[[0, 1, 2], [0, 2, 1]]
print(f"不規則な位置の要素: {elements}") # 出力: [10 60 80]
結果の形状は、インデックス配列の形状になります。上記の例では、1次元のインデックス配列を与えたため、結果も1次元配列になります。
ファンシーインデックスによる代入
ファンシーインデックスで選択した部分に対して、新しい値を代入することも可能です。これも非常に柔軟なデータ操作を可能にします。
選択した要素への代入
import numpy as np
arr = np.array([10, 20, 30, 40, 50, 60])
# インデックス0, 3, 5の要素をそれぞれ100, 200, 300に代入
arr[[0, 3, 5]] = [100, 200, 300]
print(f"代入後の配列: {arr}")
# 出力: [100 20 30 200 50 300]
# 一つの値で複数の要素を代入
arr[[1, 4]] = 99
print(f"さらに代入後の配列: {arr}")
# 出力: [100 99 30 200 99 300]
複数次元の不規則な位置への代入
複数の次元のインデックスを使って選択した要素に代入する場合も同様です。
import numpy as np
arr_2d = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# (0,0), (1,2), (2,1) の要素に新しい値を代入
arr_2d[[0, 1, 2], [0, 2, 1]] = [1000, 2000, 3000]
print(f"代入後の配列:\n{arr_2d}")
# 出力:
# [[1000 2 3]
# [ 4 5 2000]
# [ 7 3000 9]]
ファンシーインデックスと他のインデックス参照の違い
-
スライス(Slice Indexing): 連続した範囲を指定し、通常は元の配列のビューを返します。これにより、メモリ効率が良いですが、ビューの変更は元の配列に影響します。
-
ブールインデックス(Boolean Indexing): 条件式の結果としてのブール配列を使って要素を選択します。ファンシーインデックスと同様にコピーを返します。
-
ファンシーインデックス(Fancy Indexing): 明示的なインデックスのリスト(配列)を使って、非連続な要素を選択します。常にコピーを返します。元の配列を変更せず操作したい場合に便利です。
各インデックス参照方法にはメリット・デメリットがあり、用途によって使い分けることが重要です。ファンシーインデックスは、特定の要素を柔軟に選択・操作したい場合に非常に有効です。
まとめ
NumPyのファンシーインデックスは、リストやNumPy配列でインデックスを指定することで、配列から非連続的な要素を抽出したり、柔軟に値を代入したりできる高度な機能です。特に多次元配列の複雑な操作や、条件に基づかない特定の要素の選択に威力を発揮します。この強力なテクニックをマスターし、NumPyでのデータ処理を次のレベルに引き上げましょう!

