ndarray
O ndarray
foi projetado para acesso otimizado a uma grande quantidade de dados. Neste sentido, os conceitos
descritos a seguir sobre as três formas de cópias entre variáveis ditas sem cópia, cópia rasa (shallow) e
cópia profunda (deep) são fundamentais para uma codificação eficiente. Podemos dizer que um ndarray
possui
o cabeçalho que contém dados pelas informações sobre o tipo do elemento, a dimensionalidade (shape
) e
passo ou deslocamento para o próximo elemento (strides
) e os dados raster em si. A tabela
a seguir mostra a situação do cabeçalho e dos dados nos três tipos de cópias.
Tipo | Cabeçalho: Type, Shape, Strides | Dados raster | Exemplo |
---|---|---|---|
Sem cópia, apenas ref | apontador original | apontador original | a = b |
Cópia rasa | novo | apontador original | b = a.reshape, slicing, a.T |
Cópia profunda | novo | novo | a = b.copy() |
No caso abaixo, usaremos o comando normal de igual como atribuição do array a
para o array b
.
Verifica-se que tanto o shape como os dados de b
são os mesmos de a
. Tudo se passa como b
fosse apenas um apontador para a
. Qualquer modificação em b
é refletida em a
.
In [45]:
import numpy as np
a = np.arange(6)
b = a
print("a =\n",a)
print("b =\n",b)
b.shape = (2,3) # mudança no shape de b,
print("\na shape =",a.shape) # altera o shape de a
b[0,0] = -1 # mudança no conteúdo de b
print("a =\n",a) # altera o conteudo de a
print("\nid de a = ",id(a)) # id é um identificador único de objeto
print("id de b = ",id(b)) # a e b possuem o mesmo id
print('np.may_share_memory(a,b):',np.may_share_memory(a,b))
Observe que mesmo no retorno de uma função, a cópia explícita pode não acontecer. Veja o exemplo a seguir de uma função que apenas retorna a variável de entrada:
In [39]:
def cc(a):
return a
b = cc(a)
print("id de a = ",id(a))
print("id de b = ",id(b))
print('np.may_share_memory(a,b):',np.may_share_memory(a,b))
A cópia rasa é muito útil e extensivamente utilizada. É usada quando se quer indexar o array original através da mudança de dimensionalidade ou do refatiamento, porém sem a necessidade de realizar uma cópia dos dados raster. Desta forma consegue-se uma otimização no acesso ao array n-dimensional. Existem várias formas onde a cópia rasa acontece, sendo as principais:
1) no caso do reshape
onde o número de elementos do ndarray
é o mesmo, porém sua dimensionalidade
é alterada;
2) no caso de fatiamento onde um subarray é indexado;
3) no caso de transposição do array;
4) no caso de linearização do raster através do ravel()
.
entre outros.
O exemplo a seguir mostra inicialmente a criação de um vetor unidimensional sequencial sendo "visto" de forma bidimensional ou tridimensional.
In [40]:
a = np.arange(30)
print("a =\n", a)
print('a.shape:',a.shape)
In [41]:
b = a.reshape( (5, 6))
print("b =\n", b)
In [42]:
b[:, 0] = -1
print('b=\n',b)
print("a =\n", a)
In [43]:
c = a.reshape( (2, 3, 5) )
print("c =\n", c)
print('c.base is a:',c.base is a)
print('np.may_share_memory(a,c):',np.may_share_memory(a,c))
print('id(a),id(c):',id(a),id(c))
O exemplo a seguir mostra a cópia rasa no uso de fatiamento. No exemplo, todos os elementos de linhas e colunas pares são modificados para 1. CUIDADO: quando é feita a atribuição de b = 1., é importante que b seja referenciado como ndarray na forma b[:,:], caso contrário, se fizermos b = 1., uma nova variável é criada.
In [47]:
a = np.zeros( (5, 6))
print('a.shape:',a.shape)
b = a[::2,::2]
print('b.shape:',b.shape)
b[:,:] = 1.
print('b=\n', b)
print('a=\n', a)
print('b.base is a:',b.base is a)
print('np.may_share_memory(a,b):',np.may_share_memory(a,b))
Este outro exemplo é uma forma atraente de processar uma coluna de uma matriz bidimensional, porém é preciso CUIDADO, pois o uso de b deve ser com b[:] se for atribuído um novo valor para ele, caso contrário, se fizermos b = arange(5), uma nova variável é criada.
In [ ]:
a = np.arange(25).reshape((5,5))
print('a=\n',a)
b = a[:,0]
print('b=',b)
In [ ]:
b[:] = np.arange(5)
b[2] = 100
print('b=',b)
print('a=\n',a)
In [ ]:
a = np.arange(25).reshape((5,5))
print('a=\n',a)
b = np.arange(5)
print('b=',b)
print('a=\n',a)
In [ ]:
a = np.arange(24).reshape((4,6))
print('a:\n',a)
at = a.T
print('at:\n',at)
print('at.shape',at.shape)
print('np.may_share_memory(a,at):',np.may_share_memory(a,at))
In [ ]:
a = np.arange(24).reshape((4,6))
print('a:\n',a)
av = a.ravel()
print('av.shape:',av.shape)
print('av:\n',av)
print('np.may_share_memory(a,av):',np.may_share_memory(a,av))
In [ ]:
b = a.copy()
c = np.array(a, copy=True)
print("id de a = ",id(a))
print("id de b = ",id(b))
print("id de c = ",id(c))
print('np.may_share_memory(a,b):',np.may_share_memory(a,b))
print('np.may_share_memory(a,c):',np.may_share_memory(a,c))