使用matplotlib在网络中设置动态节点形状 [英] Set dynamic node shape in network with matplotlib

查看:89
本文介绍了使用matplotlib在网络中设置动态节点形状的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

第一次发帖,请多多关照.:)

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

我正在尝试在Networkx中绘制一个由不同类型的字符组成的网络,并希望为每种类型设置不同的节点形状.例如,我希望角色是圆圈,生物是三角形,等等.我试图弄清楚这个问题已经花了几个小时,并进行了广泛的搜索,但是除了找到其他方法之外,我没有找到其他方法为每种类型的字符设置不同的node_list并分别进行渲染,这似乎违反直觉.

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.

问题是我无法从以下位置访问node_shape词典值:

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

nx.draw_networkx_nodes(G, pos) 

我尝试了多种解决方案,包括尝试访问node属性,创建外部字典或列表并从调用中访问它,设置列表推导或迭代器,但似乎无济于事.

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.

我要么传递一个被批发的列表,要么将字典传递给函数无法散列的字典,要么传递诸如 shape_list.pop(0)之类的列表实例,在这种情况下,该函数仅采用第一个值并将其应用于所有节点.

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.

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

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.

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

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.

这是我的代码:

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()

推荐答案

恐怕必须使用多次通过才能完成.

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

主要思想是使用布局来获取节点的位置,然后使用 draw_networkx_nodes 重复为 n 个不同类别的节点.

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.

例如:

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()

最终结果如下:

希望这会有所帮助.

这篇关于使用matplotlib在网络中设置动态节点形状的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆