J'ai un tableau numpy binaire 3D, pour lequel je voudrais trouver des composants connectés, i.d. éléments voisins avec la valeur 1.
coordinates = np.argwhere(data > 0) connected_elements = [] for node in coordinates: neighbors = #Get possible neighbors of node if neighbors not in connected_elements: connected_elements.append(node) else: connected_elements.index(neighbor).extend(node)
Alternativement, je peux obtenir les coordonnées de chaque élément avec la valeur un et obtenir un ensemble de listes avec trois éléments pour lesquels je pourrais obtenir des clusters voisins
XXX
Comment puis-je faire cela, ou implémenter une fonction 2D connected_components pour un paramètre 3D?
3 Réponses :
Supposons:
Vous parlez de 6 voisins possibles d'un nœud (i, j, k)
dans un graphe 3D, et par "voisin" vous entendez la distance entre le voisin et le nœud vaut 1; et
"Un composant connecté valide" signifie que nodeA et nodeB sont voisins et que les deux valeurs sont 1.
Ensuite, nous pouvons avoir une telle fonction pour obtenir les voisins possibles:
def get_neighbors(data, i, j, k): neighbors = [] candidates = [(i-1, j, k), (i, j-1, k), (i, j, k-1), (i, j, k+1), (i, j+1, k), (i+1, j, k)] for candidate in candidates: try: if data[candidate] == 1: neighbors.append(candidate) except IndexError: pass return neighbors
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D def plot(data): fig = plt.figure(figsize=(10,10)) ax = fig.gca(projection='3d') for i in range(data.shape[0]): for j in range(data.shape[1]): ax.scatter([i]*data.shape[0], [j]*data.shape[1], [i for i in range(data.shape[2])], c=['r' if i == 0 else 'b' for i in data[i,j]], s=50) plot(data) plt.show() plt.close('all') plot(v) plt.show()
Algo principal:
DFP: : il s'agit de l'algorithme DFS traditionnel utilisant Queue. La seule différence pour le cas 3D est donnée (x, y, z)
nous calculons tout le voisin valide de celui-ci en utilisant itertools.product
. Dans le cas 3D, chaque point aura 27 voisins y compris lui-même (3 positions et 3 valeurs possibles - idem, incrémenter, décrémenter, donc 27 voies).
La matrice v
stocke les composants connectés numérotés à partir de 1.
Testcase:
when data =
XXX
les deux côtés opposés sont les deux composants connectés différents
L'algorithme renvoie v
[[[1 1 1] [1 1 1] [1 1 1]] [[0 0 0] [0 0 0] [0 0 0]] [[2 2 2] [2 2 2] [2 2 2]]]
Comme suggéré dans la question, nous générons d'abord les données et trouvons les coordonnées.
Ensuite, nous pouvons utiliser kd tree cKDTree
pour trouver des voisins à une distance de 1 avec query_pairs
et utilisez-les comme arêtes du graphe, ce qui réduit essentiellement le problème à une recherche de composants connectés par graphe standard.
Ensuite, nous créons le graphe networkx à partir de ces arêtes avec from_edgelist
et exécutez connected_components
pour trouver les composants connectés.
Et la dernière étape est la visualisation.
4.0 5 1.0 4 7.0 3 8.0 3 5.0 2 Name: c, dtype: int64
Output:
Remarques:
# pour inclure les composants connectés à un seul nœud
commentaire df
contient les coordonnées x
, y
et z
et l'index du composant connecté c pour chaque nœud:
df['c'].value_counts().nlargest(5)
Sortie :
x y z c 0 0 0 3 20.0 1 0 1 8 21.0 2 0 2 1 6.0 3 0 2 3 22.0 4 0 3 0 23.0 ...
df
, nous pouvons également vérifier des choses amusantes, comme la taille des plus gros composants connectés trouvés (avec le numéro du composant connecté): print(df)
Sortie :
import pandas as pd import numpy as np import networkx as nx import matplotlib.pyplot as plt from scipy.spatial.ckdtree import cKDTree from mpl_toolkits.mplot3d import Axes3D # create data data = np.random.binomial(1, 0.1, 1000) data = data.reshape((10,10,10)) # find coordinates cs = np.argwhere(data > 0) # build k-d tree kdt = cKDTree(cs) edges = kdt.query_pairs(1) # create graph G = nx.from_edgelist(edges) # find connected components ccs = nx.connected_components(G) node_component = {v:k for k,vs in enumerate(ccs) for v in vs} # visualize df = pd.DataFrame(cs, columns=['x','y','z']) df['c'] = pd.Series(node_component) # to include single-node connected components # df.loc[df['c'].isna(), 'c'] = df.loc[df['c'].isna(), 'c'].isna().cumsum() + df['c'].max() fig = plt.figure(figsize=(10,10)) ax = fig.add_subplot(111, projection='3d') cmhot = plt.get_cmap("hot") ax.scatter(df['x'], df['y'], df['z'], c=df['c'], s=50, cmap=cmhot)
Êtes-vous autorisé à vous déplacer uniquement le long de l'axe pour trouver des composants connectés ou sommes-nous autorisés à en faire plus