최초작성 2018.04.21


이전 포스팅에서 자주 업급되었던 뷰(View)와 ndarray.copy 메소드에 대해 다시 개념 정리를 해보았습니다.



다음 세 가지 경우가 있습니다.


  • 두 개 이상의 변수가 하나의 NumPy 배열 객체를 가리키는 경우

배열을 할당받은 변수를 다른 변수에 대입한 경우입니다.


  • 원본 배열의 데이터를 공유하는 새로운 NumPy 배열 객체를 생성하는 경우

배열의 shape를 변경하거나 배열의 부분을 슬라이싱하는 경우입니다.


  • 새로운 NumPy 배열 객체의 새로운 메모리 공간에 데이터를 복사하는 경우

ndarray.copy 메소드를 사용하는 경우입니다.




4.1. 두 개 이상의 변수가 하나의 NumPy 배열 객체를 가리키는 경우


배열을 생성하여 변수에 할당한 후, 이 변수를 다시 다른 변수에 대입하게 되면 하나의 NumPy 배열을 가리키는 두 개의 이름이 생성됩니다.


import numpy as np


a = np.arange(12)
b = a

# a와 b를 출력해보면 똑같은 데이터를 출력합니다.
print("a : ", a)
print("b : ", b)
print("\n")
# a :  [ 0 1 2  3 4 5 6 7  8 9 10 11]
# b :  [ 0 1 2  3 4 5 6 7  8 9 10 11]


# id 함수를 사용해보면 변수 a와 b가 같은 ndarray 객체를 가리키고 있음을 확인할 수 있습니다.
print("id(a) = ", id(a))
print("id(b) = ", id(b))
print("\n")
# id가 동일합니다.
# id(a) =  1696294860160
# id(b) =  1696294860160


# 슬라이스의 경우에는 다릅니다.
# 대입시켜도 다른 개체가 됩니다.
c = a[0:3]
print("id(a[0:3])=", id(a[0:3]))
print("id(c)=", id(c))
print("\n")
# id(a[0:3])= 1696327019216
# id(c)= 1696327019136


# 함수로 전달될 때에도 같은 ndarray 객체를 접근하게 됩니다.
def f(x):
   print("parameter id in function = ", id(x))

print("argument id  = ", id(a))
f(a)
# 함수 호출시 사용된 아규먼트와 함수의 파라미터의 id가 동일합니다.
# argument id  = 1696294860160
# parameter id in function =  1696294860160




4.2. 원본 배열의 데이터를 공유하는 새로운 NumPy 배열 객체를 생성하는 경우

배열의 shape를 변경하거나 배열의 부분을 슬라이싱하여 만들어지는 배열을 뷰라고 합니다.

기존 배열의 데이터(정확히는 데이터가 저장된 메모리 공간)을 공유하기 때문에 한쪽에서 데이터를 변경하면 서로 영향을 주게 됩니다.


import numpy as np


a = np.arange(12)
# a의 뷰로서 b를 생성합니다.
b = a.view()


# b는 a의 데이터를 공유하게 됩니다.
print("a = ", a)
print("b = ", b)
print("\n")
# a =  [ 0 1 2  3 4 5 6 7  8 9 10 11]
# b =  [ 0 1 2  3 4 5 6 7  8 9 10 11]


# 다음처럼 b가 공유하는 메모리 공간은 a가 할당받은 메모리라는 것을 확인할 수 있습니다.
print(b.base is a)
print("\n")
# True


# 데이터는 공유하고 있지만 a와 b는 서로 다른 NumPy 배열 객체입니다.
print("id(a) = ", id(a))
print("id(b) = ", id(b))
print("\n")
# id가 다릅니다.
# id(a) =  2231224530304
# id(b) =  2231224530544


# 같은 데이터를 공유하기 때문에 한쪽을 바꾸면 다른 쪽에 반영이 됩니다.
a[0] = -1
b[11] = -1
print("a = ", a)
print("b = ", b)
print("\n")
# a =  [-1  1 2  3 4 5  6 7 8 9 10 -1]
# b =  [-1  1 2  3 4 5  6 7 8 9 10 -1]


# 하지만 다른 ndarray 객체이기 때문에 한쪽의 shape를 변경해도 다른쪽에 반영이 안됩니다.
a.resize(3,4)
print("a = \n", a)
print("b = ", b)
print("\n")
# a =  
#  [[-1  1 2 3]
#  [ 4  5 6 7]
#  [ 8  9 10 -1]]
# b =  [-1 1 2  3 4 5 6 7  8 9 10 -1]


# 현재 a와 b는 다른 shape이지만 같은 데이터를 공유하기 때문에
# 값을 변경하면 서로 영향을 줍니다.
a[0,2] = 10000
b[11] = 20000
print("a = \n", a)
print("b = ", b)
print("\n")
# a =
#  [[  -1  1 10000     3]
#  [  4 5     6 7]
#  [  8 9    10 20000]]
# b =  [ -1    1 10000     3     4 5     6 7 8     9 10 20000]


# 배열 a의 두번째와 세번째 열만 슬라이싱하여 뷰를 생성합니다.
d = a[:, 1:3]
print("d = \n", d)
print("\n")
# d =  
#  [[   1 10000]
#  [  5 6]
#  [  9 10]]


# 배열 a의 뷰인 d의 전체 원소값을 10으로 변경하면
d[:] = 10
print("d = \n", d)
print("\n")
# d =  
#  [[10 10]
#  [10 10]
#  [10 10]]


# 배열 a에도 반영이 됩니다.
print("a = \n", a)
# a =
#  [[  -1 10    10     3]
#  [  4  10    10     7]
#  [  8 10    10 20000]]




4.3. 새로운 NumPy 배열 객체의 새로운 메모리 공간에 데이터를 복사하는 경우

기존 배열의 데이터를 공유하지 않는 복사본이 필요한 경우 ndarray.copy 메소드를 사용할 수 있습니다.

배열의 shape를 변경하거나 배열의 부분을 슬라이싱한 경우 ndarray.copy 메소드를 추가로 적용하면 기존 배열의 데이터를 공유하지 않고 새로운 메모리 공간에 데이터를 복사해서 사용하게 됩니다.


import numpy as np


a = np.arange(12)
# ndarray.copy 메소드를 사용하면 새로운 NumPy 객체의 새로운 메모리 공간에 배열 a의 데이터를 복사합니다.
b = a.copy()


# a와 b는 같은 데이터를 갖고 있습니다.
print("a = ", a)
print("b = ", b)
print("\n")
# a =  [ 0 1 2  3 4 5 6 7  8 9 10 11]
# b =  [ 0 1 2  3 4 5 6 7  8 9 10 11]


# id가 다르기 때문에
# a와 b는 서로 다른 NumPy 배열 객체입니다.
print("id(a) = ", id(a))
print("id(b) = ", id(b))
print("\n")
# id(a) =  2414248490368
# id(b) =  2414248490608


# ndarray.base 속성이 둘 다 None이기 때문에
# 다른 NumPy의 데이터를 공유하고 있지 않다는 것을 알 수 있습니다.
print("a.base = ", a.base)
print("b.base = ", b.base)
print("\n")
# a.base =  None
# b.base =  None


# 따라서 데이터를 변경해도 서로 영향을 주지 않습니다.
a[0] = -1
b[11] = -1
print("a = ", a)
print("b = ", b)
print("\n")
# a =  [-1  1 2  3 4 5  6 7 8 9 10 11]
# b =  [ 0 1 2  3 4 5 6 7  8 9 10 -1]



+ Recent posts

티스토리 툴바