6.7. Представления и копии¶
Представление – это второе окно в тот же блок данных, что и источник. Данные не копируются; представление хранит свежий дескриптор (свои собственные shape, strides и dtype), но разделяет буфер. Представления по существу бесплатны.
Копия запрашивает у камеры новый буфер и обходит источник, заполняя его. Копии стоят как времени, так и RAM.
Большинство методов изменения формы создают представления. Большинство методов, преобразующих данные, создают копии. Знание того, что есть что, решает, не исчерпает ли горячий цикл RAM камеры.
6.7.1. Reshape¶
reshape() возвращает массив запрошенной формы. Общее число элементов должно остаться неизменным, иначе вызывается ValueError:
a = np.arange(12, dtype=np.uint8)
m = a.reshape((3, 4))
Результат – представление: m и a разделяют данные. Запись через m[0, 0] = 99 изменяет и a[0].
Присваивание нового кортежа shape – сокращение для той же операции:
a = np.arange(9)
a.shape = (3, 3)
6.7.2. Transpose¶
transpose() (или сокращение .T) обращает оси. Реализовано путём обращения шагов – данные не перемещаются:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
t = m.T # shape (3, 2), shares m's buffer
Транспонированное представление не обходит блок данных непрерывно. Чтение приведённого выше t строка за строкой посещает позиции памяти 0, 3, 1, 4, 2, 5, а не лежащий в основе порядок 0, 1, 2, 3, 4, 5, в котором байты размещены. Обычная арифметика и редукции справляются с этим нормально – они шагают по шагам – но tobytes() не может, потому что она возвращает лежащий в основе буфер напрямую без копирования. Байты, которые хранит буфер, не соответствуют порядку, который подразумевает форма представления, поэтому метод вызывает ValueError для любого непрерывного представления. Когда байты нужны в транспонированном порядке, сначала принудительно создайте свежую непрерывную копию:
bytes_out = t.copy().tobytes()
6.7.3. Flatten и flat¶
flatten() возвращает одномерную копию массива:
f = m.flatten() # new dense 1-D ndarray
Передайте order='C' (по умолчанию), чтобы сначала обойти последнюю ось, или order='F', чтобы сначала обойти первую ось:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
# m = [[0, 1, 2],
# [3, 4, 5]]
m.flatten() # array([0, 1, 2, 3, 4, 5], dtype=uint8)
m.flatten(order='F') # array([0, 3, 1, 4, 2, 5], dtype=uint8)
flat – это форма итератора. Она выдаёт каждый элемент ndarray любого ранга как скаляры, не выделяя плоскую копию:
for x in m.flat:
print(x)
Когда приложению нужно обойти каждый элемент, предпочитайте flat; когда ему нужен плотный одномерный буфер для передачи другой функции, используйте flatten().
6.7.4. Итерация¶
Итерация одномерного массива выдаёт скаляры; итерация массива более высокого ранга выдаёт (n-1)-мерные представления:
m = np.array([[0, 1, 2], [3, 4, 5]], dtype=np.uint8)
for row in m:
print(row) # array([0, 1, 2]), array([3, 4, 5])
Строки, выдаваемые при итерации матрицы, являются представлениями, поэтому их изменение изменяет источник.
6.7.5. Копии¶
copy() – это явный способ получить независимый ndarray, изменения которого не влияют на оригинал. Выделяется новый буфер, и источник обходится в него:
c = a.copy()
tobytes() возвращает bytearray, разделяющий память с блоком данных массива. Запись через bytearray изменяет массив на месте. Вызывает ValueError, если массив не плотный (срезанное представление, транспонирование, …).
tolist() возвращает содержимое как возможно вложенный список Python list. Полезно для сериализации небольших результатов; дорого для больших, потому что каждый элемент становится отдельным объектом Python.
6.7.6. Какие операции что возвращают¶
Полное правило:
Следующие операции возвращают представления:
срезы –
a[1:5],a[::2],m[:, 0];индексация по одной оси массива более высокого ранга –
m[0];итерация n-мерного массива;
reshape(), когда запрошенное размещение совместимо;transpose()/.T;asarray(), когда dtype совпадает.
Следующие операции возвращают копии:
булева индексация –
a[mask];арифметика –
a + b,a * 2,np.sin(a);array()– всегда копирует, даже из другого массива;
Прибегайте к явной копии только тогда, когда действительно нужен независимый буфер. На камере с ограниченной RAM разница между представлением и копией часто оказывается разницей между кодом, который умещается, и кодом, который нет.