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;asarray(), quando il dtype corrisponde.
Le seguenti operazioni restituiscono copie:
indicizzazione booleana –
a[mask];aritmetica –
a + b,a * 2,np.sin(a);array()– copia sempre, anche da un altro array;
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.