Una matriz contigua es simplemente una matriz almacenada en un bloque de memoria ininterrumpido: para acceder al siguiente valor en la matriz, simplemente nos movemos a la siguiente dirección de memoria.
Considere la matriz 2D arr = np.arange(12).reshape(3,4)
. Se parece a esto:
En la memoria de la computadora, los valores de arr
se almacenan así:
Esto significa que arr
es una matriz contigua en C porque las filas se almacenan como bloques contiguos de memoria. La siguiente dirección de memoria contiene el siguiente valor de fila en esa fila. Si queremos bajar una columna, solo tenemos que saltar tres bloques (por ejemplo, saltar de 0 a 4 significa que saltamos 1,2 y 3).
Transponer la matriz con arr.T
significa que la contigüidad C se pierde porque las entradas de filas adyacentes ya no están en direcciones de memoria adyacentes. Sin embargo, Fortranarr.T
es contiguo ya que las columnas están en bloques contiguos de memoria:
En cuanto al rendimiento, acceder a las direcciones de memoria que están una al lado de la otra suele ser más rápido que acceder a las direcciones que están más "dispersas" (obtener un valor de la RAM podría implicar que se obtengan y se almacenen en caché varias direcciones vecinas para la CPU). significa que las operaciones sobre matrices contiguas a menudo serán más rápidas.
Como consecuencia del diseño de memoria contigua de C, las operaciones por filas suelen ser más rápidas que las operaciones por columnas. Por ejemplo, normalmente encontrará que
np.sum(arr, axis=1) # sum the rows
es un poco más rápido que:
np.sum(arr, axis=0) # sum the columns
De manera similar, las operaciones en columnas serán un poco más rápidas para las matrices contiguas de Fortran.
Finalmente, ¿por qué no podemos aplanar la matriz contigua de Fortran asignando una nueva forma?
>>> arr2 = arr.T
>>> arr2.shape = 12
AttributeError: incompatible shape for a non-contiguous array
Para que esto sea posible, NumPy tendría que juntar las filas de arr.T
esta manera:
(La configuración del shape
atributo asume directamente el orden C, es decir, NumPy intenta realizar la operación por filas).
Esto es imposible de hacer. Para cualquier eje, NumPy debe tener una longitud de zancada constante (el número de bytes a mover) para llegar al siguiente elemento de la matriz. Aplanar arr.T
de esta manera requeriría saltar hacia adelante y hacia atrás en la memoria para recuperar valores consecutivos de la matriz.
Si arr2.reshape(12)
escribiéramos en su lugar, NumPy copiaría los valores de arr2 en un nuevo bloque de memoria (ya que no puede devolver una vista a los datos originales para esta forma).
arr2
a 1D(12,)
usa el orden C, lo que significa que el eje 1 se desenrolla antes que el eje 0 (es decir, cada una de las cuatro filas debe colocarse una al lado de la otra para crear la matriz 1D deseada). Es imposible leer esta secuencia de números enteros (0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11) fuera del búfer usando una longitud de paso constante (los bytes para saltar para visitar estos elementos en secuencia serían 4, 4, -7, 4, 4, -7, 4, 4, 7, 4, 4). NumPy requiere una longitud de zancada constante por eje.arr[:, ::-1]
es una vista del mismo búfer de memoria quearr
, NumPy no lo considera en orden C o F, ya que ha atravesado los valores en el búfer en un orden "no estándar" ...Quizás este ejemplo con 12 valores de matriz diferentes ayude:
Los
C order
valores están en el orden en que fueron generados. Los transpuestos no sonPuede obtener vistas 1d de ambos
la forma de
x
también se puede cambiar.Pero la forma de la transposición no se puede cambiar. El
data
todavía está en el0,1,2,3,4...
orden, que no se puede acceder accede como0,4,8...
en una matriz de 1d.Pero
x1
se puede cambiar una copia de :Mirar
strides
también podría ayudar. Un paso es qué tan lejos (en bytes) tiene que dar un paso para llegar al siguiente valor. Para una matriz 2d, habrá 2 valores de zancada:Para ir a la siguiente fila, paso 16 bytes, la siguiente columna solo 4.
Transpose simplemente cambia el orden de los pasos. La siguiente fila tiene solo 4 bytes, es decir, el siguiente número.
Cambiar la forma también cambia las zancadas, simplemente recorra el búfer 4 bytes a la vez.
Aunque
x2
tiene el mismo aspectox1
, tiene su propio búfer de datos, con los valores en un orden diferente. La siguiente columna ahora tiene 4 bytes más, mientras que la siguiente fila tiene 12 (3 * 4).Y al igual que con
x
, cambiar la forma a 1d reduce los pasos a(4,)
.Porque
x1
, con los datos en el0,1,2,...
orden, no hay un paso de 1d que daría0,4,8...
.__array_interface__
es otra forma útil de mostrar información de matriz:La
x1
dirección del búfer de datos será la misma que parax
, con el que comparte los datos.x2
tiene una dirección de búfer diferente.También puede experimentar agregando un
order='F'
parámetro a los comandoscopy
yreshape
.fuente