NumPy

NumPy Quickstart チュートリアルに取り組む (3-1) コピーとビュー

配列を操作する際、新しい配列にデータがコピーされたりされないことがある。3つのケースがある。

全くコピーされない場合

単純な代入はオブジェクトやそのデータをコピーしない。

import numpy as np
a = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])
b = a
print(b is a)  # True

# Python は mutable オブジェクトを参照として渡す。関数の呼び出しではコピーが行われない
def f(x):
    print(id(x))

print(id(a))  # 2535031005632

f(a)  # 2535031005632

シャローコピー

異なる配列オブジェクトは同じデータを共有することができる。viewメソッドは、同じデータを見る新しい配列オブジェクトを作成する。

import numpy as np
a = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])

c = a.view()
print(c is a)  # False

print(c.base is a)  # True

print(c.flags.owndata)  # False


c = c.reshape((2, 6))
print(a.shape)  # (3, 4)

c[0, 4] = 1234
print(a)
'''
[[   0    1    2    3]
 [1234    5    6    7]
 [   8    9   10   11]]
'''

# 配列をスライスすると、その配列のビューが返される
s = a[ : , 1:3]
s[:] = 10
print(a)
'''
[[   0   10   10    3]
 [1234   10   10    7]
 [   8   10   10   11]]
'''

Deep Copy

copyメソッドは、配列とそのデータの完全なコピーを作成する。

import numpy as np
a = np.array([[ 0,  1,  2,  3],
              [ 4,  5,  6,  7],
              [ 8,  9, 10, 11]])

d = a.copy()
print(d is a)  # False

print(d.base is a)  # False

d[0,0] = 9999
print(a)
'''
[[   0   10   10    3]
 [1234   10   10    7]
 [   8   10   10   11]]
'''

元の配列が不要になった場合には、スライスの後にコピーが呼ばれることがある。(例:aが巨大な中間結果で、最終的な結果bがaのごく一部しか含まれていない場合、スライスを用いてbを構築する際には、Deep Copyを行う必要がある。)

a = np.arange(int(1e8))
b = a[:100].copy()
del a  # the memory of ``a`` can be released.

代わりに b = a[:100] を使用した場合、a は b によって参照され、del a が実行されてもメモリ内に持続される。