Forse questo esempio con 12 diversi valori di array aiuterà:
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
...
I C order
valori sono nell'ordine in cui sono stati generati. Quelli trasposti non lo sono
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])
Puoi ottenere 1d visualizzazioni di entrambi
In [214]: x1=x.T
In [217]: x.shape=(12,)
anche la forma x
può essere modificata.
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
Ma la forma della trasposizione non può essere modificata. Il data
è ancora in 0,1,2,3,4...
ordine, che non può essere letta accessibile come 0,4,8...
in un array 1D.
Ma una copia di x1
può essere modificata:
In [227]: x2=x1.copy()
In [228]: x2.flags
Out[228]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
OWNDATA : True
...
In [229]: x2.shape=(12,)
Anche guardare strides
potrebbe aiutare. Un passo è quanto lontano (in byte) deve fare un passo per arrivare al valore successivo. Per un array 2d, ci saranno 2 valori di stride:
In [233]: x=np.arange(12).reshape(3,4).copy()
In [234]: x.strides
Out[234]: (16, 4)
Per arrivare alla riga successiva, passo 16 byte, colonna successiva solo 4.
In [235]: x1.strides
Out[235]: (4, 16)
Transpose cambia semplicemente l'ordine dei passi. La riga successiva è di soli 4 byte, ovvero il numero successivo.
In [236]: x.shape=(12,)
In [237]: x.strides
Out[237]: (4,)
Cambiare la forma cambia anche i passi: basta scorrere il buffer di 4 byte alla volta.
In [238]: x2=x1.copy()
In [239]: x2.strides
Out[239]: (12, 4)
Anche se x2
sembra proprio come x1
, ha un proprio buffer di dati, con i valori in un ordine diverso. La colonna successiva è ora di 4 byte, mentre la riga successiva è 12 (3 * 4).
In [240]: x2.shape=(12,)
In [241]: x2.strides
Out[241]: (4,)
E come con x
, cambiare la forma in 1d riduce i passi a (4,)
.
Poiché x1
, con i dati 0,1,2,...
nell'ordine, non c'è un passo 1d che darebbe 0,4,8...
.
__array_interface__
è un altro modo utile per visualizzare le informazioni sull'array:
In [242]: x1.__array_interface__
Out[242]:
{'strides': (4, 16),
'typestr': '<i4',
'shape': (4, 3),
'version': 3,
'data': (163336056, False),
'descr': [('', '<i4')]}
L' x1
indirizzo del buffer dei dati sarà lo stesso di x
, con cui condivide i dati. x2
ha un indirizzo di buffer diverso.
Puoi anche provare ad aggiungere un order='F'
parametro ai comandi copy
e reshape
.