使用matplotlib在网络中设置动态节点形状

Tof*_*erC 5 python attributes matplotlib nodes networkx

第一次在这里发布海报,所以请保持温柔.:)

我正在尝试在Networkx中绘制不同类型的字符网络,并希望为每种类型设置不同的节点形状.例如,我希望角色是圆形,生物是三角形等等.我试图想出这几个小时并且已经广泛搜索过了,但我还没有找到一种方法来实现这个目的而不是为每种类型的字符设置不同的node_lists并分别渲染它们,这似乎违反直觉.

问题是我无法从内部访问node_shape字典值:

nx.draw_networkx_nodes(G, pos) 
Run Code Online (Sandbox Code Playgroud)

我尝试了多种解决方案,包括尝试访问节点属性,创建外部字典或列表以及从调用中访问它,设置列表理解或迭代器,似乎没有任何工作.

要么我传递一个列表,一个是批量提取的,一个字典,该函数无法哈希,或者一个列表的实例,例如shape_list.pop(0),在这种情况下,该函数只取第一个值并将其应用于所有节点.

我可以通过创建一个单独的node_colors列表来设置颜色,该列表由函数迭代,甚至尝试创建一个字典,以便node_shape由node_color触发,但这也不起作用.

我希望将代码用作在Python 3.4和Django 1.8中开发的Web应用程序的附加组件,因此Graphviz不是一个选项.

提前感谢您对替代图书馆的任何帮助或参考.

这是我的代码:

import json
import requests
import networkx as nx
import matplotlib.pyplot as plt

personas = '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'] = name
    story_objects[name]['story'] = story
    story_objects[name]['c_type'] = c_type
    story_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()
Run Code Online (Sandbox Code Playgroud)

A_A*_*A_A 6

我担心这必须使用多次传递来完成。

主要思想是使用布局来获取节点的位置,然后针对不同类别的节点重复使用draw_networkx_nodesn

例如:

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()
Run Code Online (Sandbox Code Playgroud)

最终结果如下所示:

在此处输入图片说明

希望这可以帮助。