6.7. Viste e copie#

Una vista e una seconda finestra sullo stesso blocco di dati della sorgente. Nessun dato viene copiato; la vista contiene un descrittore nuovo (con la propria forma, i propri strides e il proprio dtype) ma condivide il buffer. Le viste sono praticamente gratuite.

Una copia chiede alla camera un nuovo buffer e scorre la sorgente riempiendolo. Le copie costano sia tempo sia RAM.

La maggior parte dei metodi che modificano la forma produce viste. La maggior parte di quelli che trasformano i dati produce copie. Sapere quale e quale decide se un ciclo critico fa esaurire la RAM alla camera.

6.7.1. Reshape#

reshape() restituisce un array della forma richiesta. Il numero totale di elementi deve rimanere invariato, altrimenti viene sollevato ValueError

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

Il risultato e una vista – m e a condividono i dati. Scrivere attraverso m[0, 0] = 99 cambia anche a[0].

Assegnare una nuova tupla a shape e una forma abbreviata della stessa operazione:

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

6.7.2. Transpose#

transpose() (o la scorciatoia .T) inverte gli assi. Implementata invertendo gli strides – nessun dato viene spostato:

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

Una vista trasposta non scorre il blocco di dati in modo contiguo. Leggere t sopra riga per riga visita le posizioni di memoria 0, 3, 1, 4, 2, 5, non l’ordine sottostante 0, 1, 2, 3, 4, 5 in cui i byte sono disposti. L’aritmetica e le riduzioni ordinarie lo gestiscono senza problemi – scorrono attraverso gli strides – ma tobytes() non puo, perche restituisce direttamente il buffer sottostante senza copiare. I byte contenuti nel buffer non corrispondono all’ordine implicato dalla forma della vista, quindi il metodo solleva ValueError su qualsiasi vista non contigua. Quando i byte sono necessari nell’ordine trasposto, forza prima una nuova copia contigua:

bytes_out = t.copy().tobytes()

6.7.3. Flatten e flat#

flatten() restituisce una copia 1-D dell’array:

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

Passa order='C' (predefinito) per scorrere prima l’ultimo asse oppure order='F' per scorrere prima il primo asse:

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 e la forma iteratore. Restituisce ogni elemento di un ndarray di rango qualsiasi come scalari, senza allocare una copia piatta:

for x in m.flat:
    print(x)

Quando l’applicazione deve scorrere ogni elemento, preferisci flat; quando ha bisogno di un buffer 1-D denso da passare a un’altra funzione, usa flatten().

6.7.4. Iterazione#

Iterare un array 1-D restituisce scalari; iterare un array di rango superiore restituisce viste (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])

Le righe restituite iterando una matrice sono viste, quindi modificarle modifica la sorgente.

6.7.5. Copie#

copy() e il modo esplicito per ottenere un ndarray indipendente le cui modifiche non influiscono sull’originale. Viene allocato un nuovo buffer e la sorgente vi viene scorsa dentro:

c = a.copy()

tobytes() restituisce un bytearray che condivide la memoria con il blocco di dati dell’array. Le scritture attraverso il bytearray modificano l’array in-place. Solleva ValueError se l’array non e denso (una vista sliced, una trasposizione, …).

tolist() restituisce il contenuto come una list Python eventualmente annidata. Utile per serializzare piccoli risultati; costoso per quelli grandi, perche ogni elemento diventa un oggetto Python separato.

6.7.6. Quali operazioni restituiscono cosa#

La regola completa:

Le seguenti operazioni restituiscono viste:

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

  • indicizzazione su un singolo asse di un array di rango superiore – m[0];

  • iterazione di un array n-D;

  • reshape(), quando la disposizione richiesta e compatibile;

  • transpose() / .T;

  • frombuffer();

  • asarray(), quando il dtype corrisponde.

Le seguenti operazioni restituiscono copie:

Ricorri a una copia esplicita solo quando serve davvero un buffer indipendente. Su una camera con RAM limitata, la differenza tra una vista e una copia e spesso la differenza tra codice che entra in memoria e codice che non ci entra.