有没有办法重塑不保持原始大小的数组(或方便的解决方法)?
Is there a way to reshape an array that does not maintain the original size (or a convenient work-around)?
作为一个简化示例,假设我有一个由 40 个排序值组成的数据集。此示例的值均为整数,但实际数据集不一定如此。
import numpy as np
data = np.linspace(1,40,40)
我试图在数据集中找到某些 window 大小的最大值。计算 window 大小的公式产生了一种最适合使用数组执行的模式(在我看来)。为了简单起见,假设表示 window 大小的索引是一个列表 [1,2,3,4,5]
;这对应于 [2,4,8,16,32]
的 window 大小(模式为 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
到目前为止,代码没有问题。我用以下内容检查了它:
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
到目前为止一切顺利。现在我的问题来了。
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)
运行 上面的代码提供了这个错误:
ValueError: total size of new array must be unchanged
使用40 data points
,数组可以重塑为20 rows
的2 columns
,或10 rows
的4 columns
,或10 rows
的8 columns
5 rows
,但在 16 columns
,自 40/16 ≠ integer
以来,如果不裁剪数据,则无法重塑数组。我相信这是我的代码的问题,但我不知道如何解决。
我希望有一种方法可以截断 each 行中不适合每个 window 的 last 值].如果这不可能,我希望我可以附加零来填充保持原始数组大小的条目,以便我可以删除之后的零。或者甚至可能是一些复杂的 if
- try
- break
块。有什么方法可以解决这个问题?
以下是使用截断重塑的通用方法:
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)
您的 shapeshifter
可以用来代替 reshape
我认为这将一步步为您提供您想要的东西:
def windowFunc(a, window, f = np.max):
return np.array([f(i) for i in np.split(a, range(window, a.size, window))])
使用默认 f
,这将为您提供一组最大值 windows。
通常,使用 np.split
和 range
,这会让您拆分成一个(可能参差不齐的)数组列表:
def shapeshifter(num_col, my_array=data):
return np.split(my_array, range(num_col, my_array.size, num_col))
您需要一个数组列表,因为二维数组不能参差不齐(每行需要相同的列数)
如果你真的想要用零填充,你可以使用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)
警告:
在技术上也可以使用,例如,a.resize(32,2)
将创建一个用零填充的 ndArray
(根据您的要求)。 但是有一些重要的警告:
- 您需要计算第二个轴,因为
-1
技巧不适用于 resize
。
如果原始数组 a
被任何其他引用,a.resize
将失败并出现以下错误:
ValueError: cannot resize an array that references or is referenced
by another array in this way. Use the resize function
resize
函数(即 np.resize(a)
)不等同于 a.resize
,因为它不会用零填充,而是循环回到开头。
由于您似乎想通过多个 windows 来引用 a
,因此 a.resize
不是很有用。但这是一个容易掉进去的兔子洞。
编辑:
遍历列表很慢。如果您的输入很长而 windows 很小,上面的 windowFunc
将陷入 for
循环。这应该更有效率:
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:])]
作为一个简化示例,假设我有一个由 40 个排序值组成的数据集。此示例的值均为整数,但实际数据集不一定如此。
import numpy as np
data = np.linspace(1,40,40)
我试图在数据集中找到某些 window 大小的最大值。计算 window 大小的公式产生了一种最适合使用数组执行的模式(在我看来)。为了简单起见,假设表示 window 大小的索引是一个列表 [1,2,3,4,5]
;这对应于 [2,4,8,16,32]
的 window 大小(模式为 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
到目前为止,代码没有问题。我用以下内容检查了它:
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
到目前为止一切顺利。现在我的问题来了。
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)
运行 上面的代码提供了这个错误:
ValueError: total size of new array must be unchanged
使用40 data points
,数组可以重塑为20 rows
的2 columns
,或10 rows
的4 columns
,或10 rows
的8 columns
5 rows
,但在 16 columns
,自 40/16 ≠ integer
以来,如果不裁剪数据,则无法重塑数组。我相信这是我的代码的问题,但我不知道如何解决。
我希望有一种方法可以截断 each 行中不适合每个 window 的 last 值].如果这不可能,我希望我可以附加零来填充保持原始数组大小的条目,以便我可以删除之后的零。或者甚至可能是一些复杂的 if
- try
- break
块。有什么方法可以解决这个问题?
以下是使用截断重塑的通用方法:
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)
您的 shapeshifter
可以用来代替 reshape
我认为这将一步步为您提供您想要的东西:
def windowFunc(a, window, f = np.max):
return np.array([f(i) for i in np.split(a, range(window, a.size, window))])
使用默认 f
,这将为您提供一组最大值 windows。
通常,使用 np.split
和 range
,这会让您拆分成一个(可能参差不齐的)数组列表:
def shapeshifter(num_col, my_array=data):
return np.split(my_array, range(num_col, my_array.size, num_col))
您需要一个数组列表,因为二维数组不能参差不齐(每行需要相同的列数)
如果你真的想要用零填充,你可以使用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)
警告:
在技术上也可以使用,例如,a.resize(32,2)
将创建一个用零填充的 ndArray
(根据您的要求)。 但是有一些重要的警告:
- 您需要计算第二个轴,因为
-1
技巧不适用于resize
。 如果原始数组
a
被任何其他引用,a.resize
将失败并出现以下错误:ValueError: cannot resize an array that references or is referenced by another array in this way. Use the resize function
resize
函数(即np.resize(a)
)不等同于a.resize
,因为它不会用零填充,而是循环回到开头。
由于您似乎想通过多个 windows 来引用 a
,因此 a.resize
不是很有用。但这是一个容易掉进去的兔子洞。
编辑:
遍历列表很慢。如果您的输入很长而 windows 很小,上面的 windowFunc
将陷入 for
循环。这应该更有效率:
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:])]