python - array - numpy reshape
Diferencia entre la forma numpy.array(R, 1) y(R,) (5)
En numpy
, algunas de las operaciones retornan en forma (R, 1)
pero algunas regresan (R,)
. Esto hará que la multiplicación de matrices sea más tediosa ya que se requiere una reshape
explícita. Por ejemplo, dada una matriz M
, si queremos hacer numpy.dot(M[:,0], numpy.ones((1, R)))
donde R
es el número de filas (por supuesto, el mismo problema también ocurre en forma de columna). Obtendremos que las matrices are not aligned
error ya que M[:,0]
está en forma (R,)
pero numpy.ones((1, R))
está en forma (1, R)
.
Así que mis preguntas son:
¿Cuál es la diferencia entre la forma
(R, 1)
y(R,)
? Sé que, literalmente, que es la lista de números y lista de listas, donde toda la lista contiene sólo un número. Solo me pregunto por qué no diseñarnumpy
para que favorezca la forma(R, 1)
lugar de(R,)
para facilitar la multiplicación de matrices.¿Hay mejores formas para el ejemplo anterior? Sin una forma explícita de reformar de esta manera:
numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))
1. El significado de las formas en NumPy
Escribe: "Sé que literalmente es una lista de números y una lista de listas donde toda la lista contiene solo un número", pero esa es una manera poco útil de pensar en ello.
La mejor manera de pensar acerca de las matrices NumPy es que constan de dos partes, un búfer de datos que es solo un bloque de elementos en bruto, y una vista que describe cómo interpretar el búfer de datos.
Por ejemplo, si creamos una matriz de 12 enteros:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Entonces a
consiste en un búfer de datos, arreglado algo como esto:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
y una vista que describe cómo interpretar los datos:
>>> 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,)
Aquí, la forma (12,)
significa que la matriz se indexa mediante un índice único que se ejecuta de 0 a 11. Conceptualmente, si etiquetamos este único índice i
, la matriz a
parece a esto:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Si reshape una matriz, esto no cambia el búfer de datos. En su lugar, crea una nueva vista que describe una forma diferente de interpretar los datos. Así que después:
>>> b = a.reshape((3, 4))
la matriz b
tiene el mismo búfer de datos que a
, pero ahora está indexada por dos índices que se ejecutan de 0 a 2 y de 0 a 3 respectivamente. Si etiquetamos los dos índices i
y j
, la matriz b
ve así:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Lo que significa que:
>>> b[2,1]
9
Puede ver que el segundo índice cambia rápidamente y el primer índice cambia lentamente. Si prefiere que esto sea al revés, se puede especificar el order
de parámetros:
>>> c = a.reshape((3, 4), order=''F'')
lo que resulta en una matriz indexada como esta:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Lo que significa que:
>>> c[2,1]
5
Ahora debe quedar claro lo que significa que una matriz tenga una forma con una o más dimensiones de tamaño 1. Después:
>>> d = a.reshape((12, 1))
la matriz d
está indexada por dos índices, el primero de los cuales se ejecuta de 0 a 11, y el segundo índice siempre es 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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
y entonces:
>>> d[10,0]
10
Una dimensión de longitud 1 es "libre" (en cierto sentido), por lo que no hay nada que le impida ir a la ciudad:
>>> e = a.reshape((1, 2, 1, 6, 1))
dando una matriz indexada como esta:
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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
y entonces:
>>> e[0,1,0,0,0]
6
Consulte la documentación internos NumPy para más detalles acerca de cómo se implementan las matrices.
2. ¿Qué hacer?
Ya que reshape simplemente crea una nueva vista, no debes tener miedo de usarla siempre que sea necesario. Es la herramienta adecuada para usar cuando se desea indexar una matriz de una manera diferente.
Sin embargo, en un cálculo largo, por lo general, es posible organizar arreglos para construir matrices con la forma "correcta" en primer lugar, y así minimizar el número de formas y transposiciones. Pero sin ver el contexto real que llevó a la necesidad de una reforma, es difícil decir qué se debe cambiar.
El ejemplo en tu pregunta es:
numpy.dot(M[:,0], numpy.ones((1, R)))
Pero esto no es realista. Primero, esta expresión:
M[:,0].sum()
calcula el resultado de manera más simple. Segundo, ¿hay realmente algo especial en la columna 0? Quizás lo que realmente necesitas es:
M.sum(axis=0)
1) La razón para no preferir una forma de (R, 1)
sobre (R,)
es que complica innecesariamente las cosas. Además, ¿por qué sería preferible tener la forma (R, 1)
por defecto para un vector de longitud R en lugar de (1, R)
? Es mejor mantenerlo simple y ser explícito cuando necesite dimensiones adicionales.
2) Para su ejemplo, está calculando un producto externo, por lo que puede hacer esto sin una llamada de np.outer
usando np.outer
:
np.outer(M[:,0], numpy.ones((1, R)))
La diferencia entre (R,)
y (1,R)
es, literalmente, el número de índices que necesita usar. ones((1,R))
es una matriz 2-D que pasa a tener sólo una fila. ones(R)
es un vector. En general, si no tiene sentido que la variable tenga más de una fila / columna, debe usar un vector, no una matriz con una dimensión singleton.
Para su caso específico, hay un par de opciones:
1) Solo haz el segundo argumento un vector. Lo siguiente funciona bien:
np.dot(M[:,0], np.ones(R))
2) Si desea operaciones de matriz tipo matlab, use la matrix
clase en lugar de ndarray
. Todas las matrices son forzadas a ser matrices en 2-D, y el operador *
realiza la multiplicación de matrices en lugar de los elementos (por lo que no necesita un punto). En mi experiencia, esto es más problemático que vale la pena, pero puede ser bueno si estás acostumbrado a matlab.
La forma es una tupla. Si sólo hay 1 dimensión la forma será un número y acaba en blanco después de una coma. Para 2+ dimensiones, habrá un número después de todas las comas.
# 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)
Por su clase matriz base, arrays 2D son no más especial que los 1d o 3D. Hay algunas operaciones de la reserva de las dimensiones, algunos que los reducen, otros combinan o incluso expandirlas.
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.]])
Otras expresiones que dan la misma matriz.
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 comenzó solo con matrices 2D. Las versiones más nuevas permiten más dimensiones, pero conservan el límite inferior de 2. Pero aún debe prestar atención a la diferencia entre una matriz de filas y una columna, una con la forma (1,3)
v (3,1)
. ¿Cuántas veces has escrito [1,2,3].''
? Iba a escribir el row vector
y el row vector
column vector
, pero con esa restricción 2d, no hay ningún vector en MATLAB, al menos no en el sentido matemático del vector como 1d.
¿Has mirado np.atleast_2d
(también versiones _1d y _3d)?