先ずはいつものとおり,numpyをインポートして乱数のnumpy配列を作成します.
>>> x = np.random.randn(5)
>>> x
array([ 0.60128361, 0.24738796, -0.8625321 , -0.04678481, -1.67326829])
今回は5次元として,こんな値が入りました.
データサイエンスでよく使うものとして,ソート機能があります.
これは非常に単純で,sort()関数で出来るんですが,配列の値をソート後のものに書き換えちゃうので,一時的にソートしたいだけ(つまり,ソート前の配列の並びをそのまま残しておきたい)場合もあります.
そこで,下記のようにやってみました.
>>> y = x
>>> y.sort()
>>> y
array([-1.67326829, -0.8625321 , -0.04678481, 0.24738796, 0.60128361])
つまり,別の変数yに一旦コピーして,コピーした変数yをソートすることにしました.xはソート前のまま残しておきたかったからです.
その結果,yの値には小さい値から順に並び替わってます.
で,問題はこの後です.これが今回の本題ですが,元の変数xの値をみると
>>> x
array([-1.67326829, -0.8625321 , -0.04678481, 0.24738796, 0.60128361])
あれ?ソート後のyの値と同じになってます.つまり,yがソートされると,それに連動してxもソートされてしまったという現象が.
これ,参照渡しとコピーの違いらしいですが,なんとも紛らわしくてPythonの嫌なところです.C言語だとポインタを明示的に使うので一眼で分かるけど.
細かい理屈は他のサイトに譲るとして(自分もあまり詳しく理解してない💦),要は「別の変数としてyを定義し,そこへxの値をコピーする(退避させる)場合」は,y=xではダメで下記のように書くらしいです.
>>> y = x.copy()
単にy=xと書けば,xの値が保存されている場所をyにコピーする(参照渡し).
つまりyとxは同じ場所を示すようです.なので,xの値が変更されたら,yの値も変更になったように見える.
numpy配列をコピーする場合は参照渡しに気をつけろ!とピーンと気づかないとダメなようです.
それでは,最初からプログラムを示します.
>>> import numpy as np
>>> x = np.random.randn(5)
>>> y = x
>>> x
array([-2.01555966, -1.65176538, -1.3716272 , -1.05239708, -0.11177732])
>>> y
array([-2.01555966, -1.65176538, -1.3716272 , -1.05239708, -0.11177732])
>>> x.sort() #xだけソート
>>> x
array([-2.01555966, -1.65176538, -1.3716272 , -1.05239708, -0.11177732])
>>> y
array([-2.01555966, -1.65176538, -1.3716272 , -1.05239708, -0.11177732]) #yも連動してソートされている
>>>
>>> x = np.random.randn(5) #もう一度やり直し
>>> x
array([-1.99225332, 0.80321978, 0.27801504, 0.07454439, 1.21747589])
>>> y = x.copy() #今度はコピー
>>> y
array([-1.99225332, 0.80321978, 0.27801504, 0.07454439, 1.21747589]) #xと同じ値
>>> x.sort()
>>> x
array([-1.99225332, 0.07454439, 0.27801504, 0.80321978, 1.21747589]) #xはソートされる
>>> y
array([-1.99225332, 0.80321978, 0.27801504, 0.07454439, 1.21747589]) #yは最初の値のまま変更なし
実はnumpy配列は「参照渡し」と「(深い)コピー」だけ区別すればいいのですが,普通のリストだとこの2つ以外に「浅いコピー」というのもあるようです.なんだかややこしいなぁ.
普通のリストを使う場面が来たら,また勉強して記事にします.
まとめ
numpy配列xの値を別の変数yに退避させるときは,y=xでなく,y=x.copy()と記述すること.