6.3. 배열 만들기

이 페이지 이후의 모든 예제는 이미 손에 들고 있는 ndarray로 시작합니다. 이 페이지는 그 배열이 어떻게 생겨나는지에 대한 카탈로그입니다. 생성자에는 네 가지 계열이 있습니다:

  • Python 이터러블로부터 – 일반적인 리터럴 / 리스트 / 튜플 형식.

  • 주어진 형상으로 미리 채워짐 – 0, 1, 상수 값, 단위 행렬.

  • 시퀀스로 생성됨 – 범위형 또는 균등 간격 값.

  • 이미 RAM에 있는 버퍼를 래핑함 – 주변장치의 경우.

모든 생성자는 dtype= 키워드를 받으며 기본값은 float입니다. 센서 데이터는 거의 항상 기본값보다 작은 dtype을 원합니다.

아래의 모든 예제는 다음으로 시작합니다:

from ulab import numpy as np

6.3.1. Python 이터러블로부터

array()는 숫자로 이루어진 임의의 이터러블로부터 ndarray를 만듭니다:

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

출력:

array([1.0, 2.0, 3.0, 4.0], dtype=float)

중첩 이터러블은 다차원 배열을 생성합니다. 내부 이터러블은 모두 같은 길이를 가져야 하며, 그렇지 않으면 ValueError가 발생합니다:

m = np.array([[1, 2, 3],
              [4, 5, 6]], dtype=np.uint8)

기존의 ndarray 또한 유효한 입력입니다. array()는 항상 복사합니다. 복사가 필요하지 않을 때 이를 피하려면 asarray()를 사용하세요:

b = np.asarray(a, dtype=np.float)   # same dtype -> no copy

6.3.2. 주어진 형상으로 미리 채워짐

대상 형상은 알려져 있지만 내용이 아직 정해지지 않았을 때는 버퍼를 미리 할당하고 나중에 그 안에 씁니다:

  • zeros() – 0으로 채워짐.

  • ones() – 1로 채워짐.

  • full() – 주어진 값으로 채워짐.

  • empty()zeros()의 별칭(ulab은 버퍼를 초기화되지 않은 상태로 두지 않습니다).

  • eye()k번째 대각선에 1이 있는 단위 행렬과 유사한 N x M 행렬.

  • diag() – 벡터로부터 만든 대각 행렬, 또는 행렬의 대각선.

np.zeros((3, 3))                   # 3x3 of zeros
np.ones(5, dtype=np.uint8)         # length-5 vector of ones
np.full((2, 3), 7, dtype=np.int8)  # 2x3, all 7
np.eye(4)                          # 4x4 identity
np.diag([1, 2, 3])                 # 3x3, [1, 2, 3] on the diagonal

shape 인자는 단일 정수(1차원 배열의 경우)이거나 튜플입니다.

6.3.3. 시퀀스로 생성됨

  • arange() – 내장 range()처럼 균등 간격 값을 생성하지만 항상 ndarray를 반환합니다:

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace() – 두 경계 사이의 num개 균등 간격 점으로, endpoint=True일 때 상한이 포함됩니다:

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() – 기하적으로 간격을 둔 점. startstop은 끝점이 아니라 지수입니다. 결과는 base ** start에서 base ** stop까지 이어집니다:

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() – 두 개의 1차원 배열로부터 두 개의 좌표 행렬을 만들어, 픽셀별 함수 f(x, y)를 하나의 벡터화된 호출로 전체 그리드에 걸쳐 계산할 수 있게 합니다. 길이 W의 x-벡터와 길이 H의 y-벡터가 주어지면, meshgrid는 두 개의 H x W 행렬을 반환합니다. X는 모든 행에 걸쳐 아래로 반복된 x-벡터이고, Y는 모든 열에 걸쳐 반복된 y-벡터이므로, X[i, j]는 행 i와 열 j에 있는 셀의 x-좌표이고 Y[i, j]는 y-좌표입니다:

    x = np.arange(4)            # [0, 1, 2, 3]
    y = np.arange(3)            # [0, 1, 2]
    X, Y = np.meshgrid(x, y)
    # X = [[0, 1, 2, 3],
    #      [0, 1, 2, 3],
    #      [0, 1, 2, 3]]
    # Y = [[0, 0, 0, 0],
    #      [1, 1, 1, 1],
    #      [2, 2, 2, 2]]
    

    그러면 f(X, Y)는 하나의 표현식으로 그리드의 모든 셀에서 함수를 계산합니다. 예를 들어 (H, W) 프레임에 대한 중심으로부터의 거리 맵은 meshgrid()가 반환한 행렬에 대해 np.sqrt((X - cx)**2 + (Y - cy)**2)입니다.

6.3.4. 결합

concatenate()는 기존 축을 따라 배열의 튜플을 결합합니다:

a = np.array([[1, 2], [3, 4]], dtype=np.uint8)
b = np.array([[5, 6]],         dtype=np.uint8)
np.concatenate((a, b), axis=0)
# array([[1, 2], [3, 4], [5, 6]], dtype=uint8)

모든 입력은 같은 dtype과 ndim을 공유해야 하며, 결합하는 축을 제외한 모든 축에서 일치해야 합니다. concatenate()는 모든 입력을 담을 만큼 충분히 큰 새 배열을 할당하고 데이터를 복사해 넣으므로, 이미 존재하는 배열을 일회성으로 결합하는 데 적합한 도구입니다. 스트리밍 루프 내부에서는 적합하지 않으며, 그곳에서는 대상을 한 번 미리 할당하고 슬라이스 할당을 통해 그 안에 쓰는 것이 패턴입니다.

6.3.5. 기존 버퍼 래핑하기

카메라에서 가장 유용한 생성자는 frombuffer()입니다. 이것은 기존의 bytes 유사 버퍼를 단 한 바이트도 복사하지 않고 1차원 ndarray로 재해석합니다:

buf = bytearray(8)
audio = np.frombuffer(buf, dtype=np.int16)
# 4 int16 samples, sharing memory with buf

audio를 통한 쓰기는 buf에서 보이며 그 반대도 마찬가지입니다. 선택한 dtype은 버퍼 길이를 균등하게 나누어야 합니다.

offset=은 버퍼 시작 부분의 헤더를 건너뛰고, count=는 읽는 요소의 개수를 제한합니다:

np.frombuffer(buf, dtype=np.uint8, offset=2, count=4)

이것은 주변장치가 애플리케이션에 원시 버퍼를 넘겨줄 때 적합한 생성자입니다 – bytearray의 ADC 샘플, SPI에서 가져온 페이로드. 주변장치가 쓴 바이트가 곧 배열입니다.

주변장치가 카메라의 CPU가 기본적으로 읽지 않는 바이트 순서로 다중 바이트 값을 쓸 때, byteswap()은 각 요소의 바이트 순서를 뒤집어 값이 올바르게 읽히도록 합니다. 기본적으로 새 배열을 반환하며, inplace=True를 전달하면 원본을 그 자리에서 수정합니다.

frombuffer()numpy 자체가 정의하는 dtype만 처리합니다. 32비트 정수 샘플을 생성하는 주변장치의 경우, from_int32_buffer() 및 관련 함수가 한 번에 float로 변환합니다.