NumPyでのコピーあれこれ


2018年 08月 07日

コピーの基礎的なことから書くので、話が無駄に長くなるのを最初に断っておく。 最後に、a[…] のような記述について書くので、そこだけ読みたい場合は最後から読もう。
まず、Pythonでの浅いコピーを示す。
>>> a = [[1,2,3],[4,5,6]]
>>> b = a
>>> a[0][0],a[1][1] = 100,200
>>> a
[[100, 2, 3], [4, 200, 6]]
>>> b
[[100, 2, 3], [4, 200, 6]]
aの要素だけを変更しようとしても、 b = a の代入により浅いコピーが行われているので、bの対応する要素も変わってしまう。

これを避けるためには、b=aの代わりに、copyのdeepcopy()を使う。
>>> import copy
>>> a = [[1,2,3],[4,5,6]]
>>> b = copy.deepcopy(a)
>>> a[0][0],a[1][1] = 100,200
>>> a
[[100, 2, 3], [4, 200, 6]]
>>> b
[[1, 2, 3], [4, 5, 6]]
さて、Pythonで色々計算しようとすると、Pythonではやってられなくて、NumPyを使うのが普通だ。 そもそも、Pythonには配列がなく、Deep Learningで必須の行列計算に困る。
では、numpyの配列で同じことをやってみよう。
>>> a = np.array([[1,2,3],[4,5,6]])
>>> b = np.array([[2,4,6],[3,6,9]])
>>> b = a
>>> a[0][0],a[1][1] = 100,200
>>> a
array([[100,   2,   3],
[  4, 200,   6]])
>>> b
array([[100,   2,   3],
[  4, 200,   6]])
単に代入ではコピーにならないようだ。
次に、numpyで用意されているcopy()を使ってみよう。


深いコピーができたようだが、念の為、サイズが歪なarrayでやってみよう。
>>> a = np.array([[1,2,3],[4,5]])
>>> b = np.array([[2,4,6],[3,6]])
>>> b = np.copy(a)
>>> a[0][0],a[1][1] = 100,200
>>> a
array([list([100, 2, 3]), list([4, 200])], dtype=object)
>>> b
array([list([100, 2, 3]), list([4, 200])], dtype=object)
歪んだarrayでやったら、元のaがリストの配列になってしまい、失敗したようだ。
numpyには、copy()はあるようだが、deepcopy()が存在しない。 それで、ここはやむなく、copy.deepcopy()でやるとどうなるか試してみよう。
>>> a = np.array([[1,2,3],[4,5,6]])
>>> b = np.array([[2,4,6],[3,6,9]])
>>> b = copy.deepcopy(a)
>>> a[0][0],a[1][1] = 100,200
>>> a
array([[100,   2,   3],
[  4, 200,   6]])
>>> b
array([[1, 2, 3],
[4, 5, 6]])
>>> a = np.array([[1,2,3],[4,5]])
>>> b = np.array([[2,4,6],[3,6]])
>>> b = copy.deepcopy(a)
>>> a[0][0],a[1][1] = 100,200
>>> a
array([list([100, 2, 3]), list([4, 200])], dtype=object)
>>> b
array([list([1, 2, 3]), list([4, 5])], dtype=object)
これで深いコピーができたわけだが、元の配列aは2次元配列ではなくて、リストの配列になっているので深いコピーができなかったようだ。
さて、配列の深いコピーをするのに、copy.deepcopy()を使えば問題ない。 しかし、普通のnumpyの配列なら、numpyのcopy()で深いコピーができる。


ごちゃごちゃ書いてきたが、最後に …(3点)の使い方について書いておく。
>>> a = np.array([[1,2,3],[4,5,6]])
>>> a
array([[1, 2, 3],
[4, 5, 6]])
>>> a[...]
array([[1, 2, 3],
[4, 5, 6]])
配列aを表示するのに、単にaと書けば済むのだが、a[…]と書くこともできる。
ならば、a[…]に代入するとどうなるだろうか?
>>> a = np.array([[1,2,3],[4,5,6]])
>>> b = np.array([[2,4,6],[3,6,9]])
>>> b[...]=a
>>> a[0][0],a[1][1] = 100,200
>>> a
array([[100,   2,   3],
[  4, 200,   6]])
>>> b
array([[1, 2, 3],
[4, 5, 6]])
同じサイズの配列の場合、これで配列の深いコピーができている。 copy.deepcopy()やnumpy.copy()を使わなくてもできるのだが、事前に同じサイズの配列が存在していないといけない。

最後に、…が何者か示しておく。
>>> ...
Ellipsis
>>> type(...)

…はオブジェクトなので、引数として渡すなどもできる。