【问题标题】:Alternative to loop for for boolean / nonzero indexing of numpy arraynumpy数组的布尔/非零索引循环的替代方法
【发布时间】:2020-01-04 10:16:21
【问题描述】:

我只需要选择 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 随机分散在原始数组中会怎样?
  • 我不确定为什么你需要这样做,所以我只是猜测,但如果你试图降低操作的速度和内存稀疏数组,那么您可以考虑使用一些稀疏数据结构,例如 scipy.sparse 提供的。
  • 该应用程序是为 3D 打印应用程序减少一个数组。所以要在场内旋转或平移形状,我只需要操纵正体素区域。

标签: python numpy numpy-ndarray


【解决方案1】:

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

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-06
    • 2017-09-24
    • 1970-01-01
    • 2020-03-25
    • 2017-08-06
    • 2015-05-19
    • 2019-05-04
    • 1970-01-01
    相关资源
    最近更新 更多