Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

numpy 2d boolean array count consecutive True sizes

I'm interested in finding out individual sizes of the 'True' patches in a boolean array. For instance in the boolean matrix:

[[1, 0, 0, 0],
 [0, 1, 1, 0],
 [0, 1, 0, 0],
 [0, 1, 0, 0]]

The output would be:

[[1, 0, 0, 0],
 [0, 4, 4, 0],
 [0, 4, 0, 0],
 [0, 4, 0, 0]]

I'm aware that I can do this recursively, but I'm also under the impression that python array operations are costly on large scale and is there an available library function for this?

like image 710
Rocky Li Avatar asked Aug 14 '26 18:08

Rocky Li


1 Answers

Here's a quick and simple complete solution:

import numpy as np
import scipy.ndimage.measurements as mnts

A = np.array([
    [1, 0, 0, 0],
    [0, 1, 1, 0],
    [0, 1, 0, 0],
    [0, 1, 0, 0]
])

# labeled is a version of A with labeled clusters:
#
# [[1 0 0 0]
#  [0 2 2 0]
#  [0 2 0 0]
#  [0 2 0 0]]
#
# clusters holds the number of different clusters: 2
labeled, clusters = mnts.label(A)

# sizes is an array of cluster sizes: [0, 1, 4]
sizes = mnts.sum(A, labeled, index=range(clusters + 1))

# mnts.sum always outputs a float array, so we'll convert sizes to int
sizes = sizes.astype(int)

# get an array with the same shape as labeled and the 
# appropriate values from sizes by indexing one array 
# with the other. See the `numpy` indexing docs for details
labeledBySize = sizes[labeled]

print(labeledBySize)

output:

[[1 0 0 0]
 [0 4 4 0]
 [0 4 0 0]
 [0 4 0 0]]

The trickiest line above is the "fancy" numpy indexing:

labeledBySize = sizes[labeled]

in which one array is used to index the other. See the numpy indexing docs (section "Index arrays") for details on why this works.

I also wrote a version of the above code as a single compact function that you can try out yourself online. It includes a test case based on a random array.

like image 167
tel Avatar answered Aug 16 '26 08:08

tel