6.9. Hàm phổ quát (Universal functions)

Một hàm phổ quát (ufunc) là một hàm toán học áp dụng cho từng phần tử của mảng trong một lần gọi. Các toán tử số học ở trang trước là các hàm phổ quát dưới dạng cú pháp toán tử; trang này là danh mục các hàm có tên bao gồm lượng giác, exp / log, làm tròn, và một số hàm khác.

Mỗi ufunc chấp nhận một giá trị vô hướng, một iterable Python, hoặc một ndarray, và trả về một số float đơn (khi đầu vào là vô hướng) hoặc một ndarray kiểu float:

from ulab import numpy as np

np.exp(2.0)                    # 7.389...
np.sin(range(4))               # 1-D float ndarray
np.sqrt([1, 4, 9, 16])         # array([1.0, 2.0, 3.0, 4.0])

a = np.arange(9).reshape((3, 3))
np.exp(a)                      # 3x3 float ndarray

6.9.1. Danh mục hàm

numpy cung cấp các hàm toán học mà ứng dụng nhúng thường cần nhất:

Mỗi hàm xử lý toàn bộ mảng trong một lần gọi thư viện. Tốc độ tăng so với list comprehension Python gọi math.sin() từng phần tử một là 10-30 lần trên bộ đệm thông thường.

6.9.2. Từ khóa out=

Mỗi lần gọi ufunc thông thường cấp phát một mảng kết quả mới để lưu đầu ra. Trong một vòng lặp chạy nhiều lần mỗi giây, các lần cấp phát đó tích lũy lại và lãng phí RAM. Truyền out= -- một mảng float đã tồn tại, có cùng hình dạng với đầu vào -- sẽ ghi kết quả vào mảng đó thay vì cấp phát mảng mới:

x = np.linspace(0, 2 * np.pi, num=256)
y = np.zeros(256)

while True:
    np.sin(x, out=y)
    # use y ...

Nếu out có kiểu dữ liệu hoặc hình dạng sai, hàm sẽ phát sinh ngoại lệ. Từ khóa này được hỗ trợ trên mọi ufunc trong trang này; đây là cách gọn gàng nhất để giữ cho vòng lặp xử lý tín hiệu trực tuyến không cấp phát bộ nhớ.

6.9.3. Ufunc hai đối số

arctan2() là ufunc hai đối số thực sự duy nhất trong danh sách trên -- nó trả về arctangent nhận biết góc phần tư của y / x và phát (broadcast) hai toán hạng:

y = np.array([1, 2.2, 33.33, 444.444])
np.arctan2(y, 1.0)             # against a scalar
np.arctan2(1.0, y)             # the other way
np.arctan2(y, y)               # against another array

6.9.4. Kết hợp các hàm phổ quát

Các hàm phổ quát có thể kết hợp như bất kỳ biểu thức mảng nào khác. Một số mẫu thường gặp trên camera:

Hiệu chỉnh gamma (trong không gian float)

gamma = 0.5
out = 255.0 * (frame / 255.0) ** gamma

Bộ làm mịn low-pass đơn giản (alpha gần 1.0 có nghĩa là cập nhật chậm):

alpha = 0.95
filtered = alpha * filtered + (1.0 - alpha) * sample

Sigmoid

sigmoid = 1.0 / (1.0 + np.exp(-x))

Phổ năng lượng theo dB

spectrum = 20.0 * np.log10(np.abs(real) + 1e-12)

6.9.5. np.vectorize

Một hàm Python thông thường có thể được nâng cấp thành hàm có dạng ufunc bằng vectorize(). Callable kết quả chấp nhận giá trị vô hướng, iterable, hoặc ndarray

def f(x):
    return x * x

vf = np.vectorize(f)

vf(44.0)                          # array([1936.0])
vf(np.array([1, 2, 3, 4]))        # array([1.0, 4.0, 9.0, 16.0])
vf([2, 3, 4])                     # array([4.0, 9.0, 16.0])

Mặc định kiểu kết quả là float. otypes= ghi đè nó:

vf_u8 = np.vectorize(f, otypes=np.uint8)
vf_u8([1, 2, 3, 4])
# array([1, 4, 9, 16], dtype=uint8)

Hàm Python phải nhận một đối số duy nhất và trả về một số duy nhất.

vectorize() chủ yếu mang tính cú pháp -- hàm Python được bọc vẫn phải chạy một lần cho mỗi phần tử, do đó hầu hết chi phí thông dịch per-element mà một ufunc thực sự tránh được lại quay trở lại. Hãy mong đợi tốc độ tăng khiêm tốn 30%-50% so với list comprehension, không phải 30 lần như một hàm phổ quát thực sự. Đây là công cụ phù hợp khi một hàm cần hoạt động với cả giá trị vô hướng, danh sách, lẫn mảng dưới cùng một tên -- không phải khi tốc độ thô là mục tiêu.

Để biết đầy đủ chữ ký gọi của từng hàm phổ quát được liệt kê ở trên, xem numpy --- các phép toán mảng tương thích numpy.