6.5. Alak és lépésközök¶
Az ndarray belsejében lévő adatok egyetlen tömörített számtömböt alkotnak. Az e blokk előtt álló leíró dönti el, hogyan olvasandó ki ez a lapos blokk tenzorként.
6.5.1. Mit rögzít a leíró¶
Öt érték írja le, hogyan kell az adatblokkot tenzorként olvasni:
a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.uint8)
a.ndim # 2 - number of dimensions
a.shape # (2, 3)- length along each dimension
a.itemsize # 1 - bytes per element (from dtype)
a.size # 6 - total number of elements
a.strides # (3, 1)- step pattern through the buffer
A ndinfo() segédfüggvény egyetlen hívással kiírja mindegyiket, valamint az alapul szolgáló puffer helyét. Két tömb, amelynek a pufferhelye megegyezik, megosztja a memóriát:
np.ndinfo(a)
# class: ndarray
# shape: (2, 3)
# strides: (3, 1)
# itemsize: 1
# data pointer: 0x...
# type: uint8
6.5.2. A lépésközök magyarázata¶
Egy lépésköz azt mondja meg, hány bájtot kell lépni az adatblokkban ahhoz, hogy egy adott tengely mentén egy elemmel arrébb lépjünk. A fenti 2x3-as uint8 tömbnél a lépésközök (3, 1): egy sorral lejjebb lépve 3 bájtot ugrunk, egy oszloppal jobbra lépve 1 bájtot. Ez ugyanazt jelenti, mintha azt mondanánk, hogy a sorok közvetlenül egymás után, balról jobbra tárolódnak:
memory: [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ]
^ row 0 ^ row 1
<------- 3 bytes ---->
Az a[i, j] kiolvasásához a numpy kiszámítja az i * strides[0] + j * strides[1] értéket az adatblokk elejétől, és onnan itemsize bájtot olvas. Ugyanez a képlet tetszőleges számú dimenzióra kiterjeszthető.
Ezt az elrendezést – ahol a sorok egymás után tárolódnak, és az utolsó tengely változik leggyorsabban a memóriában – sorfolytonos (row-major) sorrendnek nevezzük. Minden tömb, amelyet a numpy a kamerán foglal, ezt az elrendezést használja.
6.5.3. A sorfolytonosságnak következményei vannak¶
A „sorok egymás után tárolva” elvből két dolog következik, ami számít, amikor egy puffert alakítasz a kamerán.
Az utolsó tengely összefüggő. Az a[0, 0]-ból az a[0, 1]-be lépve a következő bájtot érintjük. Az a[0, 0]-ból az a[1, 0]-ba lépve egy egész soron ugrunk át.
Az utolsó tengely a gyors tengely a teljes tömbös matematikához. A numpy a kamerán mindig az utolsó tengelyt járja be legbelül, függetlenül attól, melyik tengely a hosszabb. Az asztali numpy könyvtár csendben átrendezi a ciklusait, hogy a leghosszabb tengelyt tegye legbelülre; a kamera ezt nem teszi, így egy olyan elrendezésbeli döntés, amelyet az asztali numpy elsimított volna, itt mégis időbe kerül. Az np.sum(m, axis=1) az utolsó tengelyt vonja össze, és az összefüggő irányban fut; az np.sum(m, axis=0) nem. Ha az alkalmazás szabadon dönthet egy puffer elrendezéséről, tedd a hosszú tengelyt utolsónak, hogy a mentén végzett műveletek a belső ciklusban maradjanak.
Ha az elrendezés eleve hibás, a transpose() (vagy a .T rövidítés) az adatok másolása nélkül javítja – pusztán felcseréli a lépésközöket:
a = b.T # now iterates fast
A Teljesítmény oldalon található a teljes teljesítményről szóló tárgyalás.
6.5.4. Reshape, transzponálás, szeletelés – leíró-szerkesztések¶
Minden olyan művelet ingyenes, amely csak a leírót írja át. A reshape egy új shape és strides párost cserél fel ugyanazon adatblokk fölött. A transpose megfordítja a lépésközöket. Az a[::2] megduplázza egy lépésközt. Mindegyik ugyanazon alapul szolgáló puffer egy nézetét adja vissza.
Bármi, aminek be kell járnia az adatokat, és egy új puffert kell írnia, az másolat. Egyelőre a szabály az, hogy a leíró-szerkesztések ingyenesek, az adatbejárások nem.
6.5.5. Megjegyzés az ndim-ről¶
A numpy a kamerán legfeljebb 4-es ndim támogatásával van megépítve. Az olyan műveletek, amelyek magasabb rangú tömböt eredményeznének, ValueError kivételt váltanak ki. A kameraoldali munka túlnyomó többsége 1 vagy 2 dimenziós, így ez a korlát ritkán jelent problémát.