5.

NumPy 配列要素の更新完全ガイド

編集
この記事の要点
  • 単要素更新: arr[0] = 100 / 多次元は arr[1, 2] = 9
  • スライス代入: arr[1:3] = 0 でブロードキャスト
  • Boolean インデックス: arr[arr > 5] = 0 で条件一括更新
  • np.where(cond, x, y) で条件分岐 (新配列を返す、in-place ではない)
  • np.put / np.copyto / np.fill_diagonal / np.clip など便利関数
  • View と Copy の違い: スライスは View → 元配列も変わる、arr.copy() で独立
  • in-place vs new array: arr += 1 は in-place、arr = arr + 1 は新配列

単要素・スライスの更新

import numpy as np

arr = np.array([1, 2, 3, 4, 5])

# 1. 単要素更新
arr[0] = 100
# [100, 2, 3, 4, 5]

# 2. スライス代入 (右辺がスカラ → ブロードキャスト)
arr[1:3] = 0
# [100, 0, 0, 4, 5]

# 3. スライス代入 (右辺が配列)
arr[1:4] = [10, 20, 30]
# [100, 10, 20, 30, 5]

# 4. ステップ付きスライス
arr2 = np.arange(10)
arr2[::2] = -1   # 偶数 index を全部 -1
# [-1, 1, -1, 3, -1, 5, -1, 7, -1, 9]

# 5. 多次元配列
mat = np.zeros((3, 3))
mat[1, 2] = 5
mat[0, :] = [1, 2, 3]   # 1 行目を一括
mat[:, 1] = 9            # 2 列目を全部 9
# [[1, 9, 3],
#  [0, 9, 5],
#  [0, 9, 0]]

Boolean インデックス (条件一括更新)

NumPy 最大の武器。条件式から Boolean 配列を作って一括更新できます:

arr = np.array([1, 5, 3, 7, 2, 8, 4])

# 1. 条件で書き換え
arr[arr > 5] = 0
# [1, 5, 3, 0, 2, 0, 4]

# 2. 条件部分を計算
arr = np.array([1, 5, 3, 7, 2, 8, 4])
arr[arr > 5] = arr[arr > 5] * 10
# [1, 5, 3, 70, 2, 80, 4]

# 3. 複数条件 (& で and、| で or、~ で not。括弧必須)
arr = np.array([1, 5, 3, 7, 2, 8, 4])
arr[(arr > 3) & (arr < 8)] = 0
# [1, 0, 3, 0, 2, 8, 0]

# 4. 多次元
mat = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
mat[mat > 5] = -1
# [[1, 2, 3], [4, 5, -1], [-1, -1, -1]]

np.where: 条件分岐

arr = np.array([1, 5, 3, 7, 2, 8, 4])

# 1. 三項演算子的: cond なら x, でなければ y
result = np.where(arr > 5, 0, arr)
# arr 自体は変わらない (新配列を返す)
# [1, 5, 3, 0, 2, 0, 4]

# 2. 別配列との組合せ
a = np.array([1, 2, 3, 4, 5])
b = np.array([10, 20, 30, 40, 50])
result = np.where(a >= 3, b, a)
# [1, 2, 30, 40, 50]

# 3. np.where(cond) は条件を満たす index を返す
arr = np.array([1, 5, 3, 7, 2, 8, 4])
idx = np.where(arr > 5)
# (array([3, 5]),)
arr[idx] = -1

その他の更新系関数

関数用途
np.put1 次元インデックスで複数値代入np.put(arr, [0, 2], [99, 88])
np.copyto条件付き上書きnp.copyto(dst, src, where=mask)
np.fill_diagonal対角成分を一括設定np.fill_diagonal(mat, 0)
arr.fill(x)全要素を x にarr.fill(0)
np.clip範囲外を切り詰めnp.clip(arr, 0, 100)
arr.flat[i]1 次元インデックスで多次元アクセスmat.flat[5] = 99
# np.put
arr = np.arange(10)
np.put(arr, [0, 2, 4], [99, 88, 77])
# [99, 1, 88, 3, 77, 5, 6, 7, 8, 9]

# np.copyto + where (条件付き上書き)
dst = np.zeros(5)
src = np.array([1, 2, 3, 4, 5])
mask = np.array([True, False, True, False, True])
np.copyto(dst, src, where=mask)
# [1, 0, 3, 0, 5]

# np.fill_diagonal
mat = np.ones((3, 3))
np.fill_diagonal(mat, 0)
# [[0, 1, 1], [1, 0, 1], [1, 1, 0]]

# np.clip (min/max でクリップ)
arr = np.array([-3, -1, 5, 12, 100])
np.clip(arr, 0, 10)
# [0, 0, 5, 10, 10]

# flat: 多次元を 1 次元のように扱う
mat = np.arange(12).reshape(3, 4)
mat.flat[5] = 99
# [[0, 1, 2, 3], [4, 99, 6, 7], [8, 9, 10, 11]]

View と Copy の重要な違い

NumPy のスライスはView (参照) を返します。View を通じて更新すると元配列も変わる:

# View (元と連動)
arr = np.array([1, 2, 3, 4, 5])
sub = arr[1:4]      # View
sub[0] = 99
print(arr)          # [1, 99, 3, 4, 5]  ← 元も変わる!

# Copy (独立)
arr = np.array([1, 2, 3, 4, 5])
sub = arr[1:4].copy()
sub[0] = 99
print(arr)          # [1, 2, 3, 4, 5]  ← 元は変わらない

# Boolean インデックスは Copy
arr = np.array([1, 2, 3, 4, 5])
sub = arr[arr > 2]
sub[0] = 99
print(arr)          # [1, 2, 3, 4, 5]  ← 元は変わらない

# Fancy インデックスも Copy
arr = np.array([1, 2, 3, 4, 5])
sub = arr[[0, 2, 4]]
sub[0] = 99
print(arr)          # [1, 2, 3, 4, 5]

# View かどうか確認
print(sub.base is arr)    # True なら View

in-place vs 新配列 (メモリ効率)

# in-place (メモリ効率良)
arr = np.arange(1000000)
arr += 1                 # ← 既存メモリを書き換え
arr *= 2

# 新配列 (元残るがメモリ 2 倍消費)
arr = np.arange(1000000)
arr = arr + 1            # ← 新配列を作成して再代入
arr = arr * 2

# 巨大配列なら in-place が必須
# in-place 系の関数: np.add(a, b, out=a) など out= で指定可能
np.add(arr, 1, out=arr)
np.multiply(arr, 2, out=arr)

# 関数では out= 引数で結果書込先を指定
result = np.empty_like(arr)
np.sqrt(arr, out=result)

多次元での更新パターン

# 画像処理風: RGB のうち R チャンネルだけ更新
img = np.zeros((100, 100, 3), dtype=np.uint8)
img[:, :, 0] = 255       # R チャンネルを最大

# 矩形領域を一括塗りつぶし
img[20:50, 30:70, :] = [255, 0, 0]   # 赤い長方形

# 円形マスクで更新
y, x = np.ogrid[:100, :100]
mask = (x - 50)**2 + (y - 50)**2 <= 30**2
img[mask] = [0, 255, 0]   # 緑の円

# 行/列の挿入 (要 reshape)
mat = np.arange(9).reshape(3, 3)
# 1 行追加 → np.vstack
mat = np.vstack([mat, [10, 11, 12]])
# 1 列追加 → np.hstack
mat = np.hstack([mat, [[20], [21], [22], [23]]])

FAQ

Q: arr[arr > 5] = 0 はなぜ in-place?
A: Boolean インデックスでの代入 (left-hand side) は元配列を直接書き換えます。右辺 (取得) の Boolean インデックスは Copy を返します。

Q: スライスでの代入が元配列を変えないようにしたい
A: arr[1:4].copy() で明示的にコピー。または最初から copy で作る。

Q: np.where と Boolean インデックス、どちらを使う?
A: 「新配列が欲しい」なら np.where、「元配列を更新」なら Boolean インデックス代入。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 配列の作成
  2. 多次元配列の作成
  3. 要素の参照
  4. 要素の追加
  5. 要素の更新
  6. 要素の削除
  7. ブロードキャスト
  8. 多次元配列の構造の確認
  9. 多次元配列を1次元配列に変換
  10. 1次元配列を多次元配列に変換
  11. NumPy 配列の範囲指定(スライス・多次元・Boolean / Fancy Index)
  12. 平均値の算出
  13. 行列を結合する方法

最近更新/作成されたページ