Maximum number of "True" values on any axis in a Numpy Multil-dimensional Array

Viewed 111

I have a multi-dimension array in Numpy with boolean values. In my case, it is a cube.

I would like to know the maximal dimension of a the smallest rectangular box containing all the True values. In other words, that would be the maximum distance on any axis between True values.

For example, if I have the following array

np.array([[
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False,  True, False],
    [False, False,  True,  True, False],
    [False,  True,  True,  True, False],
    [False,  True, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
]])

it would return 4 because we have a distance of 4 vertically in this "box" (or a distance of 4 between the bottom-left and upper-right values):

  ...
  [False False  True]
  [False  True  True]
  [ True  True  True]
  [ True False False]
  ...

I was thinking of the following but that sounds redundant, repetitive, and costly... and actually not exactly working :)

   from itertools import product

    max_1 = max(sum(cube[:, i, j]) for i, j in product(range(3), range(3)))
    max_2 = max(sum(cube[i, :, j]) for i, j in product(range(3), range(3)))
    max_3 = max(sum(cube[i, j, :]) for i, j in product(range(3), range(3)))

    # ... and then
    max_dim = max(max_1, max_2, max_3)

Any suggestion?

3 Answers

If your goal is to get the length of the largest dimension, you can use

max(np.ptp(d) + 1 for d in np.nonzero(a))

where a is your boolean array. The output for the array in your question is 4. Read on if you need to get the smallest 3D bounding box for the True values.


One could use numpy.nonzero to find the indices of the True values in each dimension, then index the original array according to the minimum and maximum of the indices found by numpy.nonzero.

import numpy as np

a = np.array([[
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, True, False],
    [False, False, True, True, False],
    [False, True, True, True, False],
    [False, True, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
]])

slices = [slice(d.min(), d.max()+1) for d in np.nonzero(a)]
a[slices[0], slices[1], slices[2]]

The output matches your desired output.

array([[[False, False,  True],
        [False,  True,  True],
        [ True,  True,  True],
        [ True, False, False]]])

This can be turned into a function to work with N-d arrays. This uses numpy.s_, which creates an index expression that can be used to get slices of an array.

def get_bbox_indexer(a):
    slices = tuple(slice(d.min(), d.max()+1) for d in np.nonzero(a))
    return np.s_[slices]

indexer = get_bbox_indexer(a)
a[indexer]

The output is the same as above

array([[[False, False,  True],
        [False,  True,  True],
        [ True,  True,  True],
        [ True, False, False]]])

To get the size of the largest dimension with the sliced array, you can do

indexer = get_bbox_indexer(a)
max(a[indexer].shape)

I think the easiest way is to get the indices of True elements and then compute the min and max of these -- this will be your rectangle.

a = [...]
indices = np.array(np.where(a)).T
print(np.min(indices, axis=0), np.max(indices, axis=0))

[2 1 1] [2 4 3]

Then to get the max length, just subtract these two, find the max, and add 1.

print(np.max(np.max(indices, axis=0) - np.min(indices, axis=0)) + 1)

4

This answer is actually working for me but it assumes that the True values are contiguous.

import numpy as np

a = np.array([[
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, True, False],
    [False, False, True, True, False],
    [False, True, True, True, False],
    [False, True, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
], [
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
    [False, False, False, False, False],
]])

max_axis = (np.sum(np.any(a, axis=axis), axis=1) for axis in range(3))
max_dim = max(max(m) for m in max_axis)
  1. For each axis, detect if at least one True is present on the other axes
  2. Count the number of such axes with True
  3. Compute the max for each axis
Related