1. Il significato delle forme in NumPy
Scrivi: "So letteralmente che è un elenco di numeri e un elenco di elenchi in cui tutti gli elenchi contengono solo un numero", ma è un modo un po 'inutile di pensarci.
Il modo migliore di pensare agli array NumPy è che sono composti da due parti, un buffer di dati che è solo un blocco di elementi non elaborati e una vista che descrive come interpretare il buffer di dati.
Ad esempio, se creiamo un array di 12 numeri interi:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Quindi a
consiste in un buffer di dati, organizzato in questo modo:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
e una vista che descrive come interpretare i dati:
>>> a.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)
Qui la forma (12,)
indica che l'array è indicizzato da un singolo indice che va da 0 a 11. Concettualmente, se etichettiamo questo singolo indice i
, l'array a
appare così:
i= 0 1 2 3 4 5 6 7 8 9 10 11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Se rimodelliamo un array, questo non cambia il buffer di dati. Al contrario, crea una nuova vista che descrive un modo diverso di interpretare i dati. Quindi dopo:
>>> b = a.reshape((3, 4))
l'array b
ha lo stesso buffer di dati di a
, ma ora è indicizzato da due indici che vanno rispettivamente da 0 a 2 e da 0 a 3. Se etichettiamo i due indici i
e j
, l'array si b
presenta così:
i= 0 0 0 0 1 1 1 1 2 2 2 2
j= 0 1 2 3 0 1 2 3 0 1 2 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
che significa che:
>>> b[2,1]
9
Puoi vedere che il secondo indice cambia rapidamente e il primo indice cambia lentamente. Se preferisci che ciò sia il contrario, puoi specificare il order
parametro:
>>> c = a.reshape((3, 4), order='F')
che risulta in un array indicizzato in questo modo:
i= 0 1 2 0 1 2 0 1 2 0 1 2
j= 0 0 0 1 1 1 2 2 2 3 3 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
che significa che:
>>> c[2,1]
5
Ora dovrebbe essere chiaro cosa significa che un array ha una forma con una o più dimensioni di dimensione 1. Dopo:
>>> d = a.reshape((12, 1))
l'array d
è indicizzato da due indici, il primo dei quali va da 0 a 11 e il secondo indice è sempre 0:
i= 0 1 2 3 4 5 6 7 8 9 10 11
j= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
e così:
>>> d[10,0]
10
Una dimensione di lunghezza 1 è "libera" (in un certo senso), quindi non c'è nulla che ti impedisca di andare in città:
>>> e = a.reshape((1, 2, 1, 6, 1))
dando una matrice indicizzata in questo modo:
i= 0 0 0 0 0 0 0 0 0 0 0 0
j= 0 0 0 0 0 0 1 1 1 1 1 1
k= 0 0 0 0 0 0 0 0 0 0 0 0
l= 0 1 2 3 4 5 0 1 2 3 4 5
m= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
e così:
>>> e[0,1,0,0,0]
6
Consultare la documentazione interna di NumPy per maggiori dettagli su come vengono implementate le matrici.
2. Cosa fare?
Dal momento numpy.reshape
che crea solo una nuova vista, non dovresti aver paura di usarla ogni volta che è necessario. È lo strumento giusto da utilizzare quando si desidera indicizzare un array in modo diverso.
Tuttavia, in un lungo calcolo è in genere possibile organizzare in primo luogo la costruzione di matrici con la forma "giusta", minimizzando così il numero di rimodellamenti e trasposizioni. Ma senza vedere il contesto reale che ha portato alla necessità di una rimodulazione, è difficile dire cosa dovrebbe essere cambiato.
L'esempio nella tua domanda è:
numpy.dot(M[:,0], numpy.ones((1, R)))
ma questo non è realistico. Innanzitutto, questa espressione:
M[:,0].sum()
calcola il risultato in modo più semplice. Secondo, c'è davvero qualcosa di speciale nella colonna 0? Forse quello di cui hai effettivamente bisogno è:
M.sum(axis=0)