6.7. Vederi și copii

O vedere este o a doua fereastră asupra aceluiași bloc de date ca sursa. Nu se copiază niciun fel de date; vederea deține un descriptor nou (propria sa formă, pași și dtype), dar partajează tamponul. Vederile sunt în esență gratuite.

O copie solicită de la cameră un tampon nou și parcurge sursa umplându-l. Copiile costă atât timp, cât și RAM.

Majoritatea metodelor de modificare a formei produc vederi. Majoritatea celor de transformare a datelor produc copii. Cunoașterea cui este care decide dacă o buclă intensivă rămâne fără RAM pe cameră.

6.7.1. Reshape

reshape() returnează un tablou cu forma cerută. Numărul total de elemente trebuie să rămână neschimbat, altfel se ridică ValueError

a = np.arange(12, dtype=np.uint8)
m = a.reshape((3, 4))

Rezultatul este o vedere – m și a partajează datele. Scrierea prin m[0, 0] = 99 modifică și a[0].

Atribuirea unui nou tuplu către shape este o prescurtare pentru aceeași operație:

a = np.arange(9)
a.shape = (3, 3)

6.7.2. Transpunere

transpose() (sau scurtătura .T) inversează axele. Implementată prin inversarea pașilor – nu se mută niciun fel de date:

m = np.arange(6, dtype=np.uint8).reshape((2, 3))
t = m.T                  # shape (3, 2), shares m's buffer

O vedere transpusă nu parcurge blocul de date în mod contiguu. Citirea lui t de mai sus rând cu rând vizitează pozițiile de memorie 0, 3, 1, 4, 2, 5, nu ordinea subiacentă 0, 1, 2, 3, 4, 5 în care octeții sunt dispuși. Aritmetica obișnuită și reducerile gestionează acest lucru fără probleme – parcurg pașii – dar tobytes() nu poate, deoarece returnează tamponul subiacent direct, fără copiere. Octeții pe care îi conține tamponul nu corespund ordinii pe care o implică forma vederii, așa că metoda ridică ValueError pe orice vedere necontiguă. Atunci când octeții sunt necesari în ordinea transpusă, forțează mai întâi o copie contiguă nouă:

bytes_out = t.copy().tobytes()

6.7.3. Flatten și flat

flatten() returnează o copie 1-D a tabloului:

f = m.flatten()          # new dense 1-D ndarray

Transmite order='C' (implicit) pentru a parcurge mai întâi ultima axă sau order='F' pentru a parcurge mai întâi prima axă:

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 este forma de iterator. Produce fiecare element al unui ndarray de orice rang sub formă de scalari, fără a aloca o copie plată:

for x in m.flat:
    print(x)

Atunci când aplicația trebuie să parcurgă fiecare element, preferă flat; atunci când are nevoie de un tampon 1-D dens de transmis altei funcții, folosește flatten().

6.7.4. Iterare

Iterarea unui tablou 1-D produce scalari; iterarea unui tablou de rang superior produce vederi (n-1)-D:

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])

Rândurile produse prin iterarea unei matrice sunt vederi, așa că modificarea lor modifică sursa.

6.7.5. Copii

copy() este modul explicit de a obține un ndarray independent ale cărui modificări nu afectează originalul. Se alocă un tampon nou, iar sursa este parcursă în el:

c = a.copy()

tobytes() returnează un bytearray care partajează memoria cu blocul de date al tabloului. Scrierile prin bytearray modifică tabloul in-place. Ridică ValueError dacă tabloul nu este dens (o vedere segmentată, o transpunere, …).

tolist() returnează conținutul ca un list Python posibil imbricat. Util pentru serializarea rezultatelor mici; costisitor pentru cele mari, deoarece fiecare element devine un obiect Python separat.

6.7.6. Care operații returnează ce

Regula completă:

Următoarele operații returnează vederi:

  • segmentarea – a[1:5], a[::2], m[:, 0];

  • indexarea pe o singură axă a unui tablou de rang superior – m[0];

  • iterarea unui tablou n-D;

  • reshape(), atunci când dispunerea cerută este compatibilă;

  • transpose() / .T;

  • frombuffer();

  • asarray(), atunci când dtype-ul corespunde.

Următoarele operații returnează copii:

Recurge la o copie explicită doar atunci când este nevoie cu adevărat de un tampon independent. Pe o cameră cu RAM limitat, diferența dintre o vedere și o copie este adesea diferența dintre cod care încape și cod care nu încape.