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.