Talvez este exemplo com 12 valores de array diferentes ajude:
In [207]: x=np.arange(12).reshape(3,4).copy()
In [208]: x.flags
Out[208]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
...
In [209]: x.T.flags
Out[209]:
C_CONTIGUOUS : False
F_CONTIGUOUS : True
OWNDATA : False
...
Os C order
valores estão na ordem em que foram gerados. Os transpostos não estão
In [212]: x.reshape(12,) # same as x.ravel()
Out[212]: array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
In [213]: x.T.reshape(12,)
Out[213]: array([ 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11])
Você pode obter 1d visualizações de ambos
In [214]: x1=x.T
In [217]: x.shape=(12,)
a forma de x
também pode ser alterada.
In [220]: x1.shape=(12,)
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-220-cf2b1a308253> in <module>()
----> 1 x1.shape=(12,)
AttributeError: incompatible shape for a non-contiguous array
Mas a forma da transposta não pode ser alterada. O data
ainda está na 0,1,2,3,4...
ordem, que não pode ser acessada como 0,4,8...
em uma matriz 1d.
Mas uma cópia de x1
pode ser alterada:
In [227]: x2=x1.copy()
In [228]: x2.flags
Out[228]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
...
In [229]: x2.shape=(12,)
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:
In [233]: x=np.arange(12).reshape(3,4).copy()
In [234]: x.strides
Out[234]: (16, 4)
Para ir para a próxima linha, etapa 16 bytes, próxima coluna apenas 4.
In [235]: x1.strides
Out[235]: (4, 16)
Transpor apenas muda a ordem das passadas. A próxima linha tem apenas 4 bytes - ou seja, o próximo número.
In [236]: x.shape=(12,)
In [237]: x.strides
Out[237]: (4,)
Mudar a forma também muda os passos - basta percorrer o buffer 4 bytes por vez.
In [238]: x2=x1.copy()
In [239]: x2.strides
Out[239]: (12, 4)
Mesmo x2
parecendo x1
, 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).
In [240]: x2.shape=(12,)
In [241]: x2.strides
Out[241]: (4,)
E, como acontece com x
, alterar a forma para 1d reduz os passos para (4,)
.
Pois x1
, com os dados em 0,1,2,...
ordem, não há um passo de 1d que daria 0,4,8...
.
__array_interface__
é outra maneira útil de exibir informações de matriz:
In [242]: x1.__array_interface__
Out[242]:
{'strides': (4, 16),
'typestr': '<i4',
'shape': (4, 3),
'version': 3,
'data': (163336056, False),
'descr': [('', '<i4')]}
O x1
endereço do buffer de dados será o mesmo de x
, 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 comandos copy
e reshape
.