I'm working on a particle system simulation in which many thousands of particles randomly walk until they attach to a growing cluster. To visualize this cluster, I'm storing the cluster particle positions as 1s in a numpy array initially full of 0s and then using the matshow
function. The 0s are colored white and the 1s are colored black.
I've noticed in these simulations that a situation like the following might occur (the actual arrays are much larger, 1000x1000 or larger):
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
[0., 0., 1., 1., 1., 1., 1., 1., 0., 0.],
[0., 0., 1., 1., 0., 0., 1., 1., 0., 0.],
[0., 0., 1., 1., 0., 0., 1., 1., 0., 0.],
[0., 0., 1., 1., 1., 1., 1., 1., 0., 0.],
[0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
where particles (1s) attach in a such a way so that a "pocket" (of 0s) is formed. In the images of these clusters they appear as tiny holes. Here's what this looks like for a smaller cluster:
If you squint, you can see a handful of the "holes" I'm talking about. These correspond to 0s completely surrounded by 1s in the associated numpy array.
My question: How can I write a function that will:
- Count the number of 0s that are completely surrounded by 1s
- Change the 0s that are completely surrounded by 1s into -1s (I have a reason for wanting them to be -1s instead)
My first pass at writing such a function works for small/simple arrays like the example above, but fails to work for the actual cluster arrays in the simulation:
def holes(matrix):
num_holes = 0
for row in range(matrix.shape[0]):
for col in range(matrix.shape[0]):
if matrix[row, col] == 0:
can_escape = True
path_down = matrix[row, col:]
path_up = np.flip(matrix[:row+1, col])
path_right = matrix[row:, col]
path_left = np.flip(matrix[row, :col+1])
if 1 in path_down[1:] and 1 in path_up[1:] and 1 in path_left[1:] and 1 in path_right[1:]:
can_escape = False
if can_escape is False:
matrix[row, col] = -1
num_holes += 1
return num_holes
I recognize this function will fail to do what I want it to do for situations like this, where a "cleft" occurs:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 1., 1., 1., 1., 0., 0.],
[0., 0., 1., 0., 0., 0., 1., 1., 0., 0.],
[0., 0., 1., 0., 0., 0., 1., 1., 0., 0.],
[0., 0., 1., 0., 1., 1., 1., 1., 0., 0.],
[0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
Calling my attempt on this would give:
array([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 1., 1., 1., 1., 0., 0.],
[0., 0., 1., 0., -1., -1., 1., 1., 0., 0.],
[0., 0., 1., 0., -1., -1., 1., 1., 0., 0.],
[0., 0., 1., 0., 1., 1., 1., 1., 0., 0.],
[0., 0., 0., 1., 1., 1., 1., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
which is incorrect, since this configuration does not actually have a pocket of 0s.
How can I do this correctly, and perhaps efficiently? Like I said, the simulation is running on arrays at least of size 1000x1000.