6.3. Tạo mảng

Mỗi ví dụ trong các trang còn lại bắt đầu với một ndarray đã có sẵn. Trang này là danh mục về cách mảng đó được tạo ra. Có bốn nhóm constructor:

  • Từ một iterable Python -- dạng literal / list / tuple thông thường.

  • Điền sẵn theo hình dạng cho trước -- zeros, ones, một giá trị hằng, ma trận đơn vị.

  • Tạo ra như một dãy -- giá trị theo khoảng hoặc cách đều nhau.

  • Bọc một buffer đã có trong RAM -- trường hợp ngoại vi.

Mọi constructor đều nhận từ khóa dtype= và mặc định là float. Dữ liệu cảm biến hầu như luôn cần dtype nhỏ hơn mặc định.

Mọi ví dụ bên dưới đều bắt đầu với:

from ulab import numpy as np

6.3.1. Từ một iterable Python

array() xây dựng một ndarray từ bất kỳ iterable nào gồm các số:

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

Đầu ra:

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

Các iterable lồng nhau tạo ra mảng nhiều chiều. Các iterable bên trong phải có cùng độ dài, nếu không ValueError sẽ được phát sinh:

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

Một ndarray đã tồn tại cũng là đầu vào hợp lệ; array() luôn sao chép. Để tránh sao chép khi không cần thiết, dùng asarray()

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

6.3.2. Điền sẵn theo hình dạng cho trước

Khi hình dạng đích đã biết nhưng nội dung chưa có, hãy cấp phát buffer trước và ghi vào sau:

  • zeros() -- điền bằng số không.

  • ones() -- điền bằng số một.

  • full() -- điền bằng một giá trị cho trước.

  • empty() -- bí danh cho zeros() (ulab không để buffer chưa khởi tạo).

  • eye() -- ma trận N-nhân-M dạng đơn vị với số một trên đường chéo thứ k.

  • diag() -- ma trận đường chéo từ một vector, hoặc đường chéo của một ma trận.

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

Đối số shape là một số nguyên (cho mảng 1 chiều) hoặc một tuple.

6.3.3. Tạo ra như một dãy

  • arange() -- giá trị cách đều giống hàm dựng sẵn range(), nhưng luôn trả về ndarray

    np.arange(0, 10, 2)            # array([0, 2, 4, 6, 8])
    
  • linspace() -- num điểm cách đều nhau giữa hai giới hạn, với giới hạn trên được bao gồm khi endpoint=True

    np.linspace(0, 1, num=11)      # 0.0, 0.1, ..., 1.0
    
  • logspace() -- các điểm cách đều theo tỉ lệ hình học. startstopsố mũ, không phải điểm cuối; kết quả chạy từ base ** start đến base ** stop

    np.logspace(0, 3, num=4)       # 1.0, 10.0, 100.0, 1000.0
    
  • meshgrid() -- xây dựng hai ma trận tọa độ từ hai mảng 1 chiều để hàm f(x, y) theo từng điểm ảnh có thể được tính trên toàn bộ lưới trong một lần gọi vector hóa. Cho một vector x có độ dài W và vector y có độ dài H, meshgrid trả về hai ma trận H-nhân-W: X là vector x được lặp lại xuống mọi hàng, Y là vector y được lặp lại ngang mọi cột, sao cho X[i, j] là tọa độ x và Y[i, j] là tọa độ y của ô tại hàng i và cột j

    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) sau đó tính hàm tại mọi ô của lưới trong một biểu thức. Ví dụ, một bản đồ khoảng cách từ tâm trên một khung hình (H, W)np.sqrt((X - cx)**2 + (Y - cy)**2) trên các ma trận mà meshgrid() trả về.

6.3.4. Ghép nối

concatenate() ghép một tuple các mảng theo một trục đã có:

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)

Tất cả đầu vào phải có cùng dtype và ndim, và khớp trên mọi trục ngoài trục ghép. concatenate() cấp phát một mảng mới đủ lớn để chứa mọi đầu vào và sao chép dữ liệu vào, vì vậy nó là công cụ phù hợp để ghép một lần các mảng đã tồn tại; đây là công cụ không phù hợp trong vòng lặp streaming, nơi mà cấp phát đích trước một lần rồi ghi vào qua slice assignment là cách làm đúng.

6.3.5. Bọc một buffer đã tồn tại

Constructor hữu ích nhất trên camera là frombuffer(). Nó diễn giải lại một buffer dạng bytes đã tồn tại thành một ndarray 1 chiều mà không sao chép bất kỳ byte nào:

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

Các thao tác ghi qua audio được phản ánh trong buf và ngược lại. dtype được chọn phải chia đều chiều dài buffer.

offset= bỏ qua phần header ở đầu buffer; count= giới hạn số phần tử được đọc:

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

Đây là constructor phù hợp bất cứ khi nào một ngoại vi truyền cho ứng dụng một buffer thô -- mẫu ADC trong bytearray, payload lấy từ SPI. Các byte ngoại vi đã ghi chính là mảng.

Khi một ngoại vi ghi các giá trị nhiều byte theo thứ tự byte mà CPU của camera không đọc được tự nhiên, byteswap() đảo ngược thứ tự byte của mỗi phần tử để các giá trị đọc đúng. Theo mặc định nó trả về một mảng mới; truyền inplace=True để sửa đổi nguồn tại chỗ.

frombuffer() chỉ xử lý các dtype mà chính numpy định nghĩa. Với các ngoại vi tạo ra mẫu số nguyên 32-bit, from_int32_buffer() và các hàm tương tự chuyển đổi sang float trong một lần chạy.