본문 바로가기

Python/STL

Python - np.array vs list

 

python에는 데이터를 다루기 위해 주요 쓰이는 주요한 자료구조가 있습니다.

## 서로의 동작이 사실 좀 다릅니다. 자세한 내용은 다음에...

## list 선언.
a = list([1, 2, 3])
a = [1, 2, 3]

## set 선언. 
b = set([1, 2, 3])
b = {1, 2, 3}

## tuple 선언.
c = tuple([1, 2, 3])
c = (1, 2, 3)

## dict 선언
d = dict(k1=1, k2=2, k3=3)
d = {"k1": 1, "k2": 2, "k3": 3}

 

List, set, tuple, dict은 다음과 같은 특징을 가지고 있습니다.

Type 특징
List 수정, 중복데이터를 허용한다.
Set 중복데이터는 허용하지 않는다.
Tuple 수정이 불가능한 구조
Dict key-value 형태의 구조

 

 

그런데 사실 python을 하다보면 numpy라는 library를 자연스럽게 접하게 되고, 데이터를 다루기 시작합니다.

import numpy as np

a = np.array([1, 2, 3])

 

list와 np.array는 언뜻 보기에는 비슷해 보입니다. 두 개는 언제 어떻게 사용하는 걸까요?

 

 


list : 데이터들이 저장된 주소의 묶음

List는 데이터들의 주소를 하나로 묶어 관리하는 데이터 입니다.

자세한 내용을 알고 싶으시면 List의 데이터 저장방식에 대한 글을 참조해 주세요

 

list의 method를 먼저 살펴볼까요?

dir(list)
[## __magic_method__는 생략
'append',	## 맨 뒤에 데이터를 추가합니다.
'clear',	## 모든 요소 제거
'copy',   	## 값을 복사합니다. list를 그냥 대입하면 주소가 복사됩니다.
'count',	## 찾고자 하는 항목이 몇개인지 확인
'extend',	## 맨 뒤에 데이터를 연장합니다.
'index',	## 찾고자 하는 항목이 몇번째에 있는지 확인
'insert',	## 추가할 데이터를 특정 index에 삽입
'pop',  	## 맨 뒤에 데이터를 출력 후 제거.
'remove',	## 지우고자 하는 데이터를 지움
'reverse',	## 순서를 반대로 뒤집음
'sort']  	## 순서를 정렬함.

 

list는 기본적으로 append, extend, index 와 같이 데이터를 추가할 수 있으면서, pop, remove와 같이 삭제도 자유롭습니다.

 

여러 데이터들을 묶어서 관리한다는 느낌으로 List를 생각하시면 됩니다.

List는 주소만 보관하기 때문에 다양한 type의 데이터들도 묶어서 관리할 수 있다.

반면에, array는 어떤식으로 데이터를 다룰까요?

 

 


np.array : 차원을 가지고 있는 하나의 데이터

 

이번에는 array의 method를 살펴볼까요?

>>> dir(np.array(0))
['T', ## __magic_method__는 생략
'all', ## 모든 원소가 조건을 만족하는지 확인 후, bool type 출력
'any', ## 적어도 한개의 원소가 조건을 만족하는지 확인 후, bool type 출력
'argmax', ## 최대값의 index 출력
'argmin', ## 최소값의 index 출력
'argpartition', ## 부분정렬의 index 출력
'argsort', ## 전체 정렬의 index 출력
'astype', ## 배열의 type을 변경
'base', ## 참조 원본값 출력, 원본시 None 출력
'byteswap', ## Little-endian과 Big-endian방식을 swap후 출력
'choose', ## 배열의 index로 값을 선택 
'clip', ## min, max 범위 gate 후 출력
'compress', ## bool값으로 array를 부분 추출
'conj', ##
'conjugate', ##
'copy', ## 복사
'ctypes', ## C 유형의 type으로 변환
'cumprod', ##
'cumsum', ##
'data', ##
'diagonal', ## 대각선 값 출력
'dot', ## 내적값을 출력
'dtype', ## type을 출력
'dump', ## 데이터를 파일 형태로 저장(pickle)
'dumps', ## 데이터를 string 형태로 저장(pickle)
'fill', ## 특정 값으로 전체를 대입
'flags', ## 메모리 레이아웃에 대한 정보를 출력
'flat', ##
'flatten', ## 차원을 안쪽에서 하나 풀고 출력
'getfield', ##
'imag', ## 복소수의 허수부 출력
'item', ## index에 따라 python 표준 스칼라 값이 출력
'itemset', ##
'itemsize', ## 원소의 byte 크기를 출력
'max', ## 최대값 출력
'mean', ## 평균값 출력
'min', ## 최소값 출력
'nbytes', ## 데이터의 전체 bytes수를 출력
'ndim', ## 차원 수 출력
'newbyteorder', ##
'nonzero', ## 0이 아닌 index 출력
'partition', ##
'prod', ## 곱연산 후 출력
'ptp', ##
'put', ##
'ravel', ##
'real', ## 복소수의 실수부 출력
'repeat', ##
'reshape', ## 입력된 차원 크기로 재배열해서 출력
'resize', ## 입력된 차원 크기로 배열을 변환
'round', ## 반올림한 값을 출력
'searchsorted', ##
'setfield', ##
'setflags', ##
'shape', ## 현재 배열의 차원을 출력
'size', ## 현재 배열의 원소 개수를 출력
'sort', ## 배열을 정렬
'squeeze', ## 길이가 1인 하위 차원을 출력
'std', ## 표준편차를 출력
'strides', ##
'sum', ## 전체를 합
'swapaxes', ##
'take', ##
'tobytes', ##
'tofile', ##
'tolist', ## list()와 비슷한 동작을 한다.
'tostring', ##
'trace', ##
'transpose', ##
'var', ##
'view' ## 동일한 byte를 새롭게 본다
]

 

너무 많네요...저도 여기 있는 method를 전부 사용하지는 않았으니, 주로 사용해 본 것들 위주로 설명해 보았습니다.

 

그런데, 설명을 하고 나니 뭔가 이상하지 않습니까? array에는 method가 저렇게 많은데, list에 있던 append, extend, insert같은 함수가 제공되지 않습니다. 심지어 remove, pop과 같은 것 또한 없습니다.

 

이유는 마치 array 자체가 하나의 데이터 객체로써 다뤄지기를 원하기 때문입니다. Array가 아무리 길어지고 복잡해도 그 데이터는 한 개의 개념을 설명하고 있을 뿐이죠. 20x20x80x100인 4차원 크기의 복잡한 데이터가 있다 해도, Array 하나에 담겨진 순간, 이 데이터는 단지 array 한 개의 데이터 입니다.

5차원 Array예시. 아무리 데이터가 많아도, Array에서 한 번에 관리되는 이상 "하나의 데이터 객체"로 생각할 수 있다.

 

혹시 array를 list처럼 사용하고 계신지는 아닌지 확인해 봅시다. dtype을 통해 현재 쓰고 있는 array의 data type을 확인 해 볼 수 있습니다. 숫자를 다루고자 했던 array가 어느샌가 Unicode string을 사용할 수도 있습니다.

>>> import numpy as np
>>> a = np.array([1,2,3, "4", 5.5])
>>> a.dtype
dtype('<U32')   ## 숫자를 다뤄야 하는 array를 Unicode string으로 data를 잘못 다루고 있다.

>>> b = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> b.dtype()
dtype('int32')  ## 정수 숫자만 넣었더니 정수형 데이터로 type이 명확해졌다.

>>> c = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=np.uint8)
>>> c.dtype()
dtype('uint8')  ## 이미지를 다루는 분들은 uint8 type이 익숙하실 것이다.

 

array 자체에는 데이터를 더 붙이거나 삭제하는 개념은 없지만, numpy 자체에는 append, concate, slice를 지원합니다.

>>> a1 = np.array([1, 2, 3])
>>> a2 = np.array([[4, 5, 6], [7, 8, 9]])
>>> np.append(a1, a2)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])

>>> b1 = np.array([[1, 2, 3]])
>>> b2 = np.array([[4, 5, 6], [7, 8, 9]])
>>> np.append(b1, b2)
array([1, 2, 3, 4, 5, 6, 7, 8, 9]) 	## axis를 지정하지 않으면 flatten 하게 출력된다.

>>> c1 = np.array([[1, 2, 3]])
>>> c2 = np.array([[4, 5, 6], [7, 8, 9]])
>>> np.append(c1, c2, axis=0)
array([[1, 2, 3],
       [4, 5, 6],
       [7, 8, 9]])

 

하지만 append와 같은 method가 list에서 데이터 나열 확장이 아닌, 데이터 차원의 확장으로 사용합니다. 즉 array는 여전히 하나의 데이터로 취급합니다.

 

데이터가 하나로 묶여있으면, 어떤 점이 유리할까요? Vectorization과 Broadcasting을 알아봅시다!

 


 

Vectorization

## vectorization 예제

import numpy as np

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

## 배열 전체가 한 번에 덧셈이 된다.
c = a + b

## 배열 전체가 한 번에 곱셈이 된다.
d = a * b

print(c)  ## [5 7 9]
print(d)  ## [4 10 18]

 

 

Broadcasting

import numpy as np

## 2차원 배열
a = np.array([[1, 2, 3], [4, 5, 6]])

## 1차원 배열
b = np.array([10, 20, 30])

# 1차원 배열을 2차원에 맞춰 Broadcasting하고 연산을 수행
c = a + b

print(c)  ## [[11 22 33], [14 25 36]]

 

 

 


* reference

https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html

 

'Python > STL' 카테고리의 다른 글

Django REST framework tutorial3 - Class-based Views  (0) 2023.06.25