6.7. Представлення та копії

Представлення – це друге вікно у той самий блок даних, що й джерело. Дані не копіюються; представлення містить новий дескриптор (власну форму, кроки та 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() повертає 1-вимірну копію масиву:

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; коли потрібен щільний 1-вимірний буфер для передачі іншій функції, використовуйте flatten().

6.7.4. Ітерація

Ітерація 1-вимірного масиву дає скаляри; ітерація масиву вищого рангу дає (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;

  • frombuffer();

  • asarray(), коли dtype збігається.

Наступні операції повертають копії:

  • copy();

  • flatten();

  • булеве індексування – a[mask];

  • арифметика – a + b, a * 2, np.sin(a);

  • array() – завжди копіює, навіть з іншого масиву;

  • concatenate().

Вдавайтеся до явної копії лише тоді, коли незалежний буфер дійсно необхідний. На камері з обмеженою RAM різниця між представленням і копією часто є різницею між кодом, що вміщується, і кодом, що не вміщується.