6.5. Muoto ja askelvälit

ndarray-taulukon sisällä oleva data on yksi pakattu lukulohko. Lohkon edessä oleva kuvaaja päättää, miten tuo litteä lohko luetaan tensorina.

6.5.1. Mitä kuvaaja tallentaa

Viisi arvoa kuvaavat, miten datalohko luetaan tensorina:

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

Apufunktio ndinfo() tulostaa ne kaikki sekä taustalla olevan puskurin sijainnin yhdellä kutsulla. Kaksi taulukkoa, joiden puskurisijainnit täsmäävät, jakavat muistia:

np.ndinfo(a)
# class: ndarray
# shape: (2, 3)
# strides: (3, 1)
# itemsize: 1
# data pointer: 0x...
# type: uint8

6.5.2. Askelvälit selitettynä

Askelväli on se, montako tavua datalohkossa täytyy edetä siirtyäkseen yhden alkion verran tiettyä akselia pitkin. Yllä olevalle 2x3 uint8-taulukolle askelvälit ovat (3, 1): yhden rivin verran alaspäin siirtyminen hyppää 3 tavua, yhden sarakkeen verran oikealle siirtyminen hyppää 1 tavun. Tämä tarkoittaa samaa kuin sanoa, että rivit on tallennettu peräkkäin, vasemmalta oikealle:

memory: [ 1 ][ 2 ][ 3 ][ 4 ][ 5 ][ 6 ]
          ^ row 0          ^ row 1
          <------- 3 bytes ---->

Lukeakseen a[i, j] numpy laskee i * strides[0] + j * strides[1] datalohkon alusta ja lukee itemsize tavua siitä. Sama kaava laajenee mihin tahansa ulottuvuuksien määrään.

Tätä asettelua – rivit tallennettuna peräkkäin, viimeisen akselin vaihdellessa nopeimmin muistissa – kutsutaan riviensisäiseksi (row-major) järjestykseksi. Jokainen taulukko, jonka numpy varaa kamerassa, käyttää tätä asettelua.

6.5.3. Riviensisäisellä järjestyksellä on seurauksia

Kaksi asiaa seuraa ”rivit tallennettu peräkkäin” -periaatteesta, joilla on merkitystä puskuria muotoiltaessa kamerassa.

Viimeinen akseli on yhtenäinen. Siirtyminen kohdasta a[0, 0] kohtaan a[0, 1] koskettaa seuraavaa tavua. Siirtyminen kohdasta a[0, 0] kohtaan a[1, 0] hyppää kokonaisen rivin yli.

Viimeinen akseli on nopea akseli koko taulukkoa käsittelevälle laskennalle. numpy kamerassa käy aina viimeisen akselin sisimpänä riippumatta siitä, mikä akseli sattuu olemaan pidempi. Pöytäkoneen numpy-kirjasto järjestää silmukkansa hiljaisesti uudelleen sijoittaakseen pisimmän akselin sisimmäksi; kamera ei tee niin, joten asetteluvalinta, jonka pöytäkoneen numpy olisi peittänyt, maksaa täällä silti aikaa. np.sum(m, axis=1) romahduttaa viimeisen akselin ja kulkee yhtenäisessä suunnassa; np.sum(m, axis=0) ei. Kun sovellus voi valita, miten puskuri asetellaan, sijoita pitkä akseli viimeiseksi, jotta sitä pitkin tehtävät operaatiot pysyvät sisimmässä silmukassa.

Jos asettelu on alun perin väärin, transpose() (tai .T-oikotie) korjaa sen kopioimatta dataa – se vain vaihtaa askelvälit:

a = b.T            # now iterates fast

Sivulla Suorituskyky on täysi suorituskykykeskustelu.

6.5.4. Reshape, transpose, viipalointi – kuvaajan muokkauksia

Mikä tahansa operaatio, joka vain kirjoittaa kuvaajan uudelleen, on ilmainen. reshape vaihtaa uuden shape:n ja strides:n saman datalohkon yli. transpose kääntää askelvälit. a[::2] kaksinkertaistaa askelvälin. Kukin palauttaa näkymän samaan taustalla olevaan puskuriin.

Mikä tahansa, joka joutuu käymään datan läpi ja kirjoittamaan uuden puskurin, on kopio. Sääntö toistaiseksi on, että kuvaajan muokkaukset ovat ilmaisia ja datan läpikäynnit eivät.

6.5.5. Huomautus ndim:istä

numpy kamerassa on rakennettu enimmäistuetulla ndim-arvolla 4. Operaatiot, jotka tuottaisivat korkeamman asteen taulukon, nostavat ValueError-poikkeuksen. Valtaosa kameran puolen työstä on 1-ulotteista tai 2-ulotteista, joten raja on harvoin ongelma.