6.7. Vistas e cópias¶
Uma vista é uma segunda janela sobre o mesmo bloco de dados da fonte. Nenhum dado é copiado; a vista tem um novo descritor (a sua própria forma, strides e dtype), mas partilha o buffer. As vistas são essencialmente gratuitas.
Uma cópia pede à câmara um novo buffer e percorre a fonte a preenchê-lo. As cópias custam tanto tempo como RAM.
A maioria dos métodos de transformação de forma produz vistas. A maioria dos que transformam dados produz cópias. Saber qual é qual decide se um ciclo crítico esgota a RAM da câmara.
6.7.1. Reshape¶
reshape() devolve um array com a forma pedida. O número total de elementos deve ser inalterado, caso contrário é lançado ValueError
a = np.arange(12, dtype=np.uint8)
m = a.reshape((3, 4))
O resultado é uma vista – m e a partilham dados. Escrever através de m[0, 0] = 99 altera a[0] também.
Atribuir um novo tuplo a shape é uma abreviatura para a mesma operação:
a = np.arange(9)
a.shape = (3, 3)
6.7.2. Transpose¶
transpose() (ou o atalho .T) inverte os eixos. Implementado invertendo os strides – nenhum dado é movido:
m = np.arange(6, dtype=np.uint8).reshape((2, 3))
t = m.T # shape (3, 2), shares m's buffer
Uma vista transposta não percorre o bloco de dados de forma contígua. Ler t acima linha por linha visita as posições de memória 0, 3, 1, 4, 2, 5, não a ordem 0, 1, 2, 3, 4, 5 subjacente em que os bytes estão dispostos. A aritmética comum e as reduções tratam isso corretamente – percorrem os strides – mas tobytes() não consegue, porque devolve o buffer subjacente diretamente sem copiar. Os bytes que o buffer contém não correspondem à ordem que a forma da vista implica, por isso o método lança ValueError em qualquer vista não contígua. Quando os bytes são necessários na ordem transposta, force primeiro uma cópia contígua nova:
bytes_out = t.copy().tobytes()
6.7.3. Flatten e flat¶
flatten() devolve uma cópia 1-D do array:
f = m.flatten() # new dense 1-D ndarray
Passe order='C' (predefinição) para percorrer o último eixo primeiro ou order='F' para percorrer o primeiro eixo primeiro:
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 é a forma iteradora. Produz cada elemento de um ndarray de qualquer rank como escalares, sem alocar uma cópia plana:
for x in m.flat:
print(x)
Quando a aplicação precisa de percorrer cada elemento, prefira flat; quando precisa de um buffer 1-D denso para passar a outra função, use flatten().
6.7.4. Iteração¶
Iterar um array 1-D produz escalares; iterar um array de maior rank produz vistas (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])
As linhas produzidas ao iterar uma matriz são vistas, por isso modificá-las modifica a fonte.
6.7.5. Cópias¶
copy() é a forma explícita de obter um ndarray independente cujas modificações não afetam o original. Um novo buffer é alocado e a fonte é percorrida para o preencher:
c = a.copy()
tobytes() devolve um bytearray que partilha memória com o bloco de dados do array. As escritas através do bytearray modificam o array in-place. Lança ValueError se o array não for denso (uma vista fatiada, uma transposição, …).
tolist() devolve o conteúdo como uma list Python possivelmente aninhada. Útil para serializar resultados pequenos; dispendioso para os grandes, pois cada elemento torna-se um objeto Python separado.
6.7.6. Quais as operações que devolvem quê¶
A regra completa:
As seguintes operações devolvem vistas:
fatiamento –
a[1:5],a[::2],m[:, 0];indexação de eixo único de um array de maior rank –
m[0];iteração de um array n-D;
reshape(), quando a disposição pedida é compatível;transpose()/.T;asarray(), quando o dtype corresponde.
As seguintes operações devolvem cópias:
indexação booleana –
a[mask];aritmética –
a + b,a * 2,np.sin(a);array()– copia sempre, mesmo de outro array;
Recorra a uma cópia explícita apenas quando for genuinamente necessário um buffer independente. Numa câmara com RAM limitada, a diferença entre uma vista e uma cópia é frequentemente a diferença entre código que cabe e código que não cabe.