공부/likelion

4. Meta-data of ndarrays

세안_ 2021. 9. 29. 20:00

 

메타인지를 아십니까?

한 때 메타인지라는 단어가 유행한 적이 있었다.

메타인지란, 인지에 대한 한 차원 더 높은 고차원에 대한 생각으로,
자기 자신의 인지에 대해 어떤 것을 알고, 어떤 것을 모르는지를 일컫는 말이다. 

그렇다면 이와 연결해서 메타 데이터는 무엇일까?
데이터에 대한 데이터, 즉 우리가 선언한 데이터에 대하여 관련된 정보를 일컫는 말이다.
우리가 다루는 것은 ndarray, 즉 배열이니 해당되는 배열의 차원, 형태, 축, 요소 등등을 가리킨다. 

즉 포스팅에서는 메타 데이터, 즉 데이터에 대한 데이터에 대한 설명을 하는 함수들에 대하여 다룬다. 

포스팅에서 다룰 함수들은 다음과 같다.

  • ndarray.ndim
  • ndarray.shape
  • ndarray.size
  • ndarray.dtype
  • ndarray.itemsize
  • ndarray.nbytes

1. ndim

먼저 ndim들이 어떤 기능을 하는지 알아보기 위해 데이터를 만들어보자. 

각각 스칼라, 1차원, 2차원, 3차원 텐서이다. 

scalar_np = np.array(3.14)
vector_np = np.array([1, 2, 3])
matrix_np = np.array([[1, 2], [3, 4]])
tensor_np = np.array([[[1, 2, 3], [4, 5, 6]],[[11, 12, 13], [14, 15, 16]]])
# ndim = 해당 객체의 dimension, 즉 차원을 나타낸다. 
print(scalar_np.ndim)
print(vector_np.ndim)
print(matrix_np.ndim)
print(tensor_np.ndim)

간단하다. 스칼라는 상수값이므로 0차원, 나머지는 각각 차원에 맞는 값을 출력한다.

1. shape (관련 함수 : ndarray.reshape, ndarray.resize ndarray.flatten, ndarray.ravel)

# shape = 해당 객체의 차원을 나타낸다. 표기되는 순서는 3차원을 기준으로 z,y,x 순.
print(scalar_np.shape) 
print(vector_np.shape)
print(matrix_np.shape)
print(tensor_np.shape)
print(type(tensor_np.shape))

shape와 ndim을 비교해보면, np.ndim 함수는 len(shape)와 같은 것을 알 수 있다.
스칼라의 경우 상수 값만 존재하기 때문에, shape를 통해 차원을 찍어봐도 공란으로 나오는 것을 알 수 있다. 
또한 shape는 튜플 형태로 데이터를 반환한다.

2. size

#size = 차원의 크기를 나타낸다. 
#크기란, 해당 객체 안의 차원 안에 있는 원소를 포함하여 총 얼마나 원소가 들어갈 공간이 있는지를 나타낸다.

S = np.ones(shape=(10, ))
V = np.ones(shape=(3, 4))
M = np.ones(shape=(3, 4, 5))
T = np.ones(shape=(2, 3, 4, 5, 6))

print(S.size)
print(V.size)
print(M.size)
print(T.size)

결과를 보면 어느 정도 유추할 수 있겠지만
각 shape의 값을 서로 곱한 값이 size와 동일하다.

예외로, 공간만 만들라고 선언하면 어떻게 나타날까?
이런 걸 만들어주는 함수는 없나?

a = np.array([[],[]])
print(a.ndim)
print(a.size)

작동은 잘 된다.

3. dtype (dtype 바꿔주기 : ndarray.astype)

우선 ndarray에는 다음과 같은 자료형이 있다[1]

int 및 uint의 자료형
실수 자료형
복소수 자료형

 

그래서 해당 ndarray 객체를, dtype 함수를 이용해 확인할 수 있다

a = np.arange(10)
b = np.arange(3.14)
c = np.array(-10,dtype = np.uint32)
d = np.array(-10,dtype = np.int32)

print(a.dtype)
print(b.dtype)
print(c, c.dtype)
print(d, d.dtype)

uint의 경우 음수라고 하더라도 unsigned로 취급되어 비트 연산을 사용하여 변환 처리하는 것을 볼 수 있다.
0x7FFFFFFF +1 +x 이므로 4294967295 + 1 +(-10) = 4294967295-9 = 4294967286
양수에서 저것보다 더 큰 수는 long으로 처리된다.

따라서 우리가 가진 원소가 저것보다 더 큰 수일 경우에는
파이썬의 특성상 남아있는 가용 메모리를 수 표현에 끌어다 쓸 수 있기 때문에(그것도 자동으로)[2]
메모리 부족에 시달리는 데이터 분석의 특성상 단순한 숫자 표현 때문에 처리 간 OOM가 생길 수도 있을 것이다.
그러면 어떻게 해야 될까? 해당 값들을 범주형으로 처리하거나,
저 값에 가까운 값일수록 이상치가 아닐지 판단하는 태도가 필요할 것 같다.


4. itemsize

배열이 가지고 있는 요소, 즉 item의 크기를 가리킨다. 8bit = 1byte를 까먹지 말자.

complex128은 실수부와 허수부에 각각 float64가 적용되므로 저렇게 출력된다

x = np.array([1,2,3], dtype=np.float64)
y = np.array([1,2,3], dtype=np.float32)
z = np.array([1,2,3], dtype=np.float16)
a = np.array([1,2,3], dtype=np.complex128)


print(x.itemsize)
print(y.itemsize)
print(z.itemsize)
print(a.itemsize)

또한 배열의 크기(size) * 요소의 크기(itemsize)를 곱하면 해당 byte를 알 수 있는데,

그렇다면 이 연산을 해주는 건 없을까?

5. nbytes

그게 바로 nbytes다.

x = np.array([1,2,3], dtype=np.float64)

print(x.itemsize * x.size)
print(x.nbytes)

 

6. 참고자료

[1] https://kongdols-room.tistory.com/53?category=796900 

 

NumPy의 데이터 타입(자료형), 관련된 함수 - NumPy(2)

참고 자료 https://docs.scipy.org/doc/numpy/user/basics.types.html 파이썬 버전 3.7 기준 NumPy 버전 1.15 기준 본 포스팅에선 NumPy의 주요 자료형인 정수(int), 부호없는 정수(uint), 실수(float), 복소수(c..

kongdols-room.tistory.com

[2] https://mortada.net/can-integer-operations-overflow-in-python.html

 

Can Integer Operations Overflow in Python? — Random Points

We can see that it takes 28 bytes before we get to $2^{30}$ where python allocates 4 more bytes to store larger integers. Certainly not the most compact representation, as a raw 64-bit array (i.e. 8 bytes) could do the job with fixed-precision. However we

mortada.net