6.2. Масив ndarray¶
ndarray — це тип, що зберігає числові дані у numpy. Це дві речі в одному: єдиний щільно упакований блок даних і невеликий дескриптор перед цим блоком, що визначає, як його читати.
6.2.1. Всередині¶
Блок даних зберігає всі елементи масиву підряд, без жодних додаткових даних між ними. Кожен елемент займає однакову кількість байтів — один для масиву значень uint8, два для uint16, чотири для float. 256-елементний масив uint8 займає рівно 256 байтів; ті самі 256 чисел у Python list займають кілобайт — один 32-бітний слот на елемент, незалежно від того, скільки бітів насправді потрібне значенню.
Дескриптор фіксує, що означає блок. П’яти значень достатньо для опису будь-якого прямокутного масиву, незалежно від кількості вимірів:
dtype— тип елемента. Визначає, скільки байтів займає кожен елемент і який діапазон значень він може зберігати (розглядається на Типи даних (dtype)).itemsize— ширина одного елемента в байтах, похідна від dtype.ndim— кількість вимірів (1 для вектора, 2 для матриці, 3 для об’єму, до 4).shape— розмір вздовж кожного виміру у вигляді кортежу.strides— як проходити блок даних, щоб обійти кожну вісь. Розглядається на Форма та кроки.
Ось і все. Кожен швидкий шлях у numpy — арифметика, редукції, трансляція, нарізка — працює безпосередньо на основі цих п’яти значень плюс вказівника на дані, без накладних витрат Python на кожен елемент.
6.2.2. Що дає ця конструкція¶
Три властивості випливають з конструкції «щільний блок + невеликий дескриптор» і визначають поведінку решти розділу.
Поелементна математика виконується одним викликом. a + b між двома масивами однакової форми додає два буфери і записує третій — все в одному виклику бібліотеки. np.sin(a) робить те саме для синуса кожного елемента. Арифметичні, порівняльні та побітові оператори — всі працюють так само.
Інший погляд на ті самі дані — безкоштовний. Запит підобласті масиву або тих самих даних, структурованих за іншою формою, не переміщує жодного байта. Операція повертає новий дескриптор, що вказує на той самий блок даних. Результат називається видом — другим вікном в той самий базовий буфер. Запис через вид записує в джерело.
Різні форми все одно працюють. Коротший масив проти довшого, рядок проти матриці, стовпець проти рядка — numpy вирівнює їх через трансляцію — невеликий набір правил, що визначає, яка коротка вісь розтягується до відповідності довгій. Розтягнення — віртуальне; дані не дублюються.
6.2.3. Що коштує ця конструкція¶
Два обмеження випливають з тієї самої конструкції.
Кожен елемент має однаковий тип. Список може містити int поруч із str поруч зі списком ще трьох значень int; ndarray — ні. Тип фіксується під час конструювання. На сторінці Типи даних (dtype) розглядається невеликий набір типів, що підтримує numpy, і правила, що виникають із фіксації одного типу.
Збільшення масиву — не безкоштовне. Список зберігає вільні слоти в кінці й підтримує дешевий .append. ndarray має рівно той розмір, що йому потрібен; додавання означало б виділення нового, більшого буфера та копіювання старого вмісту. Метода append() немає, і це навмисно. Правильний підхід на камері — попередньо виділити місце призначення кінцевого розміру і заповнювати його; техніка розглядається на Продуктивність.
Маючи щільний типізований буфер для даних, невеликий дескриптор для метаданих і три поведінкові гарантії (швидка поелементна математика, копії-без-копіювання тих самих даних під іншим виглядом і форми, що транслюються), ndarray є фундаментом, на якому будується весь решта розділу. Наступне практичне питання — як масив насправді з’являється: з літерала, з попередньо заповненого виділення, з буфера периферійного пристрою.