NumPy:配列のコピーとビューの違い(copy vs view)
NumPy では、配列を操作したときにコピーが作られる場合とビュー(元データへの参照)が作られる場合があります。この違いを理解しないと、意図せずデータを壊してしまうことがあります。
ビューとは
ビューは、元の配列と同じメモリを参照する配列です。ビューを変更すると、元の配列も変わります。
import numpy as np
a = np.array([1, 2, 3, 4, 5])
b = a[1:4] # スライスはビューを返す
print(b) # [2 3 4]
b[0] = 99
print(a) # [ 1 99 3 4 5](元も変わる)
コピーとは
コピーは、元の配列とは別のメモリに複製された配列です。コピーを変更しても、元の配列は変わりません。
a = np.array([1, 2, 3, 4, 5])
b = a.copy() # 明示的にコピーを作成
b[0] = 99
print(a) # [1 2 3 4 5](元は変わらない)
ビューを返す操作
以下の操作はビューを返します。元のデータを共有するため、高速でメモリ効率が良い反面、変更に注意が必要です。
a = np.arange(6).reshape(2, 3)
b = a.T # 転置もビュー
b[0, 0] = 99
print(a[0, 0]) # 99(元も変わる)
コピーを返す操作
以下の操作はコピーを返します。元のデータとは独立しているため、安全に変更できます。
a = np.array([1, 2, 3, 4, 5])
b = a[[0, 2, 4]] # ファンシーインデックス
b[0] = 99
print(a) # [1 2 3 4 5](元は変わらない)
ビューかコピーか確認する方法
np.shares_memory() を使うと、2つの配列がメモリを共有しているか確認できます。
a = np.array([1, 2, 3, 4, 5])
b = a[1:4]
print(np.shares_memory(a, b)) # True(ビュー)
c = a.copy()
print(np.shares_memory(a, c)) # False(コピー)
また、配列の base 属性を見ると、元になった配列がわかります。ビューの場合は元の配列が、コピーの場合は None が入っています。
a = np.array([1, 2, 3, 4, 5])
b = a[1:4]
c = a.copy()
print(b.base is a) # True(bはaのビュー)
print(c.base) # None(cはコピー)
使い分けのポイント
ビューを使う場面
大きな配列を扱うとき。読み取り専用で使うとき。メモリと速度を重視するとき
コピーを使う場面
元のデータを保護したいとき。配列を独立して変更したいとき
迷ったら .copy() を使っておけば安全です。パフォーマンスが重要な場面では、ビューを意識的に活用しましょう。