NumPy におけるインデックス入門

NumPyBeginner
オンラインで実践に進む

はじめに

おかえりなさい!NumPy 配列の作成方法を理解したので、次は配列内のデータにアクセスし、操作する方法を学びましょう。この実験では、NumPy 配列内のデータにアクセスおよび変更するための主要な方法であるインデックス参照について学びます。

学んだことの応用

前回の実験では、以下のことを学びました。

  • さまざまな NumPy 関数を使用した配列の作成方法
  • 配列と Python リストの違い
  • 配列の次元(1D、2D、3D)とデータ型

今回は以下のことを学びます。

  • 個々の要素または要素のグループへのアクセス方法
  • 配列から特定の行、列、または領域を抽出する方法
  • 複雑なデータフィルタリングのための高度な選択テクニックの使用方法
  • インデックス参照による配列データの変更方法

インデックス参照が重要な理由

インデックス参照は、以下のことが可能になるため、データ操作の基本となります。

  • 分析のためにデータのサブセットを抽出する
  • 大規模データセットの特定の値を変更する
  • 条件に基づいてデータをフィルタリングする
  • 選択された要素に対してベクトル化された操作を実行する

これらのスキルは、Python でのあらゆるデータ分析または科学計算タスクに不可欠です。

1 次元配列における基本的なインデックスとスライシング

配列の作成方法がわかったので、次は配列の内容にアクセスする方法を学びましょう。この実験でのすべての作業は、indexing_practice.py ファイルで行います。

配列のインデックス参照の理解

ゼロベースインデックス参照

Python のリストと同様に、NumPy 配列はゼロベースインデックス参照を使用します。これは次のことを意味します。

  • 最初の要素はインデックス 0 にあります
  • 2 番目の要素はインデックス 1 にあります
  • 以下同様...

配列インデックスの視覚化

配列 x = [10, 20, 30, 40, 50] の場合:

Index:  0   1   2   3   4
Value: 10  20  30  40  50

したがって、x[0]10 を返し、x[2]30 を返します。

スライシングの構文

スライシングを使用すると、start:stop:step の構文で要素の範囲を選択できます。

  • start: スライスが始まるインデックス(含まれる)
  • stop: スライスが終わるインデックス(含まれない)
  • step: スキップする要素数(オプション、デフォルトは 1)

一般的なスライシングパターン:

  • x[1:4]: インデックス 1、2、3 の要素
  • x[:3]: 最初の 3 要素(インデックス 0、1、2)
  • x[2:]: インデックス 2 から最後まで
  • x[::2]: インデックス 0 から始まる 2 つおきの要素
  • x[::-1]: 配列全体を反転

まず、エディタで indexing_practice.py ファイルを開きます。次に、その内容全体を以下のコードに置き換えます。このコードは 1 次元配列を作成し、単一の要素と要素のスライスにアクセスする方法を示しています。

import numpy as np

## 0 から 9 までの数値を持つ 1 次元配列を作成
x = np.arange(10)
print("Original array:", x)

## インデックス 2 の単一要素にアクセス
element = x[2]
print("Element at index 2:", element)

## インデックス 1 からインデックス 7 まで(ただし 7 は含まない)、ステップ 2 で配列をスライス
a_slice = x[1:7:2]
print("Slice from 1 to 7 with step 2:", a_slice)

indexing_practice.py にコードを追加したら、ファイルを保存します。次に、ターミナルから次のコマンドを実行してスクリプトを実行します。

python indexing_practice.py

元の配列、指定されたインデックスの要素、および結果のスライスを示す次の出力が表示されるはずです。

Original array: [0 1 2 3 4 5 6 7 8 9]
Element at index 2: 2
Slice from 1 to 7 with step 2: [1 3 5]

多次元配列のインデックス

次に、複数の次元を持つ配列を扱ってみましょう。ここで NumPy は Python のリストと比較して真価を発揮します!

多次元の考え方

2 次元配列をテーブルとして捉える

2 次元配列は、スプレッドシートやテーブルのようなものです。

  • は最初の次元(水平方向)です。
  • は 2 番目の次元(垂直方向)です。
  • 行と列のインデックスを指定します:array[row, column]

2 次元インデックス参照の視覚化

2 次元配列の場合:

array = [[10, 20, 30],
         [40, 50, 60],
         [70, 80, 90]]

Indices:     0,0  0,1  0,2
             1,0  1,1  1,2
             2,0  2,1  2,2
  • array[0, 0] → 10(最初の行、最初の列)
  • array[1, 2] → 60(2 番目の行、3 番目の列)
  • array[2, 1] → 80(3 番目の行、2 番目の列)

行全体または列全体の選択

  • array[0] または array[0, :] → 最初の行全体 [10, 20, 30]
  • array[:, 1] → 2 番目の列全体 [20, 50, 80]
  • これはネストされた Python リストよりもはるかに便利です!

これを 2 次元(2D)配列で練習してみましょう。indexing_practice.py ファイルを以下のコードで更新してください。このスクリプトは 3x4 の配列を作成し、単一の要素と行全体にアクセスする方法を示しています。

import numpy as np

## 2 次元配列(3 行 4 列)を作成
x = np.arange(12).reshape(3, 4)
print("Original 2D array:\n", x)

## 1 行目の 2 列目の要素にアクセス
element = x[1, 2]
print("\nElement at (1, 2):", element)

## 最初の行(行インデックス 0)全体にアクセス
first_row = x[0]
print("\nFirst row:", first_row)

ファイルを保存し、ターミナルから再度実行します。

python indexing_practice.py

出力には、2 次元配列と選択した特定の箇所が表示されます。

Original 2D array:
 [[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

Element at (1, 2): 6

First row: [0 1 2 3]

高度なインデックス

基本的なスライシングは連続した領域には適していますが、より複雑な選択が必要な場合もあります。NumPy は、2 つの強力な高度なインデックス参照テクニックを提供します。

整数配列インデックス参照

インデックスの配列を指定して、任意の要素を選択します。これは、リストから特定のアイテムをその位置を使って拾い出すようなものです。

実例: テストの点数があり、3 番目、7 番目、12 番目の位置の生徒の点数を確認したい場合:

scores = np.array([85, 92, 78, 95, 88, 76, 91, 89, 84, 93, 87, 90, 82])
student_positions = [3, 7, 12]  ## 関心のある生徒
selected_scores = scores[student_positions]  ## [95, 89, 82]

ブール配列インデックス参照(マスキング)

条件に基づいて要素を選択します。「True/False」値の「マスク」を作成し、それを使用して配列をフィルタリングします。

実例: クラスから合格点(80 点以上)をフィルタリングする場合:

scores = np.array([85, 92, 78, 95, 88, 76, 91, 89, 84, 93])
passing_mask = scores >= 80  ## [True, True, False, True, True, False, True, True, True, True]
passing_scores = scores[passing_mask]  ## [85, 92, 95, 88, 91, 89, 84, 93]

なぜこれが重要なのか

  • 整数インデックス参照: 特定のデータポイントのサンプリングに最適です。
  • ブールインデックス参照: データフィルタリングや条件付き選択に理想的です。
  • どちらもコピーを作成します(ビューではありません)。そのため、変更は元の配列に影響しません。

両方を試してみましょう。indexing_practice.py の内容を以下のコードに置き換えてください。

import numpy as np

## --- 整数配列インデックス参照 ---
x = np.arange(10, 0, -1)
print("Array for integer indexing:", x)

## インデックス 3、3、1、8 の要素を選択
selected_elements = x[np.array([3, 3, 1, 8])]
print("Selected elements with integer array:", selected_elements)


## --- ブール配列インデックス参照 ---
y = np.array([1., -1., -2., 3.])
print("\nArray for boolean indexing:", y)

## 負の要素に対するブールマスクを作成
mask = y < 0
print("Boolean mask (y < 0):", mask)

## 条件が True の要素を選択
negative_elements = y[mask]
print("Elements where y < 0:", negative_elements)

ファイルを保存してスクリプトを実行します。

python indexing_practice.py

出力は、整数インデックス参照とブールインデックス参照の両方が、配列から特定のデータを選択するためにどのように機能するかを示しています。

Array for integer indexing: [10  9  8  7  6  5  4  3  2  1]
Selected elements with integer array: [7 7 9 2]

Array for boolean indexing: [ 1. -1. -2.  3.]
Boolean mask (y < 0): [False  True  True False]
Elements where y < 0: [-1. -2.]

インデックス付き配列への値の代入

インデックス参照はデータの読み取りだけでなく、データの変更にも強力です。代入演算子 (=) の左辺に任意のインデックス参照メソッドを使用して、特定の要素を変更できます。

ブロードキャスティング:形状の互換性を持たせる

インデックス参照された配列に値を代入する際、NumPy は形状を互換性のあるものにするためにブロードキャスティングを使用します。これは NumPy の最も強力な機能の 1 つです!

ブロードキャスティングのルール

NumPy は、代入中に小さい配列を自動的に拡張して大きい配列に合わせることができます。これは以下のルールに従います。

  1. 単一の値を複数の要素に: 1 つの値を多くの位置に代入できます。
  2. 小さい配列を大きい選択範囲に: 次元が互換性がある限り。

代入におけるブロードキャスティングの例

## スライスへの単一値の代入
arr = np.array([1, 2, 3, 4, 5])
arr[1:4] = 99  ## [1, 99, 99, 99, 5]

## 配列を対応するスライスに代入
arr = np.array([1, 2, 3, 4, 5])
arr[1:4] = [10, 20, 30]  ## [1, 10, 20, 30, 5]

## ブロードキャスティングによるブールインデックス参照
arr = np.array([1, 2, 3, 4, 5])
arr[arr % 2 == 0] = -1  ## すべての偶数を -1 に置き換える

重要な注意点

  • ブロードキャスティングは、形状が互換性のある場合にのみ機能します。
  • 代入される値の形状は、インデックス参照された選択範囲に「収まる」必要があります。
  • これは、要素を個別にループ処理するよりもはるかに効率的です。

この機能を実際に確認するために、indexing_practice.py ファイルを以下のコードに更新してください。

import numpy as np

## --- スライスへの単一値の代入 ---
x = np.arange(10)
print("Original array:", x)

## インデックス 2 から 4 までの要素に値 99 を代入
x[2:5] = 99
print("After assigning 99 to slice [2:5]:", x)


## --- ブール条件に基づく値の代入 ---
y = np.arange(10)
print("\nOriginal array:", y)

## すべての偶数に値 -1 を代入
y[y % 2 == 0] = -1
print("After assigning -1 to even numbers:", y)

ファイルを保存し、ターミナルから実行します。

python indexing_practice.py

出力には、変更前後の配列が表示され、この機能がデータ操作にどれほど強力であるかが示されます。

Original array: [0 1 2 3 4 5 6 7 8 9]
After assigning 99 to slice [2:5]: [ 0  1 99 99 99  5  6  7  8  9]

Original array: [0 1 2 3 4 5 6 7 8 9]
After assigning -1 to even numbers: [-1  1 -1  3 -1  5 -1  7 -1  9]

まとめ

この実験では、NumPy 配列のインデックス参照に関する基本的なテクニックを学びました。Python のリストと同様に、1 次元配列での基本的な単一要素アクセスとスライシングから始めました。次に、多次元配列のインデックス参照に進み、特定の要素やサブ配列を選択しました。最後に、整数配列とブール配列を使用した高度なインデックス参照を探求し、これらの強力な選択方法を使用して配列内のデータを変更する方法を学びました。これらのスキルは、NumPy を使用した Python での効果的なデータ操作と分析の基礎となります。