6.5. Oblik i koraci

Podaci unutar ndarray jedan su zapakirani blok brojeva. Deskriptor ispred tog bloka odlučuje kako se taj plosnati blok čita kao tenzor.

6.5.1. Što deskriptor bilježi

Pet vrijednosti opisuje kako čitati blok podataka kao tenzor:

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

Pomoćnik ndinfo() ispisuje sve njih plus lokaciju temeljnog međuspremnika u jednom pozivu. Dva polja čije se lokacije međuspremnika podudaraju dijele memoriju:

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

6.5.2. Koraci objašnjeni

Korak je broj bajtova koje treba prekoračiti u bloku podataka da se pomakne jedan element duž zadane osi. Za 2x3 uint8 polje gore, koraci su (3, 1): pomak prema dolje za jedan redak preskače 3 bajta, pomak udesno za jedan stupac preskače 1 bajt. To je isto kao reći da su retci pohranjeni jedan za drugim, slijeva nadesno:

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

Da pročita a[i, j], numpy računa i * strides[0] + j * strides[1] od početka bloka podataka i čita itemsize bajtova odande. Ista formula proširuje se na bilo koji broj dimenzija.

Ovaj raspored – retci pohranjeni od kraja do kraja, s posljednjom osi koja se najbrže mijenja duž memorije – naziva se redak-glavni (row-major) redoslijed. Svako polje koje numpy alocira na kameri koristi ovaj raspored.

6.5.3. Redak-glavni redoslijed ima posljedice

Iz „retci pohranjeni jedan za drugim” proizlaze dvije stvari koje su važne pri oblikovanju međuspremnika na kameri.

Posljednja os je kontinuirana. Hod od a[0, 0] do a[0, 1] dodiruje sljedeći bajt. Hod od a[0, 0] do a[1, 0] preskače cijeli redak.

Posljednja os je brza os za matematiku nad cijelim poljem. numpy na kameri uvijek hoda posljednjom osi kao najunutarnjijom, bez obzira na to koja je os duža. Desktopska biblioteka numpy tiho preuređuje svoje petlje da najdužu os postavi kao najunutarnjiju; kamera to ne čini, pa izbor rasporeda koji bi desktopski numpy zamaskirao ovdje i dalje košta vremena. np.sum(m, axis=1) sažima posljednju os i izvodi se u kontinuiranom smjeru; np.sum(m, axis=0) ne čini to. Kada aplikacija ima izbor kako rasporediti međuspremnik, postavite dugu os zadnju kako bi operacije duž nje ostale u unutarnjoj petlji.

Ako raspored krene pogrešno, transpose() (ili prečac .T) to popravlja bez kopiranja podataka – samo zamijeni korake:

a = b.T            # now iterates fast

Performanse ima cjelovitu raspravu o performansama.

6.5.4. Reshape, transpose, rezanje – uređivanja deskriptora

Bilo koja operacija koja samo prepisuje deskriptor je besplatna. reshape zamjenjuje novi shape i strides preko istog bloka podataka. transpose obrće korake. a[::2] udvostručuje korak. Svaka vraća pogled na isti temeljni međuspremnik.

Sve što mora proći kroz podatke i upisati novi međuspremnik je kopija. Pravilo je za sada da su uređivanja deskriptora besplatna, a prolasci kroz podatke nisu.

6.5.5. Napomena o ndim

numpy na kameri izgrađen je s maksimalnim podržanim ndim od 4. Operacije koje bi proizvele polje višeg ranga izazivaju ValueError. Velika većina rada na strani kamere je 1-D ili 2-D, pa ograničenje rijetko predstavlja problem.