6.2. ndarray

ndarraynumpy에서 수치 데이터를 담는 타입입니다. 이것은 두 가지가 하나로 합쳐진 것입니다. 즉, 하나의 빽빽하게 채워진 데이터 블록과, 그 블록을 어떻게 읽을지 알려주는 그 앞의 작은 디스크립터입니다.

6.2.1. 상자 안쪽

데이터 블록은 배열의 모든 요소를 끝과 끝을 맞대어 담으며, 그 사이에 여분의 것은 아무것도 없습니다. 각 요소는 동일한 바이트 수를 차지합니다 – uint8 값 배열은 요소당 1바이트, uint16은 2바이트, float는 4바이트입니다. 256개 요소의 uint8 배열은 정확히 256바이트의 데이터입니다. 동일한 256개의 숫자를 Python list에 담으면 1킬로바이트를 차지합니다 – 값이 실제로 얼마나 적은 비트를 필요로 하든 상관없이 요소당 32비트 슬롯 하나가 사용됩니다.

디스크립터는 블록이 무엇을 의미하는지를 기록합니다. 다섯 개의 값이면 차원이 몇 개이든 상관없이 임의의 직사각형 배열을 기술하기에 충분합니다:

  • dtype – 요소 타입. 각 요소가 차지하는 바이트 수와 담을 수 있는 값의 범위를 결정합니다(Dtype에서 다룹니다).

  • itemsize – dtype에서 유도된, 한 요소의 바이트 폭.

  • ndim – 차원의 수(벡터는 1, 행렬은 2, 볼륨은 3, 최대 4까지).

  • shape – 각 차원을 따른 크기를 튜플로 나타낸 것.

  • strides – 각 축을 따라가기 위해 데이터 블록을 거치는 방법. 형태(shape)와 스트라이드(strides)에서 다룹니다.

그것이 전부입니다. numpy의 모든 빠른 경로 – 산술, 축소, 브로드캐스팅, 슬라이싱 – 는 이 다섯 개의 값과 데이터 포인터로부터 직접 작동하며, 요소별 Python 오버헤드가 없습니다.

6.2.2. 이 설계가 가져다주는 것

“빽빽한 블록 + 작은 디스크립터”에서 세 가지 속성이 나오며, 이것이 이 절의 나머지 부분이 어떻게 동작하는지를 정의합니다.

요소별 연산이 단일 호출로 실행됩니다. 형상이 일치하는 두 배열 사이의 a + b는 두 버퍼를 더하고 세 번째 버퍼에 쓰는데, 이 모든 것이 하나의 라이브러리 호출 안에서 이루어집니다. np.sin(a)는 모든 요소의 사인에 대해 같은 일을 합니다. 산술, 비교, 비트 연산자는 모두 이런 방식으로 작동합니다.

같은 데이터를 다른 방식으로 보는 것은 비용이 들지 않습니다. 배열의 하위 영역을 요청하거나 같은 데이터를 다른 형상으로 배치해 달라고 요청해도 어떤 바이트도 이동하지 않습니다. 이 연산은 같은 데이터 블록을 가리키는 새 디스크립터를 반환합니다. 그 결과를 라고 부릅니다 – 같은 기반 버퍼를 향한 두 번째 창입니다. 뷰를 통해 쓰면 원본에 쓰입니다.

형상이 섞여 있어도 여전히 작동합니다. 더 긴 배열에 대한 더 짧은 배열, 행렬에 대한 행, 행에 대한 열 – numpy는 이들을 브로드캐스팅으로 정렬합니다. 이는 어떤 짧은 축이 늘어나 긴 축에 맞춰지는지 결정하는 작은 규칙 집합입니다. 늘어남은 가상적이며, 데이터는 복제되지 않습니다.

6.2.3. 이 설계가 치르는 비용

같은 설계로부터 두 가지 제약이 따라옵니다.

모든 요소가 같은 타입을 가집니다. 리스트는 int 옆에 str, 그 옆에 int 값 세 개로 이루어진 리스트를 담을 수 있지만, ndarray는 그렇게 할 수 없습니다. dtype은 생성 시점에 고정됩니다. Dtype 페이지는 numpy가 지원하는 소수의 타입 집합과 하나를 고정함으로써 나오는 규칙들을 다룹니다.

배열을 키우는 것은 비용이 없지 않습니다. 리스트는 끝에 여분의 슬롯을 유지하며 .append를 저렴하게 지원합니다. ndarray는 정확히 필요한 만큼의 크기입니다. 추가는 새롭고 더 큰 버퍼를 할당하고 이전 내용을 그 안으로 복사하는 것을 의미합니다. append() 메서드는 의도적으로 없습니다. 카메라에서 올바른 패턴은 대상을 최종 크기로 미리 할당하고 채우는 것입니다. 성능에서 그 기법을 다룹니다.

데이터를 위한 빽빽한 타입 지정 버퍼, 메타데이터를 위한 작은 디스크립터, 그리고 세 가지 동작 보장(빠른 요소별 연산, 같은 데이터에 대한 복사 없는 대체 뷰, 그리고 브로드캐스트되는 형상)을 갖춘 ndarray는 이 장의 나머지가 기대는 기반입니다. 배열이 실제로 어떻게 존재하게 되는지 – 리터럴로부터, 미리 채워진 할당으로부터, 주변장치 버퍼로부터 – 가 다음의 실용적인 질문입니다.