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[1:4])
reshape(a.reshape(2, 3))
転置(a.T)
ravel(a.ravel())
a = np.arange(6).reshape(2, 3) b = a.T # 転置もビュー b[0, 0] = 99 print(a[0, 0]) # 99(元も変わる)
コピーを返す操作
以下の操作はコピーを返します。元のデータとは独立しているため、安全に変更できます。
copy メソッド(a.copy())
flatten(a.flatten())
ファンシーインデックス(a[[0, 2]])
ブールインデックスの結果
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() を使っておけば安全です。パフォーマンスが重要な場面では、ビューを意識的に活用しましょう。