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[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() を使っておけば安全です。パフォーマンスが重要な場面では、ビューを意識的に活用しましょう。