【numpy】viewとcopy
配列のview
とは、もとの配列と同じデータを指している。
一方、copy
は、もとの配列と同じデータのコピーを指している。
つまり、配列a
のview
であるv
が存在するとして、v
の要素を書き換えるとa
の値も同時に書き換わる。
しかし、a
のcopy
であるc
の要素を書き換えてもa
の値は影響を受けない。
例えば、
import numpy as np a = np.arange(5) # もとの配列 v = a[:] # aへのview c = a.copy() # aのcopy # 最初の値の確認 print(a) # array([0, 1, 2, 3, 4]) print(v) # array([0, 1, 2, 3, 4]) print(c) # array([0, 1, 2, 3, 4]) # 元の配列aに対して変更を加えると? a[0] = 100 print(a) # array([100, 1, 2, 3, 4]) print(v) # array([100, 1, 2, 3, 4]) print(c) # array([0, 1, 2, 3, 4]) # viewが影響を受ける。copyは無事 # viewに対して変更を加えると? v[2] = 200 print(a) # array([100, 1, 200, 3, 4]) print(v) # array([100, 1, 200, 3, 4]) print(c) # array([0, 1, 2, 3, 4]) # 元の配列aが影響を受ける。copyは無事 # copyに対して変更を加えると? c[3] = 300 print(a) # array([100, 1, 200, 3, 4]) print(v) # array([100, 1, 200, 3, 4]) print(c) # array([0, 1, 2, 300, 4]) # cだけが書き換わる
view
はコピーをつくらないので基本的に速いしメモリ効率も良いが、もとの配列を壊したくないときcopy
が必要となる。
どんな操作がview
を返して、どんな操作がcopy
を返すのかを整理してみた。ちなみにこれは自分向けの整理なので、できるだけ誤りのないようにするが必ず正しいとは言えないので必ずドキュメントを参照してください。
また、カバーしきれていない部分がかなりあると思うので、教えてくれたら喜んで加筆します。
view を返す操作 |
copy を返す操作 |
何してるの | 備考 |
---|---|---|---|
v = a または v = a[:] |
c = a.copy() |
配列全ての値の取得 | Pythonのlistだとa[:] はcopyを返すことに注意 |
v = a.ravel() *1 |
c = a.flatten() |
a を1次元ベクトルに変換 |
v = np.ravel(a) も可。 |
v = a[something] |
c = a[something].copy() |
a のslicing |
例えばa[start:stop:step] *2, a[a>0] *3, a[ndarray([3,2,1])] *4など |
さらに、fancy indexingは大変便利なのですが、内部の処理が複雑で多少計算時間がかかります。 そこで、fancy indexingのそれぞれの機能を分割したような高速な専用の関数が用意されています *5 :
共通の処理 | 専用の関数(速い) | fancy indexing (遅い) | 何してるの | 備考 |
---|---|---|---|---|
i = np.random.rand(10) > .5 |
c = a.compress[i] |
c = a[i] |
i の要素がTrue の場所だけ取得 |
c = np.compress(a, i) も可 |
i = np.array([3,2,1]) |
c = a.take(i) |
c = a[i] |
3,2,1番目の要素を取得 | c = np.take(a,i) も可 |
他にも、入力を破壊するメソッドとそうでないメソッドもある。
例えば、np.random.shuffle(a)
はa
の値を書き換えるので、もとのa
は上書きされてしまうが、同様の処理をするb = np.random.permutation(a)
はa
を保存したまま、b
にその結果を格納する、よってa
の値は上書きされない:
上書きする | 上書きしない | 操作 |
---|---|---|
np.random.shuffle(a) # 返り値なし |
b = np.random.permutation(a) |
要素をランダムにシャッフル |
a.resize((n,m)) # 返り値なし |
v = a.reshape((n,m)) # viewを返す *6 |
n行m列に変換 |