Um array contíguo é apenas um array armazenado em um bloco ininterrupto de memória: para acessar o próximo valor no array, simplesmente passamos para o próximo endereço de memória.
Considere a matriz 2D arr = np.arange(12).reshape(3,4)
. Se parece com isso:
Na memória do computador, os valores de arr
são armazenados assim:
Isso significa que arr
é uma matriz contígua C porque as linhas são armazenadas como blocos contíguos de memória. O próximo endereço de memória contém o próximo valor de linha nessa linha. Se quisermos descer uma coluna, só precisamos pular três blocos (por exemplo, pular de 0 para 4 significa pular 1,2 e 3).
Transpor a matriz com arr.T
significa que a contiguidade C é perdida porque as entradas de linha adjacentes não estão mais em endereços de memória adjacentes. No entanto, o Fortranarr.T
é contíguo, pois as colunas estão em blocos contíguos de memória:
Em termos de desempenho, acessar endereços de memória que estão próximos um do outro é muito mais rápido do que acessar endereços que estão mais "espalhados" (buscar um valor da RAM pode implicar em vários endereços vizinhos sendo buscados e armazenados em cache para a CPU). significa que as operações em matrizes contíguas geralmente serão mais rápidas.
Como consequência do layout de memória contígua C, as operações em linha são geralmente mais rápidas do que em colunas. Por exemplo, você normalmente encontrará que
np.sum(arr, axis=1) # sum the rows
é ligeiramente mais rápido que:
np.sum(arr, axis=0) # sum the columns
Da mesma forma, as operações em colunas serão ligeiramente mais rápidas para matrizes contíguas em Fortran.
Finalmente, por que não podemos nivelar a matriz contígua do Fortran atribuindo uma nova forma?
>>> arr2 = arr.T
>>> arr2.shape = 12
AttributeError: incompatible shape for a non-contiguous array
Para que isso fosse possível, o NumPy teria que colocar as linhas arr.T
juntas assim:
(Definir o shape
atributo diretamente assume a ordem C - ou seja, NumPy tenta realizar a operação em linha.)
Isso é impossível de fazer. Para qualquer eixo, o NumPy precisa ter um comprimento de passo constante (o número de bytes a mover) para chegar ao próximo elemento da matriz. O achatamento arr.T
dessa maneira exigiria pular para a frente e para trás na memória para recuperar valores consecutivos do array.
Se arr2.reshape(12)
escrevêssemos em vez disso, NumPy copiaria os valores de arr2 em um novo bloco de memória (uma vez que não pode retornar uma visualização dos dados originais para esta forma).
arr2
para a forma 1D(12,)
usa a ordem C, o que significa que o eixo 1 é desenrolado antes do eixo 0 (ou seja, cada uma das quatro linhas precisa ser colocada uma ao lado da outra para criar a matriz 1D desejada). É impossível ler esta sequência de inteiros (0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11) para fora do buffer usando um comprimento de passo constante (os bytes para pular para visitar esses elementos em sequência seriam 4, 4, -7, 4, 4, -7, 4, 4, 7, 4, 4). NumPy requer um comprimento de passada constante por eixo.arr[:, ::-1]
seja uma visão do mesmo buffer de memória quearr
, NumPy não o considera ordem C ou F, pois percorreu os valores no buffer em uma ordem "não padrão" ...Talvez este exemplo com 12 valores de array diferentes ajude:
Os
C order
valores estão na ordem em que foram gerados. Os transpostos não estãoVocê pode obter 1d visualizações de ambos
a forma de
x
também pode ser alterada.Mas a forma da transposta não pode ser alterada. O
data
ainda está na0,1,2,3,4...
ordem, que não pode ser acessada como0,4,8...
em uma matriz 1d.Mas uma cópia de
x1
pode ser alterada:Olhar
strides
também pode ajudar. Um passo é a distância (em bytes) que ela deve percorrer para chegar ao próximo valor. Para uma matriz 2d, haverá 2 valores de passada:Para ir para a próxima linha, etapa 16 bytes, próxima coluna apenas 4.
Transpor apenas muda a ordem das passadas. A próxima linha tem apenas 4 bytes - ou seja, o próximo número.
Mudar a forma também muda os passos - basta percorrer o buffer 4 bytes por vez.
Mesmo
x2
parecendox1
, ele tem seu próprio buffer de dados, com os valores em uma ordem diferente. A próxima coluna agora tem 4 bytes, enquanto a próxima linha tem 12 (3 * 4).E, como acontece com
x
, alterar a forma para 1d reduz os passos para(4,)
.Pois
x1
, com os dados em0,1,2,...
ordem, não há um passo de 1d que daria0,4,8...
.__array_interface__
é outra maneira útil de exibir informações de matriz:O
x1
endereço do buffer de dados será o mesmo dex
, com o qual ele compartilha os dados.x2
tem um endereço de buffer diferente.Você também pode experimentar adicionar um
order='F'
parâmetro aos comandoscopy
ereshape
.fonte