How to get graphically connected array items in Python?

Question:

Alright… Here’s a fun question.
I want to make a program that counts how many patches are connected in a string.
To make this a little easier to grasp, here is how the input/outputs should go:

Input:

.....%%%%%..%
%%%...%%...%%
%.....%%%..%%
...%%.....%%%
....%%.....%%
.....%%%..%%%
%%%%....%%%..

Output:
4 patches.

One block of paint is a "%" sign and a empty space is a ".".
How would I go about finding the number of patches?

For instance, a connected patch would be a few % signs connected like this:

%%%%%
%%...
%....
Asked By: Lashen Dharmadasa

||

Answers:

Here’s one way you could do it by using a bit of hackery to create a NetworkX Graph and then just using NetworkX algorithm number_connected_components.

$ ipython
Python 3.10.6 (main, Aug  2 2022, 00:00:00) [GCC 11.3.1 20220421 (Red Hat 11.3.1-2)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: raster='''.....%%%%%..%
   ...: %%%...%%...%%
   ...: %.....%%%..%%
   ...: ...%%.....%%%
   ...: ....%%.....%%
   ...: .....%%%..%%%
   ...: %%%%....%%%..'''

In [2]: import networkx as nx

In [3]: vertices = [(row, column) for row,line in enumerate(raster.splitlines()) for column,char in
   ...: enumerate(line) if char=='%']

In [4]: G = nx.Graph()

In [5]: G.add_nodes_from(vertices)

In [6]: for node in G:
   ...:     for x in range(node[0]-1,node[0]+2):
   ...:         for y in range (node[1]-1,node[1]+2):
   ...:             if (x,y) in G:
   ...:                 G.add_edge(node, (x,y))
   ...: 

In [7]: nx.number_connected_components(G)
Out[7]: 4

If for some unfortunate reason, nice libraries/packages (like NetworkX) can’t be used, here’s another way to do it.

$ ipython
Python 3.10.6 (main, Aug  2 2022, 00:00:00) [GCC 11.3.1 20220421 (Red Hat 11.3.1-2)]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: raster='''.....%%%%%..%
   ...: %%%...%%...%%
   ...: %.....%%%..%%
   ...: ...%%.....%%%
   ...: ....%%.....%%
   ...: .....%%%..%%%
   ...: %%%%....%%%..'''

In [2]: vertices = [(row, column) for row,line in enumerate(raster.splitlines()) for column,char in
   ...: enumerate(line) if char=='%']

In [3]: vertex_edges = dict()

In [4]: for vertex in vertices:
   ...:     vertex_edges[vertex] = set()
   ...:     for x in range(vertex[0]-1, vertex[0]+2):
   ...:         for y in range (vertex[1]-1, vertex[1]+2):
   ...:             if ((x,y) != vertex) and ((x,y) in vertices):
   ...:                 vertex_edges[vertex].add((x,y))
   ...: 

In [5]: num_connected_components = 0

In [6]: removed_vertices = set()

In [7]: while len(vertex_edges) > 0:
   ...:     num_connected_components += 1
   ...:     vertex, vertex_set = vertex_edges.popitem()
   ...:     removed_vertices.add(vertex)
   ...:     while len(vertex_set) > 0:
   ...:         vertex = vertex_set.pop()
   ...:         if vertex in vertex_edges:
   ...:             removed_vertices.add(vertex)
   ...:             vertex_set = vertex_set.union(vertex_edges[vertex] - removed_vertices)
   ...:             _ = vertex_edges.pop(vertex)
   ...: 

In [8]: num_connected_components
Out[8]: 4
Answered By: rickhg12hs
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.