6.3. Створення масивів¶
Кожен приклад на решті цих сторінок починається із вже готового ndarray. Ця сторінка — каталог того, як такий масив з’являється. Існують чотири сімейства конструкторів:
З ітерованого об’єкта Python — звичайна форма літерала / списку / кортежу.
Попередньо заповнений заданої форми — нулі, одиниці, константне значення, одинична матриця.
Згенерований як послідовність — значення в діапазоні або рівномірно розташовані.
Обгортання буфера, що вже є в RAM — периферійний варіант.
Кожен конструктор приймає ключове слово dtype= і за замовчуванням використовує float. Дані датчиків майже завжди потребують менший тип, ніж типовий.
Кожен приклад нижче починається з:
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()— заповнений нулями.ones()— заповнений одиницями.full()— заповнений заданим значенням.empty()— псевдонім дляzeros()(ulabне залишає буфер неініціалізованим).eye()— матрицяN-на-Mтипу одиничної з одиницями наk-й діагоналі.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 — це або одне ціле число (для одновимірного масиву), або кортеж.
6.3.3. Згенерований як послідовність¶
arange()— рівномірно розташовані значення, подібно до вбудованогоrange(), але завжди повертаєndarraynp.arange(0, 10, 2) # array([0, 2, 4, 6, 8])linspace()—numрівномірно розташованих точок між двома межами, з включенням верхньої межі приendpoint=Truenp.linspace(0, 1, num=11) # 0.0, 0.1, ..., 1.0logspace()— геометрично розташовані точки.startіstop— це показники степеня, а не кінцеві точки; результат проходить відbase ** startдоbase ** stopnp.logspace(0, 3, num=4) # 1.0, 10.0, 100.0, 1000.0meshgrid()— будує дві матриці координат з двох одновимірних масивів, щоб функцію на піксельf(x, y)можна було обчислити по всій сітці в одному векторизованому виклику. Маючи x-вектор довжиниWі y-вектор довжиниH,meshgridповертає дві матриціH-на-W:X— x-вектор, повторений у кожному рядку,Y— y-вектор, повторений у кожному стовпці, томуX[i, j]— це x-координата, аY[i, j]— y-координата комірки у рядкуiі стовпціjx = 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), наприклад, — цеnp.sqrt((X - cx)**2 + (Y - cy)**2)відносно матриць, що повернулаmeshgrid().
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)
Усі вхідні масиви повинні мати однаковий тип і ndim, і збігатися по всіх осях, крім осі об’єднання. concatenate() виділяє новий масив достатнього розміру для всіх вхідних даних і копіює їх — тому це правильний інструмент для одноразового об’єднання вже існуючих масивів; але невірний інструмент всередині циклу потокової обробки, де правильний підхід — попередньо виділити місце призначення один раз і записувати дані через присвоєння зрізу.
6.3.5. Обгортання існуючого буфера¶
Найкорисніший конструктор на камері — frombuffer(). Він повторно інтерпретує існуючий байто-подібний буфер як одновимірний 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)
Це правильний конструктор завжди, коли периферійний пристрій передає застосунку сирий буфер — зразки ADC у bytearray, корисне навантаження, отримане з SPI. Байти, записані периферійним пристроєм, і є масивом.
Коли периферійний пристрій записує багатобайтові значення у порядку байтів, який CPU камери не читає нативно, byteswap() змінює порядок байтів кожного елемента, щоб значення читалися правильно. За замовчуванням метод повертає новий масив; передаючи inplace=True, модифікує джерело на місці.
frombuffer() обробляє лише типи, що визначає сам numpy. Для периферійних пристроїв, що виробляють 32-бітні цілочисельні зразки, from_int32_buffer() та подібні функції перетворюють їх у float за один прохід.
6.3.6. Скорочення при друку¶
Друк великого масиву показує лише перші та останні кілька елементів із ... посередині, щоб термінал IDE не заповнювався тисячами значень:
>>> print(np.arange(1000, dtype=np.uint16))
array([0, 1, 2, ..., 997, 998, 999], dtype=uint16)
set_printoptions() перевизначає порогові значення, коли при налагодженні потрібен весь буфер:
np.set_printoptions(threshold=2000) # print up to 2000 elements in full
np.set_printoptions(edgeitems=10) # 10 items at each end, not 3
get_printoptions() повертає поточні налаштування у вигляді словника.