Set dynamic node shape in network with matplotlib

2024/9/8 10:33:53

First time poster here, so please be gentle. :)

I'm trying to graph a network of characters of different types in Networkx and want to set different node shapes for each type. For example, I'd like characters to be circles, creatures to be triangles, etc. I've tried to figure this out for several hours and have searched SO extensively, but I haven't found a way to achieve this other than to set different node_lists for each type of character and render them separately, which just seems counterintuitive.

The issue is that I'm unable to access the node_shape dictionary value from within:

nx.draw_networkx_nodes(G, pos) 

I've tried multiple solutions including trying to access the node attribute, creating an external dictionary or list and accessing it from within the call, setting up a list comprehension or iterator and nothing seems to work.

Either I pass a list, which is pulled in wholesale, a dictionary, which the function isn't able to hash, or an instance of the list such as shape_list.pop(0), in which case the function only takes the first value and applies it to all nodes.

I am able to set color by creating a separate node_colors list which is iterated over by the function and even tried creating a dictionary so that the node_shape is triggered by node_color, but that didn't work either.

I'm hoping to use the code as an add-on to a web app developed in Python 3.4 and Django 1.8, so Graphviz isn't an option.

Thanks in advance for any assistance or reference to alternate libraries.

Here is my code:

import json
import requests
import networkx as nx
import matplotlib.pyplot as pltpersonas = 'http://story-chronicles.herokuapp.com/storyobjects/'
target = requests.get(personas)
x = target.json()story_objects = {}
labels = {}
node_colors = []for character in x:name = character["name"]story = character["story"]c_type = character["c_type"]story_objects[name] = {}story_objects[name]['name'] = namestory_objects[name]['story'] = storystory_objects[name]['c_type'] = c_typestory_objects[name]['to_relationships'] = []if character['c_type'] == "Character":story_objects[name]['node_shape'] = 'o'story_objects[name]['node_color'] = 'r'elif character['c_type'] == "Organization":story_objects[name]['node_shape'] = 'h'story_objects[name]['node_color'] = 'b'elif character['c_type'] == "Creature":story_objects[name]['node_shape'] = '^'story_objects[name]['node_color'] = 'g'elif character['c_type'] == "Force":story_objects[name]['node_shape'] = 'v'story_objects[name]['node_color'] = 'c'elif character['c_type'] == "Thing":story_objects[name]['node_shape'] = 's'story_objects[name]['node_color'] = 'y'for relationship in character["to_relationships"]:break_1 = relationship.find(">>")break_2 = relationship.find("weight:")sub_1 = relationship[0:break_1].strip()context = relationship[break_1:break_2]weight = relationship[break_2+8:-1]story_objects[name]['to_relationships'].append([sub_1, context, weight])G=nx.MultiDiGraph()for sub in story_objects:s = story_objects[sub]if s['story'] == "http://story-chronicles.herokuapp.com/story/1/":G.add_node(s['name'], node_shape=s['node_shape'])labels[s['name']] = s['name']node_colors.append(s['node_color'])print("***", s['name'], "***", s['c_type'])print("details:", s['node_color'], s['node_shape'])for i in s['to_relationships']:print('target:', i[0])print('context:', i[1])print('weight:', i[2])G.add_edge(s['name'], i[0], weight=int(i[2]))print("")node_shapes=nx.get_node_attributes(G, 'node_shape') # Latest attempt at getting this to work
node_shapes = [v for k,v in node_shapes.items()]pos=nx.spring_layout(G)
G.degree(weight=weight)nx.draw_networkx_nodes(G, pos, node_color=node_colors, node_shape=node_shapes.pop(0)) # <--- This is where I'm having problems
nx.draw_networkx_edges(G, pos)
nx.draw_networkx_labels(G, pos, labels)plt.show()
Answer

I am afraid that this would have to be done using multiple passes.

The main idea is to use a layout to get the positions of the nodes and then use draw_networkx_nodes repeatedly for the n different classes of nodes.

For example:

import networkx
import pylab#Build a graph (Node attribute 's' determines the node shape here)
G = networkx.Graph()
G.add_node(0, s="^", b=1)
G.add_node(1, s="^", b=2)G.add_node(2, s="o", b=3)
G.add_node(3, s="o", b=4)G.add_node(4, s="v", b=5)
G.add_node(5, s="v", b=6)networkx.add_path(G, [0,2,5])
networkx.add_path(G, [1,4,3,0])
networkx.add_path(G, [2,4,0,5])#Drawing the graph
#First obtain the node positions using one of the layouts
nodePos = networkx.layout.spring_layout(G)#The rest of the code here attempts to automate the whole process by
#first determining how many different node classes (according to
#attribute 's') exist in the node set and then repeatedly calling 
#draw_networkx_node for each. Perhaps this part can be optimised further.#Get all distinct node classes according to the node shape attribute
nodeShapes = set((aShape[1]["s"] for aShape in G.nodes(data = True)))#For each node class...
for aShape in nodeShapes:#...filter and draw the subset of nodes with the same symbol in the positions that are now known through the use of the layout.networkx.draw_networkx_nodes(G,nodePos,node_shape = aShape, nodelist = [sNode[0] for sNode in filter(lambda x: x[1]["s"]==aShape,G.nodes(data = True))])#Finally, draw the edges between the nodes
networkx.draw_networkx_edges(G,nodePos)#And show the final result
pylab.show()

Final result looks something like this:

enter image description here

Hope this helps.

https://en.xdnf.cn/q/73084.html

Related Q&A

How can I strip comments and doc strings from python source code? [closed]

Closed. This question is seeking recommendations for books, tools, software libraries, and more. It does not meet Stack Overflow guidelines. It is not currently accepting answers.We don’t allow questi…

How to install scipy misc package

I have installed (actually reinstalled) scipy:10_x86_64.whl (19.8MB): 19.8MB downloaded Installing collected packages: scipy Successfully installed scipyBut the misc subpackage is apparently not includ…

How to color surface with stronger contrast

In Matlab, I am trying to plot a function on 2-dim Euclidean space with following codes=.05; x=[-2:s:2+s]; y=[-1:s:3+s]; [X,Y]=meshgrid(x,y); Z=(1.-X).^2 + 100.*(Y-X.*X).^2; surf(X,Y,Z) colormap jetHer…

How to know that the interpreter is Jython or CPython in the code? [duplicate]

This question already has answers here:Can I detect if my code is running on cPython or Jython?(5 answers)Closed 9 years ago.Is there a way to detect that the interpreter that executes the code is Jyt…

Regular expression - replace all spaces in beginning of line with periods

I dont care if I achieve this through vim, sed, awk, python etc. I tried in all, could not get it done.For an input like this:top f1 f2 f3sub1 f1 f2 f3sub2 f1 f2 …

Writing append only gzipped log files in Python

I am building a service where I log plain text format logs from several sources (one file per source). I do not intend to rotate these logs as they must be around forever.To make these forever around f…

How to configure bokeh plot to have responsive width and fixed height

I use bokeh embedded via the components function. Acutally I use :plot.sizing_mode = "scale_width"Which scales according to the width and maintains the aspect ratio. But I would like to have …

Matplotlib show multiple images with for loop [duplicate]

This question already has an answer here:Can I generate and show a different image during each loop?(1 answer)Closed 8 years ago.I want to display multiple figure in Matplotlib. Heres my code:for i in…

How do I efficiently fill a file with null data from python?

I need to create files of arbitrary size that contain no data. The are potentially quite large. While I could just loop through and write a single null character until Ive reached the file size, that s…

Setting specific permission in amazon s3 boto bucket

I have a bucket called ben-bucket inside that bucket I have multiple files. I want to be able to set permissions for each file URL. Im not too sure but Im assuming if I wanted URL for each file inside …