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를 생각하시면 됩니다.
반면에, 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 한 개의 데이터 입니다.
혹시 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 |
---|