[NumPy超入門]多次元配列「ndarray」の高度なインデックス指定に触れてみようPythonデータ処理入門

NumPyが提供する多次元配列の要素を選択するために、その整数値のインデックスを配列で与えたり、ブーリアン値の配列を与えたりする方法を紹介します。覚えると便利に使えるはずです。

» 2023年07月21日 05時00分 公開
[かわさきしんじDeep Insider編集部]

この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。

「Pythonデータ処理入門」のインデックス

連載目次

連載概要

 本連載はPythonについての知識を既にある程度は身に付けている方を対象として、Pythonでデータ処理を行う上で必須ともいえるNumPyやpandas、Matplotlibなどの各種ライブラリの基本的な使い方を学んでいくものです。そして、それらの使い方をある程度覚えた上で、それらを活用してデータ処理を行うための第一歩を踏み出すことを目的としています。


 前回はNumPyが提供する多次元配列「ndarray」に格納されている要素をインデックスやスライスを指定して選択する基本的な方法を紹介しました。今回は多次元配列に格納されている要素をより柔軟な形で選択する方法を見ていきます。

高度なインデックス指定またはファンシーインデクシング

 NumPyの多次元配列「ndarray」では、選択したい要素を表すインデックスを含んだ配列(やPythonのリストなど)を角かっこ「[]」の中に記述することも可能です。と書くと分かりにくいのでさっそく簡単な例を示しましょう。例なので簡単に一次元配列を扱ってみます。

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

一次元配列

 このような配列があったときに、0番目、8番目、5番目、1番目の要素をこの順番で取り出す(選択する)ときに次のようなコードを書けるということです。

idx = np.array([0, 8, 5, 1])
result = a[idx]
print(result)  # [0 8 5 1]

0番目、8番目、5番目、1番目の要素を選択するコード

 最初の行では、選択したい要素があるインデックスを含んだ配列を作成しています。そして、2行目ではそれを配列aのインデックスとして指定することで、その結果を取り出しています。ここでは「インデックスの値=要素の値」となっているので、インデックスの値と同じ値が得られています。

 このように、インデックス指定にndarrayオブジェクトを使用して、複数の要素を選択する方法をNumPyのドキュメントでは「高度なインデックス指定」(advanced indexing)または「ファンシーインデクシング」と呼んでいます。

 高度なインデックス指定では、インデックス指定に使用する配列の要素として整数かブーリアン値(True/False)が使えます。後者については後ほど紹介することにしましょう。

 また、インデックスを指定するためのオブジェクトとしてはndarrayやタプル以外のシーケンス(リストなど)、最低でも1つのシーケンスまたはndarrayオブジェクトを含むタプルを使用できます。以下に例を示します。

print(a[[0, 8, 5, 1]])  # タプル以外のシーケンス
print(a[np.array([0, 8, 5, 1])])  # ndarrayオブジェクト
print(a[(0, 8, 5, 1),])  # シーケンスを要素とするタプル
print(a[(0, 8, 5, 1)])  # IndexError:a[0, 8, 5, 1]と同じ。1次元配列なのに4つの次元を指定

高度なインデックス指定の例

 なお、高度なインデックス指定で値を選択(取得)した場合、それらはビューではなく元のデータのコピーとなる点には注意してください。以下はその例です。

tmp = a[[1, 2]]
print(tmp)  # [1 2]
tmp[0] = 11
print(tmp)  # [11 2]
print(a)  # [0 1 2 3 4 5 6 7 8 9]:変更が元データには反映されない

高度なインデックス指定で取得できるのは元データのコピー

 ただし、高度なインデックス指定により選択された要素に値を代入する場合には、元のデータが変更されます。

a[[1, 2]] = np.array([11, 22])
print(a)  # [ 0 11 22  3  4  5  6  7  8  9]:選択した要素への代入は元データに反映される

高度なインデックス指定で選択した要素への代入は元データに反映される

2次元配列で高度なインデックス指定を使用する

 2次元配列に対して高度なインデックス指定を使う場合には、少し注意が必要です。つまり、「[行インデックス, 列インデックス]」のような指定の仕方とは異なり、インデックス指定に使用する配列には次元ごと(行ごと、列ごと)にインデックスをまとめます。

 例えば、次のような3行4列の2次元配列があったとしましょう。

a = np.arange(12).reshape((3, 4))
print(a)
# 出力結果:
#[[ 0  1  2  3]
# [ 4  5  6  7]
# [ 8  9 10 11]]

2次元配列

 このときに0行0列(0)、1行1列(5)、2行2列(10)の値を選択するには次のようなインデックスを指定します。

rows = np.array([0, 1, 2])
columns = np.array([0, 1, 2])
result = a[rows, columns]
print(result)  # [ 0  5 10]

高度なインデックスでは次元ごとにインデックスの値をまとめる

 この例では、インデックス指定に使用する配列を2つ作成しています(rowsとcolumns)。その値はいずれも[0 1 2]という多次元配列です。そして、これらに行ごと列ごとのインデックスが格納されています。第0要素は共に0なので0行0列を、第1要素は共に1なので1行1列を、第2要素は共に2なので2行2列を示しています。得られた値もそれに対応したものになっていることを確認してください。

高度なインデックス指定でブーリアン値を要素とする配列を使う

 冒頭でも述べましたが、高度なインデックス指定ではブーリアン値を要素とする配列(リストなど)も使用できます。このときには、インデックスに使用する配列の要素のうち、値がTrueのものに対応する要素が選択され、Falseに対応する要素が選択されないということになります。文字だと分かりにくいので簡単な例を示しましょう。

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。