循环替换numpy数组的布尔/非零索引

时间:2019-08-31 14:37:55

标签: python numpy numpy-ndarray

我只需要选择3d二进制数组的非零3d部分(或布尔数组的真实值)。目前,我可以使用一系列使用np.any的“ for”循环来做到这一点,但这确实可行,但似乎很尴尬且缓慢,因此目前正在研究一种更直接的方法来完成任务。

我对numpy相当陌生,因此我尝试过的方法包括a)使用 np.nonzero,它返回我不知所措的索引,以了解出于我的目的该做什么,b)boolean array indexing和c)boolean masks。我通常可以理解用于简单2d数组的每种方法,但是我很难理解这些方法之间的差异,并且无法让它们返回3d数组的正确值。

这是我当前的函数,该函数返回具有非零值的3D数组:

def real_size(arr3):
    true_0 = []
    true_1 = []
    true_2 = []
    print(f'The input array shape is: {arr3.shape}')

    for zero_ in range (0, arr3.shape[0]):
        if arr3[zero_].any()==True:
            true_0.append(zero_)
    for one_ in range (0, arr3.shape[1]):
        if arr3[:,one_,:].any()==True:
            true_1.append(one_)
    for two_ in range (0, arr3.shape[2]):
        if arr3[:,:,two_].any()==True:
            true_2.append(two_)

    arr4 = arr3[min(true_0):max(true_0) + 1, min(true_1):max(true_1) + 1, min(true_2):max(true_2) + 1]
    print(f'The nonzero area is: {arr4.shape}')
    return arr4

# Then use it on a small test array:
test_array = np.zeros([2, 3, 4], dtype = int)
test_array[0:2, 0:2, 0:2] = 1

#The function call works and prints out as expected:
non_zero = real_size(test_array)
>> The input array shape is: (2, 3, 4) 
>> The nonzero area is: (2, 2, 2)

# So, the array is correct, but likely not the best way to get there:
non_zero

>> array([[[1, 1],
        [1, 1]],

       [[1, 1],
        [1, 1]]])

代码工作正常,但是我在更大,更复杂的数组上使用它,因此认为这不是合适的方法。任何对采用更直接的方法进行这项工作的想法将不胜感激。如果输入数组例如在原始数组内有两个单独的非零3d区域,我也会担心错误和结果。

为澄清问题,我需要将一个或多个3D部分作为一个或多个3d数组(以原始的较大数组开头)返回。返回的数组不应在三维空间中任何给定的外部平面中包含无关的零(或假值)。仅获取非零值的索引(反之亦然)本身并不能解决问题。

1 个答案:

答案 0 :(得分:2)

假设您要消除仅包含 零的所有行,列等,可以执行以下操作:

nz = (test_array != 0)
non_zero = test_array[nz.any(axis=(1, 2))][:, nz.any(axis=(0, 2))][:, :, nz.any(axis=(0, 1))]

使用np.nonzero的替代解决方案:

i = [np.unique(_) for _ in np.nonzero(test_array)]
non_zero = test_array[i[0]][:, i[1]][:, :, i[2]]

这也可以推广到任意维度,但需要做更多的工作(此处仅显示第一种方法):

def real_size(arr):
    nz = (arr != 0)
    result = arr
    axes = np.arange(arr.ndim)
    for axis in range(arr.ndim):
        zeros = nz.any(axis=tuple(np.delete(axes, axis)))
        result = result[(slice(None),)*axis + (zeros,)]
    return result

non_zero = real_size(test_array)