arrays - recorrer - multiplicacion de matrices en fortran 90
¿Hay alguna manera de remodelar una matriz que no mantiene el tamaño original(o una solución práctica)? (2)
Como un ejemplo simplificado, supongamos que tengo un conjunto de datos compuesto por 40 valores ordenados. Los valores de este ejemplo son todos enteros, aunque este no es necesariamente el caso para el conjunto de datos real.
import numpy as np
data = np.linspace(1,40,40)
Estoy tratando de encontrar el valor máximo dentro del conjunto de datos para ciertos tamaños de ventana. La fórmula para calcular los tamaños de ventana produce un patrón que se ejecuta mejor con matrices (en mi opinión). Por simplicidad, digamos que los índices que denotan los tamaños de ventana son una lista [1,2,3,4,5]
; esto corresponde a tamaños de ventana de [2,4,8,16,32]
(el patrón es 2**index
).
## this code looks long because I''ve provided docstrings
## just in case the explanation was unclear
def shapeshifter(num_col, my_array=data):
"""
This function reshapes an array to have ''num_col'' columns, where
''num_col'' corresponds to index.
"""
return my_array.reshape(-1, num_col)
def looper(num_col, my_array=data):
"""
This function calls ''shapeshifter'' and returns a list of the
MAXimum values of each row in ''my_array'' for ''num_col'' columns.
The length of each row (or the number of columns per row if you
prefer) denotes the size of each window.
EX:
num_col = 2
==> window_size = 2
==> check max( data[1], data[2] ),
max( data[3], data[4] ),
max( data[5], data[6] ),
.
.
.
max( data[39], data[40] )
for k rows, where k = len(my_array)//num_col
"""
my_array = shapeshifter(num_col=num_col, my_array=data)
rows = [my_array[index] for index in range(len(my_array))]
res = []
for index in range(len(rows)):
res.append( max(rows[index]) )
return res
Hasta ahora, el código está bien. Lo revisé con lo siguiente:
check1 = looper(2)
check2 = looper(4)
print(check1)
>> [2.0, 4.0, ..., 38.0, 40.0]
print(len(check1))
>> 20
print(check2)
>> [4.0, 8.0, ..., 36.0, 40.0]
print(len(check2))
>> 10
Hasta aquí todo bien. Ahora aquí está mi problema.
def metalooper(col_ls, my_array=data):
"""
This function calls ''looper'' - which calls
''shapeshifter'' - for every ''col'' in ''col_ls''.
EX:
j_list = [1,2,3,4,5]
==> col_ls = [2,4,8,16,32]
==> looper(2), looper(4),
looper(8), ..., looper(32)
==> shapeshifter(2), shapeshifter(4),
shapeshifter(8), ..., shapeshifter(32)
such that looper(2^j) ==> shapeshifter(2^j)
for j in j_list
"""
res = []
for col in col_ls:
res.append(looper(num_col=col))
return res
j_list = [2,4,8,16,32]
check3 = metalooper(j_list)
Ejecutar el código de arriba proporciona este error:
ValueError: total size of new array must be unchanged
Con 40 data points
, la matriz se puede remodelar en 2 columns
de 20 rows
, o 4 columns
de 10 rows
u 8 columns
de 5 rows
, PERO en 16 columns
, la matriz no se puede remodelar sin recortar datos desde 40/16 ≠ integer
. Creo que este es el problema con mi código, pero no sé cómo solucionarlo.
Espero que haya una forma de cortar los últimos valores en cada fila que no encajan en cada ventana. Si esto no es posible, espero poder agregar ceros para llenar las entradas que mantienen el tamaño de la matriz original, para poder eliminar los ceros después. O tal vez incluso algún bloque complicado if
- try
- break
. ¿Cuáles son algunas formas de solucionar este problema?
Aquí hay una forma generalizada de cambiar la forma con el truncamiento:
def reshape_and_truncate(arr, shape):
desired_size_factor = np.prod([n for n in shape if n != -1])
if -1 in shape: # implicit array size
desired_size = arr.size // desired_size_factor * desired_size_factor
else:
desired_size = desired_size_factor
return arr.flat[:desired_size].reshape(shape)
Que tu shapeshifter
podría usar en lugar de reshape
Creo que esto te dará lo que quieres en un solo paso:
def windowFunc(a, window, f = np.max):
return np.array([f(i) for i in np.split(a, range(window, a.size, window))])
con f
defecto, que le dará una matriz de máximos para sus ventanas.
Generalmente, al usar np.split
y range
, esto te permitirá np.split
en una lista (posiblemente desigual) de matrices:
def shapeshifter(num_col, my_array=data):
return np.split(my_array, range(num_col, my_array.size, num_col))
Necesita una lista de matrices porque una matriz 2D no puede ser irregular (cada fila necesita el mismo número de columnas)
Si realmente quieres rellenar con ceros, puedes usar np.lib.pad
:
def shapeshifter(num_col, my_array=data):
return np.lib.pad(my_array, (0, num_col - my.array.size % num_col), ''constant'', constant_values = 0).reshape(-1, num_col)
Advertencia:
También es técnicamente posible utilizar, por ejemplo, a.resize(32,2)
que creará un ndArray
rellenado con ceros (como lo solicitó). Pero hay algunas grandes advertencias:
- Debería calcular el segundo eje porque los trucos
-1
no funcionan con el cambio deresize
. Si la matriz original
a
es referenciada por cualquier otra cosa,a.resize
fallará con el siguiente error:ValueError: cannot resize an array that references or is referenced by another array in this way. Use the resize function
La función de cambio de
resize
(es decir,np.resize(a)
) no es equivalente aa.resize
, ya que en lugar de relleno con ceros, volverá al principio.
Dado que parece querer hacer referencia a a
por varias ventanas, a.resize
no es muy útil. Pero es un agujero de conejo en el que es fácil caer.
EDITAR:
Looping a través de una lista es lento. Si su entrada es larga y las ventanas son pequeñas, el windowFunc
anterior se empantanará en los bucles for
. Esto debería ser más eficiente:
def windowFunc2(a, window, f = np.max):
tail = - (a.size % window)
if tail == 0:
return f(a.reshape(-1, window), axis = -1)
else:
body = a[:tail].reshape(-1, window)
return np.r_[f(body, axis = -1), f(a[tail:])]