有没有办法重塑不保持原始大小的数组(或方便的解决方法)?

时间:2017-03-30 09:57:50

标签: arrays python-3.x numpy error-handling reshape

作为一个简化的例子,假设我有一个由40个排序值组成的数据集。此示例的值都是整数,但实际数据集不一定如此。

import numpy as np
data = np.linspace(1,40,40)

我试图在某些窗口大小的数据集中找到最大值。计算窗口大小的公式产生了一个最好用数组执行的模式(在我看来)。为简单起见,我们假设表示窗口大小的索引是列表[1,2,3,4,5];这对应于[2,4,8,16,32]的窗口大小(模式为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,数组可以重新整形为2 columns 20 rows4 columns 10 rows8 columns 5 rows 1}},但是在16 columns,由于40/16 ≠ integer,数据无法在不剪切数据的情况下重新整形。我相信这是我的代码的问题,但我不知道如何解决它。

我希望有一种方法可以切断每个行中的最后值,这些值不适合每个窗口。如果这是不可能的,我希望我可以附加零来填充保持原始数组大小的条目,以便我可以删除之后的零。或者甚至可能是一些复杂的if - try - break块。有什么方法可以解决这个问题?

2 个答案:

答案 0 :(得分:3)

我认为这将在一步中为您提供您想要的东西:

def windowFunc(a, window, f = np.max):
    return np.array([f(i) for i in np.split(a, range(window, a.size, window))])

使用默认f,它会为您提供一系列最大值的窗口。

通常,使用np.splitrange,这将允许您拆分为(可能是参差不齐的)数组列表:

def shapeshifter(num_col, my_array=data):    
    return np.split(my_array, range(num_col, my_array.size, num_col))

您需要一个数组列表,因为2D数组不能被褴褛(每行需要相同数量的列)

如果确实想要填充零,则可以使用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. 您需要计算第二个轴,因为-1技巧不适用于resize
  2. 如果原始数组a被其他任何内容引用,则a.resize将失败,并显示以下错误:

    ValueError: cannot resize an array that references or is referenced
    by another array in this way.  Use the resize function
    
  3. resize函数(即np.resize(a))不等同于a.resize,因为它不是用零填充,而是循环回到开头。

    < / LI>

    由于您似乎想要通过多个窗口引用aa.resize不是很有用。但这是一个很容易落入的兔子洞。

    修改

    循环列表很慢。如果您的输入很长且窗口较小,则上面的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:])]
    

答案 1 :(得分:2)

这是一种通过截断重新整形的通用方法:

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