Python環境を起動中... 初回のみ数秒かかります
Lesson 4 / 12

インデックスとスライス

このレッスンで学ぶこと

  • 1次元・多次元配列の要素にアクセスできる
  • スライス [start:stop:step] で部分配列を取り出せる
  • 2次元配列を arr[行, 列] 形式で操作できる
  • ビュー(参照)とコピーの違いを理解し、落とし穴を回避できる

1. 1次元配列のインデックス

NumPy配列の要素アクセスは、基本的にPythonリストと同じ感覚で書けます。 arr[i] で「i番目の要素」を取り出せます。インデックスは 0始まり です。

また、Pythonリスト同様 負のインデックス を使うと末尾から数えられます(-1 が最後の要素)。

sample_1.py
Ctrl+Enter
出力

💡 Pythonリストとの違い: ここまでは Python リストとほぼ同じです。NumPy配列の真価は 多次元アクセススライス時の動作(後述)で発揮されます。

2. 1次元配列のスライス

arr[start:stop:step] で部分配列を取り出せます。これも Python リストと同じ書き方です。 stop は含まれない こと、step を負にすると逆順 になることを覚えておきましょう。

sample_2.py
Ctrl+Enter
出力

💡 スライスの覚え方: [start:stop] は「start 番目から始まり stop 番目の手前まで」。 よく使う略記:[:n] = 先頭n件・[-n:] = 末尾n件・[::-1] = 逆順。 スライスに値を代入すると その範囲を一括更新 できるのもNumPyの便利な点です。

3. 多次元配列のインデックス・スライス

ここからがNumPyの本領発揮です。多次元配列では arr[行, 列] のように カンマ区切りで複数の軸を同時に指定できます。 この書き方はPythonリストにはない、NumPy独自の便利機能です。

sample_3.py
Ctrl+Enter
出力

💡 arr[行, 列] の読み方:

  • labs[2, 0] → 「2行目・0列目の1要素」
  • labs[3] または labs[3, :] → 「3行目の全列」(1行まるごと)
  • labs[:, 0] → 「全行の0列目」(1列まるごと)
  • labs[:3, :2] → 「先頭3行・先頭2列」の部分行列

なお Python リスト風に labs[2][0] も書けますが、labs[2, 0] の方が速くて読みやすいので NumPy ではこちらを使います。

🩺 医療AI文脈: 医療画像(2D)のスライスは「関心領域(ROI)の切り出し」に直結します。 例:胸部X線画像(shape=(1024, 1024))から肺野中央あたりを切り出すなら img[300:700, 200:800] のように書けます。 CT(3D)なら ct[20:30, 100:400, 100:400] のように3軸を同時に指定して直方体領域を取り出します。

4. ビュー vs コピー — 重要な落とし穴

ここはNumPy初学者が最も混乱しやすいポイントです。 スライスで取り出した結果は、元の配列を共有する「ビュー(参照)」 です。 つまり、スライス先を書き換えると 元の配列も変わります

sample_4.py
Ctrl+Enter
出力

⚠ なぜビューなのか: NumPyがビューを返すのは 性能のため です。スライスのたびに巨大な画像をコピーしていたらメモリも時間も無駄になります。 ただし、これが「思わぬ書き換え」の温床になるため、「書き換える可能性があるなら .copy() を付ける」 習慣をつけましょう。

💡 ビュー vs コピーの見分け方:

  • スライス([start:stop] → ビュー(参照)
  • 明示的な .copy() → 独立コピー
  • ブール選択や fancy indexing(次のレッスンで学習) → コピーが返る

迷ったら arr.base is None でビューかどうかを確認できます(None ならコピー)。

🩺 医療AI文脈: ROI(関心領域)に対してフィルタやノイズ除去を適用するとき、roi = image[100:300, 200:400] のスライスを直接書き換えると元画像も変わります。 「元画像を残しつつROIだけを加工したい」場合は必ず roi = image[100:300, 200:400].copy() としましょう。

5. 練習問題

問題 1

2次元配列から特定の行・列を抽出しよう

以下の2次元配列 data(患者4人 × 検査3項目)から、 「全患者の2列目(血糖値)」「患者2の全項目」 を取り出してください。

exercise_1.py
出力
ヒントを見る(答え+解説)
import numpy as np

data = np.array([
    [6200,  95, 36.5],
    [7100, 110, 37.2],
    [5800,  88, 36.8],
    [8400, 145, 37.5]
])

# 全患者の血糖値(2列目)を取り出してください
glucose = data[:, 1]

# 患者2の全項目を取り出してください
patient2 = data[2]   # data[2, :] でも同じ

print("全患者の血糖:", glucose)       # [ 95. 110.  88. 145.]
print("患者2の検査値:", patient2)     # [5800.   88.   36.8]

2D配列では arr[行, 列] 形式が便利。: は「その軸の全要素」を意味するので、data[:, 1] は「全行の1列目」となります。1行をまるごと取るには data[2] でOK(暗黙的に data[2, :] と同じ)。

問題 2

関心領域(ROI)を切り出そう

10×10 のダミー画像 image から、中央の 4×4 領域(行 3〜6、列 3〜6)を切り出してください。

exercise_2.py
出力
ヒントを見る(答え+解説
import numpy as np

image = np.arange(100).reshape(10, 10)
print("元の画像 shape:", image.shape)

# 中央の 4×4 領域(行 3〜6、列 3〜6)を取り出してください
roi = image[3:7, 3:7]

print("ROI shape:", roi.shape)   # (4, 4)
print("ROI:\n", roi)
# [[33 34 35 36]
#  [43 44 45 46]
#  [53 54 55 56]
#  [63 64 65 66]]

「行 3〜6 を含む」 = 3:7(7は含まない)。「列 3〜6」も同じく 3:7。 つまり image[3:7, 3:7] で 4×4 の領域が取れます。医療画像で関心領域を切り出すときに毎日のように使う書き方です。

問題 3

ビューとコピーの違いを確認しよう

以下の配列 original から先頭3要素をスライスして取り出し、 そのスライスを書き換えても元の配列が変わらないようにしてください。 .copy() を使うのがポイントです。

exercise_3.py
出力
ヒントを見る(答え+解説)
import numpy as np

original = np.array([10, 20, 30, 40, 50])

# 先頭3要素をスライス。ただし元を変えたくないので .copy() してください
part = original[:3].copy()

# 取り出した part の全要素を 0 にする
part[:] = 0

print("part:    ", part)            # [0 0 0]
print("original:", original)        # [10 20 30 40 50]  ← 変わらない!

.copy() を付けないと part はビューになり、part[:] = 0original の先頭3要素も0にしてしまいます。「スライスから書き換える可能性があるときは .copy()」が鉄則です。

6. まとめ

このレッスンのポイント

  • 1次元アクセスは Python リスト同様 arr[i](負のインデックスもOK)
  • スライス arr[start:stop:step] で部分配列を取り出せる(stopは含まない)
  • 多次元では arr[行, 列] 形式が便利。: は「その軸の全要素」
  • スライスで取り出した結果は ビュー(参照)。書き換えると元も変わる
  • 独立コピーが欲しいときは .copy() を付ける

自由に試してみましょう:

free_practice.py
出力

完了するとコース一覧に進捗が記録されます