6.3. Создание массивов¶
Каждый пример на остальных этих страницах начинается с уже имеющегося в наличии ndarray. Эта страница – каталог того, как этот массив появляется на свет. Существует четыре семейства конструкторов:
Из итерируемого объекта Python – обычная форма литерала / списка / кортежа.
Предзаполненный заданной формой – нули, единицы, постоянное значение, единичная матрица.
Сгенерированный как последовательность – значения в диапазоне или равномерно распределённые.
Оборачивание буфера, уже находящегося в 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()– заполненный нулями.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(), но всегда возвращающие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.0logspace()– геометрически распределённые точки.startиstopявляются экспонентами, а не конечными точками; результат идёт отbase ** startдоbase ** stop:np.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и столбце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)затем вычисляет функцию в каждой ячейке сетки одним выражением. Карта расстояния от центра по кадру(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)
Все входы должны иметь одинаковый dtype и 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() обрабатывает только dtype, которые определяет сам 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() считывает текущие настройки обратно в виде словаря.