Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deleting nodes while plotting 3D network using mayavi

I've a graph network created using Networkx and plotted using Mayavi.

After the graph is created, I 'm deleting nodes with degree < 2, using G.remove_nodes_from(). Once the nodes are deleted, the edges connected to these nodes are deleted but the nodes still appear in the final output (image below).

import matplotlib.pyplot as plt
from mayavi import mlab
import numpy as np
import pandas as pd
    
    
pos = [[0.1, 2, 0.3], [40, 0.5, -10],
       [0.1, -40, 0.3], [-49, 0.1, 2],
       [10.3, 0.3, 0.4], [-109, 0.3, 0.4]]
pos = pd.DataFrame(pos, columns=['x', 'y', 'z'])
    
ed_ls = [(x, y) for x, y in zip(range(0, 5), range(1, 6))]
    
G = nx.Graph()
G.add_edges_from(ed_ls)
remove = [node for node, degree in dict(G.degree()).items() if degree < 2]
G.remove_nodes_from(remove)
pos.drop(pos.index[remove], inplace=True)

print(G.edges)
    
nx.draw(G)
plt.show()
    
mlab.figure(1, bgcolor=bgcolor)
mlab.clf()
    
for i, e in enumerate(G.edges()):
    
# ----------------------------------------------------------------------------
    # the x,y, and z co-ordinates are here
    pts = mlab.points3d(pos['x'], pos['y'], pos['z'],
                        scale_mode='none',
                        scale_factor=1)
# ----------------------------------------------------------------------------
    pts.mlab_source.dataset.lines = np.array(G.edges())
    tube = mlab.pipeline.tube(pts, tube_radius=edge_size)
    
    mlab.pipeline.surface(tube, color=edge_color)
    
mlab.show()  # interactive window

enter image description here

I'd like to ask for suggestions on how to remove the deleted nodes and the corresponding positions and display the rest in the output.

Secondly, I would like to know how to delete the nodes and the edges connected to these nodes interactively. For instance, if I want to delete nodes and edges connected to nodes of degree < 2, first I would like to display an interactive graph with all nodes with degree < 2 highlighted. The user can select the nodes that have to be deleted in an interactive manner. By clicking on a highlighted node, the node and connect edge can be deleted.

EDIT: I tried to remove the positions of the deleted nodes from the dataframe pos by including pos.drop(pos.index[remove], inplace=True) updated in the complete code posted above.

But I still don't get the correct output.

enter image description here

like image 219
Natasha Avatar asked Jan 17 '26 06:01

Natasha


1 Answers

Here is a solution for interactive removal of network nodes and edges in Mayavi (I think matplotlib might be sufficient and easier but anyways...).

The solution is inspired by this Mayavi example. However, the example is not directly transferable because a glyph (used to visualize the nodes) consists of many points and when plotting each glyph/node by itself, the point_id cannot be used to identify the glyph/node. Moreover, it does not include the option to hide/delete objects. To avoid these problems, I used four ideas:

  1. Each node/edge is plotted as a separate object, so it is easier to adjust it's (visibility) properties.

  2. Instead of deleting nodes/edges, they are just hidden when clicked upon. Moreover, clicking twice makes the node visible again (this does not work for the edges with the code below but you might be able to implement that if required, just needs keeping track of visible nodes). The visible nodes can be collected at the end (see code below).

  3. As in the example, the mouse position is captured using a picker callback. But instead of using the point_id of the closest point, it's coordinates are used directly.

  4. The node to be deleted/hidden is found by computing the minimum Euclidean distance between the mouse position and all nodes.

PS: In your original code, the for-loop is quite redundant because it plots all nodes and edges many times on top of each other.

Hope that helps!

enter image description here

# import modules
from mayavi import mlab
import numpy as np
import pandas as pd
import networkx as nx

# set number of nodes
number = 6

# create random node positions
np.random.seed(5)
pos = 100*np.random.rand(6, 3)
pos = pd.DataFrame(pos, columns=['x', 'y', 'z'])

# create chain graph links
links = [(x, y) for x, y in zip(range(0, number-1), range(1, number))]

# create graph (not strictly needed, link list above would be enough)
graph = nx.Graph()
graph.add_edges_from(links)

# setup mayavi figure
figure = mlab.gcf()
mlab.clf()

# add nodes as individual glyphs
# store glyphs in dictionary to allow interactive adjustments of visibility
color = (0.5, 0.0, 0.5)
nodes = dict()
texts = dict()
for ni, n in enumerate(graph.nodes()):
    xyz = pos.loc[n]
    n = mlab.points3d(xyz['x'], xyz['y'], xyz['z'], scale_factor=5, color=color)
    label = 'node %s' % ni
    t = mlab.text3d(xyz['x'], xyz['y'], xyz['z']+5, label, scale=(5, 5, 5))
    # each glyph consists of many points
    # arr = n.glyph.glyph_source.glyph_source.output.points.to_array()
    nodes[ni] = n
    texts[ni] = t

# add edges as individual tubes
edges = dict()
for ei, e in enumerate(graph.edges()):
    xyz = pos.loc[np.array(e)]
    edges[ei] = mlab.plot3d(xyz['x'], xyz['y'], xyz['z'], tube_radius=1, color=color)


# define picker callback for figure interaction
def picker_callback(picker):
    # get coordinates of mouse click position
    cen = picker.pick_position
    # compute Euclidean distance btween mouse position and all nodes
    dist = np.linalg.norm(pos-cen, axis=1)
    # get closest node
    ni = np.argmin(dist)
    # hide/show node and text
    n = nodes[ni]
    n.visible = not n.visible
    t = texts[ni]
    t.visible = not t.visible
    # hide/show edges
    # must be adjusted if double-clicking should hide/show both nodes and edges in a reasonable way
    for ei, edge in enumerate(graph.edges()):
        if ni in edge:
            e = edges[ei]
            e.visible = not e.visible


# add picker callback
picker = figure.on_mouse_pick(picker_callback)
picker.tolerance = 0.01

# show interactive window
# mlab.show()

# collect visibility/deletion status of nodes, e.g.
# [(0, True), (1, False), (2, True), (3, True), (4, True), (5, True)]
[(key, node.visible) for key, node in nodes.items()]
like image 60
David Avatar answered Jan 19 '26 18:01

David