0

I have to do spectral clustering on moons shaped dataset and then have to create a graph showing the links between data points.

This is my code

import numpy as np
import os
from sklearn import metrics
from sklearn.cluster import SpectralClustering
from sklearn.neighbors import DistanceMetric
from sklearn.cluster import KMeans
import pandas as pd
import pylab as pl
import sklearn.metrics as sm
from sklearn.metrics import confusion_matrix,classification_report
from sklearn.preprocessing import MinMaxScaler
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
import networkx as nx
X, y = make_moons(n_samples=20)
print(X)
clustering=SpectralClustering(n_clusters=2,
       assign_labels='kmeans',affinity='rbf',gamma=10, degree=3,
         random_state=0)
y_predict=clustering.fit_predict(X)
y_predict_labels = clustering.labels_
clustering.affinity_matrix_

I have got the nodes as data points and the affinity matrix as the weight over the edges. Can someone help me to create a graph using nearest neighbors=2 in the shape of two moons(as my dataset is of two moons) using data points as nodes and affinity matrix as edges between the nodes.

Vinícius Queiroz
  • 849
  • 1
  • 7
  • 19
Ytt
  • 29
  • 8

2 Answers2

3

If by "nearest neighbours=2" you mean that, instead of returning a Complete Graph, each node must have an outdegree of 2, then one way to achieve this is with the code below:

Code

k = 2

# Make Graph
G = nx.DiGraph()
for i in range(0, len(X)):
  affinity_list = clustering.affinity_matrix_[i]
  affinity_list[i] = 0 # in case we don't want to consider the node as it's own neighbour
  nearest_neighbors_indices = np.argpartition(clustering.affinity_matrix_[i], -k)[-k:]
  for j in nearest_neighbors_indices:
    G.add_edge(tuple(X[i]), tuple(X[j]), weight = clustering.affinity_matrix_[i][j])

# Draw Graph
pos = {node_name: node_name for node_name in G.nodes}
nx.draw_networkx(G, pos, with_labels=False)

# for node in G.nodes:
#    print(list(G.neighbors(node)))

Output

enter image description here

Details

  • For each node we get the indices corresponding to the two maximum values in the affinity matrix with the np.argpartition() method.
  • We don't consider nodes as neighbors of themselves, so we change their affinity with themselves to 0, before applying the np.argparition().
  • We need a nx.DiGraph instead of a nx.Graph to correctly retrieve only the 2 nearest neighbours of a node. If we use a standard, undirected graph instead, some nodes will have 3 neighbours because they are the closest to another node, which might not be reciprocal
    • As an example, the node with the highest Y in the output have two successors (neighbours) and three predecessors, while two of its predecessors are also successors. If it were an undirected graph, it would have 3 neighbours, since there is no discernment between successors and predecessors in undirected graphs.
    • I recommend uncommenting the print in the last 2 lines of the code, checking the outputs, and then changing the DiGraph to a Graph to understand what this means, if it is not clear yet.
Vinícius Queiroz
  • 849
  • 1
  • 7
  • 19
  • Thank you for your help. I think this is the correct graph. Can you just help me to change the colour and shape of the nodes? I wanted to change the colour and shape of one cluster so as to differentiate it from the second one. – Ytt Aug 20 '21 at 16:41
  • To change the color of each node according to each cluster you should use a colormap. I recommend searching for other questions in SO related specifically this matter, such as this one: https://stackoverflow.com/questions/28910766/python-networkx-set-node-color-automatically-based-on-number-of-attribute-opt or this one: https://stackoverflow.com/questions/27030473/how-to-set-colors-for-nodes-in-networkx If you don't find anything that answers you, then I recommend creating a new question, as changing nodes' colors and shapes are not related to the original question. – Vinícius Queiroz Aug 23 '21 at 19:50
  • Lastly, I feel like I should say that the documentation on `draw_networkx()` might help you as well: https://networkx.org/documentation/stable/reference/generated/networkx.drawing.nx_pylab.draw_networkx.html . Specifically the optional parameters `node_color` and `node_shape` – Vinícius Queiroz Aug 23 '21 at 19:52
  • Hey, can you answer this question on the given link. I will be very grateful. Same as the question above with some minor details added. Here is the link [link](https://stackoverflow.com/questions/68928638/to-make-a-graph-using-networkx-after-spectral-clustering-on-moons-dataset) – Ytt Aug 25 '21 at 19:14
1

Code

# Make Graph
G = nx.Graph()
i = 0
for i in range(0, len(X)):
  j = 0
  for affinity in clustering.affinity_matrix_[i]:
    G.add_edge(tuple(X[i]), tuple(X[j]), weight = affinity)
    j += 1
  i += 1

# Draw graph in moon shape
pos = {node_name: node_name for node_name in G.nodes}
nx.draw_networkx(G, pos, with_labels=False)

Output

enter image description here

Details

  • Nodes' indices in Networkx need to be immutable. That's why we convert X[i] and X[j] to tuples;
  • To plot the graph in a moon shape, we first get the positions of each node by getting their indices with G.nodes, and store them in a dictionary (the pos variable is constructed with a dict comprehension). Then we can use the pos dict to draw the graph with the custom layout;
  • There might be a more "pythonic" way to make the graph, but this will work too.
Dharman
  • 30,962
  • 25
  • 85
  • 135
Vinícius Queiroz
  • 849
  • 1
  • 7
  • 19
  • Thank you soo much for your answer. I wanted to create a graph using nearest neighbour =2. I think the nearest neighbour is not used for creating the above graph. – Ytt Aug 20 '21 at 12:45
  • By "nearest neighbour=2" do you mean that instead of having a Complete Graph, you want to have edges only between the two nearest nodes, for each node? – Vinícius Queiroz Aug 20 '21 at 13:03
  • Yes for one particular node it should consider its two nearest neighbours and same for all the nodes it should consider its 2 nearest node without showing connection to itself. – Ytt Aug 20 '21 at 15:29