Differenza tra forma numpy.array (R, 1) e (R,)


320

In numpy, alcune delle operazioni tornano in forma (R, 1)ma alcune ritornano (R,). Ciò renderà la moltiplicazione della matrice più noiosa poiché reshapeè necessario esplicito . Ad esempio, data una matrice M, se vogliamo fare numpy.dot(M[:,0], numpy.ones((1, R)))dov'è Ril numero di righe (ovviamente, lo stesso problema si verifica anche per quanto riguarda le colonne). Otterremo un matrices are not alignederrore poiché M[:,0]è in forma (R,)ma numpy.ones((1, R))è in forma (1, R).

Quindi le mie domande sono:

  1. Qual è la differenza tra forma (R, 1)e (R,). So letteralmente che è un elenco di numeri e un elenco di elenchi in cui tutti gli elenchi contengono solo un numero. Mi chiedo solo perché non progettare in numpymodo da favorire la forma (R, 1)anziché (R,)per una più semplice moltiplicazione della matrice.

  2. Ci sono modi migliori per l'esempio sopra? Senza rimodellare esplicitamente in questo modo:numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))


3
Questo potrebbe aiutare. Non con la ricerca di una soluzione pratica però.
keyser,

1
Soluzione corretta: numpy.ravel (M [:, 0]) - converte la forma da (R, 1) a (R,)
Andi R

Risposte:


545

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 aconsiste 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 aappare 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 bha 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 ie j, l'array si bpresenta 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 orderparametro:

>>> 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.reshapeche 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)

34
Ciò è stato estremamente utile nel pensare a come vengono archiviate le matrici. Grazie! L'accesso a una colonna (o riga) di una matrice (2-d) per un ulteriore calcolo della matrice è scomodo, dato che devo sempre rimodellare la colonna in modo appropriato. Ogni volta che devo cambiare la forma da (n,) a (n, 1).
OfLettersAndNumbers

3
@SammyLee: utilizzare newaxisse è necessario un altro asse, ad esempio a[:, j, np.newaxis]è la jcolonna di a, ed a[np.newaxis, i]è la iriga di th.
Gareth Rees,

sto cercando di tracciare gli indici per capire meglio sulla carta con questo modello e non sembra averlo capito, se avessi una forma 2 x 2 x 4 capisco che i primi 2 possono essere compresi come 0000000011111111 e gli ultimi 4 possono essere inteso come 0123012301230123 cosa succede a quello centrale?
PirateApp,

3
Un modo semplice per pensare a questo è che numpy funziona esattamente come previsto qui, ma la stampa di tuple di Python può essere fuorviante. Nel (R, )caso, la forma di ndarrayè una tupla con un singolo elemento, quindi viene stampata da Python con una virgola finale. Senza la virgola aggiuntiva, sarebbe ambiguo con un'espressione tra parentesi . A ndarraycon una singola dimensione può essere considerato come un vettore di colonna di lunghezza R. Nel (R, 1)caso, la tupla ha due elementi, quindi può essere considerata come un vettore di riga (o una matrice con 1 riga di lunghezza R.
Michael Yang

1
@ Alex-droidAD: vedi questa domanda e le sue risposte.
Gareth Rees,

16

La differenza tra (R,)e (1,R)è letteralmente il numero di indici che è necessario utilizzare. ones((1,R))è un array 2-D che sembra avere solo una riga. ones(R)è un vettore. Generalmente se non ha senso che la variabile abbia più di una riga / colonna, dovresti usare un vettore, non una matrice con una dimensione singleton.

Per il tuo caso specifico, ci sono un paio di opzioni:

1) Basta trasformare il secondo argomento in un vettore. Di seguito funziona bene:

    np.dot(M[:,0], np.ones(R))

2) Se si desidera matlab come operazioni a matrice, utilizzare la classe matrixanziché ndarray. Tutte le matrici sono costrette a essere matrici 2D e l'operatore *esegue la moltiplicazione di matrici invece di elementi (quindi non è necessario il punto). Nella mia esperienza, questo è più un problema che ne vale la pena, ma può essere bello se sei abituato a matlab.


Sì. Mi aspettavo un comportamento più simile a un matlab. Dò un'occhiata alla matrixlezione. Qual è il problema per la matrixclasse BTW?
clwen,

2
Il problema matrixè che è solo 2D, e anche perché sovraccaricando l'operatore '*', le funzioni scritte per ndarraypotrebbero non funzionare se utilizzate su a matrix.
Evan,

11

La forma è una tupla. Se esiste solo 1 dimensione, la forma sarà un numero e sarà vuota dopo una virgola. Per 2+ dimensioni, ci sarà un numero dopo tutte le virgole.

# 1 dimension with 2 elements, shape = (2,). 
# Note there's nothing after the comma.
z=np.array([  # start dimension
    10,       # not a dimension
    20        # not a dimension
])            # end dimension
print(z.shape)

(2)

# 2 dimensions, each with 1 element, shape = (2,1)
w=np.array([  # start outer dimension 
    [10],     # element is in an inner dimension
    [20]      # element is in an inner dimension
])            # end outer dimension
print(w.shape)

(2,1)


5

Per la sua classe di array di base, gli array 2d non sono più speciali di quelli 1d o 3d. Ci sono alcune operazioni per preservare le dimensioni, alcune che le riducono, altre che le combinano o addirittura le espandono.

M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) selects one column, returns a 1d array
M[0,:].shape # same, one row, 1d array
M[:,[0]].shape # (3,1), index with a list (or array), returns 2d
M[:,[0,1]].shape # (3,2)

In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))

Out[20]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

Altre espressioni che danno lo stesso array

np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])

MATLAB è iniziato con solo array 2D. Le versioni più recenti consentono più dimensioni, ma mantengono il limite inferiore di 2. Ma devi ancora prestare attenzione alla differenza tra una matrice di righe e una colonna, una con forma (1,3)v (3,1). Quanto spesso hai scritto [1,2,3].'? Stavo per scrivere row vectore column vector, ma con quel vincolo 2d, non ci sono vettori in MATLAB - almeno non nel senso matematico del vettore come 1d.

Hai np.atleast_2dmai visto (anche le versioni _1d e _3d)?


1

1) Il motivo per non preferire una forma di (R, 1)over (R,)è che complica inutilmente le cose. Inoltre, perché sarebbe preferibile avere la forma (R, 1)di default per un vettore lunghezza-R anziché (1, R)? È meglio mantenerlo semplice ed essere esplicito quando si richiedono dimensioni aggiuntive.

2) Per il tuo esempio, stai calcolando un prodotto esterno in modo da poterlo fare senza una reshapechiamata usando np.outer:

np.outer(M[:,0], numpy.ones((1, R)))

Grazie per la risposta. 1) M[:,0]sta essenzialmente ottenendo tutte le righe con il primo elemento, quindi ha più senso avere (R, 1)di (1, R). 2) Non è sempre sostituibile np.outer, ad esempio, punto per matrice in forma (1, R) quindi (R, 1).
clwen,

1) Sì, quella potrebbe essere la convenzione ma ciò la rende meno conveniente in altre circostanze. La convenzione potrebbe anche essere che M [1, 1] restituisca un array di forme (1, 1) ma che di solito è anche meno conveniente di uno scalare. Se vuoi davvero un comportamento simile a una matrice, allora sarebbe meglio usare un matrixoggetto. 2) In realtà, np.outerfunziona indipendentemente dal fatto che le forme sono (1, R), (R, 1)o una combinazione dei due.
Bogatron,

0

Ci sono già molte buone risposte qui. Ma per me è stato difficile trovare qualche esempio, in cui la forma o l'array possono interrompere tutto il programma.

Quindi ecco quello:

import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])


from sklearn.linear_model import LinearRegression
regr = LinearRegression()
regr.fit(a,b)

Questo fallirà con errore:

ValueError: array 2D previsto, invece ottenuto array 1D

ma se aggiungiamo reshapea a:

a = np.array([1,2,3,4]).reshape(-1,1)

questo funziona correttamente!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.