タイトル: 要素の削除
SEOタイトル: NumPy 要素削除完全ガイド(np.delete / Boolean Mask / axis 指定)
| この記事の要点 |
|
np.delete の基本
NumPy 配列は「サイズ可変なリスト」ではないため、Python の list.pop() 相当の操作は新しい配列を生成する形で行います。中心 API は np.delete():
import numpy as np
a = np.array([10, 20, 30, 40, 50])
# インデックス 2 を削除 (30 が消える)
b = np.delete(a, 2)
print(b) # [10 20 40 50]
# 元配列 a は変わらない
print(a) # [10 20 30 40 50]
# 複数インデックス
c = np.delete(a, [0, 4])
print(c) # [20 30 40]
# スライス指定(後ろ 2 つを削除)
d = np.delete(a, np.s_[-2:])
print(d) # [10 20 30]
2 次元配列での行・列削除
m = np.arange(12).reshape(3, 4)
# array([[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11]])
# 行削除 (axis=0)
np.delete(m, 1, axis=0)
# array([[ 0, 1, 2, 3],
# [ 8, 9, 10, 11]])
# 列削除 (axis=1)
np.delete(m, 1, axis=1)
# array([[ 0, 2, 3],
# [ 4, 6, 7],
# [ 8, 10, 11]])
# 複数列
np.delete(m, [0, 2], axis=1)
# array([[ 1, 3],
# [ 5, 7],
# [ 9, 11]])
# axis 省略 → flatten 後に削除
np.delete(m, [0, 5])
# array([ 1, 2, 3, 4, 6, 7, 8, 9, 10, 11])
条件削除: Boolean マスク
np.delete は「インデックス指定」ベース。「条件に合うものを除外」したいときはBoolean マスクを使う方が高速で読みやすいです。
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# 5 より大きい要素のみ残す(= 5 以下を削除)
result = a[a > 5]
print(result) # [ 6 7 8 9 10]
# 偶数だけ残す
print(a[a % 2 == 0]) # [ 2 4 6 8 10]
# 0 を削除
b = np.array([1, 0, 2, 0, 3, 0])
print(b[b != 0]) # [1 2 3]
# 複数条件 (&, | を使う、and/or は使えない)
print(a[(a > 2) & (a < 8)]) # [3 4 5 6 7]
print(a[(a < 3) | (a > 7)]) # [ 1 2 8 9 10]
# 反転 ~
mask = a > 5
print(a[~mask]) # [1 2 3 4 5]
np.where と組み合わせる
a = np.array([10, 20, 30, 40, 50])
# 条件にマッチするインデックスを取得
idx = np.where(a > 25)[0]
print(idx) # [2 3 4]
# そのインデックスを削除(つまり 25 以下のみ残す)
result = np.delete(a, idx)
print(result) # [10 20]
# == マスク方式と同じ結果
print(a[a <= 25]) # [10 20] ← より直接的
NaN や特定値の除去
a = np.array([1.0, np.nan, 2.0, np.inf, 3.0, -np.inf])
# NaN 除去
a[~np.isnan(a)] # [ 1. 2. inf 3. -inf]
# Inf / NaN 全て除去
a[np.isfinite(a)] # [1. 2. 3.]
# 重複除去 (順序は維持されない)
b = np.array([3, 1, 4, 1, 5, 9, 2, 6, 5, 3])
np.unique(b) # [1 2 3 4 5 6 9]
# 順序を保ったまま重複除去 (pandas 経由が簡単)
import pandas as pd
pd.Series(b).drop_duplicates().to_numpy() # [3 1 4 5 9 2 6]
パフォーマンス比較
大きな配列では、Boolean マスクと np.delete の速度差は無視できません:
import numpy as np
import time
a = np.arange(10_000_000)
# 方法 A: np.delete
start = time.time()
b = np.delete(a, np.where(a % 2 == 0)[0])
print(f"np.delete: {time.time() - start:.3f}s")
# 方法 B: Boolean マスク
start = time.time()
c = a[a % 2 != 0]
print(f"mask : {time.time() - start:.3f}s")
# ★ Boolean マスクの方が 5-10 倍速い
| 用途 | 推奨 API | 備考 |
|---|---|---|
| 位置指定で 1〜数件削除 | np.delete(a, idx) | 読みやすい |
| 大量削除・条件削除 | Boolean マスク a[cond] | ★ 高速 |
| NaN / inf 除外 | a[np.isfinite(a)] | 専用関数 |
| 重複除去 | np.unique(a) | ソートされる |
| 行・列単位 | np.delete(m, i, axis=0/1) | axis 必須 |
メモリ効率の注意(コピー vs ビュー)
import numpy as np
a = np.arange(10_000_000)
# np.delete は新規配列を返す(コピー) → 元と同サイズのメモリが必要
b = np.delete(a, 0)
print(b.base is None) # True (独立した配列)
# スライス(ビュー)は元のメモリを共有
v = a[1:]
print(v.base is a) # True (ビュー)
# ★ 大きな配列の頭・末尾削除はスライス推奨
head_drop = a[1:] # 0 番目を「論理的に」削除
tail_drop = a[:-1] # 末尾を「論理的に」削除
多次元での Boolean マスク
m = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
])
# 全要素対象(1 次元化される)
m[m > 5] # array([6, 7, 8, 9])
# 行単位の条件(合計が 10 以下の行のみ残す)
m[m.sum(axis=1) <= 10]
# array([[1, 2, 3]])
# 列単位の条件
m[:, m.sum(axis=0) > 12]
# 列 2 (sum=18) のみ残す
# array([[3], [6], [9]])
Python list との違い
| 操作 | Python list | NumPy ndarray |
|---|---|---|
| 末尾削除 | lst.pop() → in-place | arr = arr[:-1] → 新規(ビュー) |
| indexed 削除 | del lst[i] → in-place | np.delete(arr, i) → 新規 |
| 条件削除 | [x for x in lst if cond] | arr[cond] → 高速 |
| サイズ可変 | ○ | ×(固定。np.append も新規) |
FAQ
Q: in-place で削除したい
A: NumPy は基本不可。配列のサイズを変えるには新規確保が必要です。頻繁に削除するなら list や deque を使い、最後に np.array() 化する設計を。
Q: 削除後に shape を保ちたい
A: 「削除」ではなく「マスクや NaN で無効化」を検討。np.where(cond, arr, np.nan) や np.ma.masked_where が有用。
Q: np.delete で負のインデックスは使える?
A: 使えます。np.delete(a, -1) で末尾削除。スライスも np.s_[-3:] で OK。