2015-06-27 28 views
5

第一次在這裏發佈海報,所以請溫柔。 :)用matplotlib在網絡中設置動態節點形狀

我試圖在Networkx中繪製不同類型字符的網絡,並且想要爲每種類型設置不同的節點形狀。例如,我希望角色是圓形,生物是三角形等。我試圖找出這個問題幾個小時,並且已經廣泛搜索過,但是我還沒有找到一種方法來實現這一點,而不是爲每種類型的角色設置不同的node_lists並單獨渲染它們,這似乎是違反直覺的。

的問題是,我無法從訪問node_shape字典值內:

nx.draw_networkx_nodes(G, pos) 

我試過多種解決方案,包括試圖訪問節點的屬性,創建一個外部字典或列表和訪問它在調用中,建立一個列表理解或迭代器,似乎沒有任何工作。

要麼我傳遞一個列表,它是批量拉取的,字典,函數不能散列的字典,或者列表的一個實例,例如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() 
+2

您的一些縮進有錯誤。你可以採取你發佈的內容,並確保複製/粘貼產生可行的代碼? – Joel

+1

所以 - 基本答案(我記得)是每個繪圖命令必須爲每個節點使用相同的形狀。所以如果你想繪製多個不同的形狀,你需要每次調用'draw_networkx_nodes'。它使用'matplotlib.pyplot.scatter',(http://matplotlib.org/api/pyplot_api。html#matplotlib.pyplot.scatter)這就是爲什麼它有這個限制。 – Joel

+0

我更新了代碼。謝謝,喬爾。是否有另一個與Python3一起工作的庫可以做到這一點? – ToferC

回答

3

恐怕這不得不採用多遍來完成。

主要想法是使用layout獲取節點的位置,然後重複使用draw_networkx_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) 

G.add_path([0,2,5]) 
G.add_path([1,4,3,0]) 
G.add_path([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() 

最終結果看起來是這樣的:

enter image description here

希望這有助於。

2

以前的帖子(A_A)略有差異。礦用同樣的東西只有一條畫線,幷包含一些標籤,並顯示一個二部圖。它可能對互聯網上的其他人有用,所以我把它放在這裏。 enter image description here

import networkx as nx 
% matplotlib inline 
from networkx.algorithms import bipartite 
B = nx.Graph() 
B.add_nodes_from(['$x_1$','$x_2$','$x_3$'], s='o', bipartite=0) # Add the node attribute 'bipartite' 
B.add_nodes_from(['$f_a$','$f_b$','$f_c$','$f_d$'], s='s', bipartite=1) 
B.add_edges_from([('$x_1$','$f_a$'),('$x_1$','$f_b$'),('$x_2$','$f_a$'),('$x_2$','$f_b$'),('$x_2$','$f_c$'),('$x_3$','$f_c$'),('$x_3$','$f_d$')]) 

pos = dict() 
X, Y = bipartite.sets(B) 
pos.update((n, (i,1)) for i, n in enumerate(X)) 
pos.update((n, (i+0.5,2)) for i, n in enumerate(Y)) 
nodeShapes = set((aShape[1]["s"] for aShape in B.nodes(data=True))) 

for aShape in nodeShapes: 
    nx.draw(
     B, 
     pos, 
     with_labels=True, 
     node_shape = aShape, 
     node_color = '0.95', 
     node_size=1000, 
     nodelist = [ 
      sNode[0] for sNode in filter(lambda x: x[1]["s"]==aShape, B.nodes(data=True)) 
     ] 
    ) 

plt.savefig("img/15_Graphical_Models_12b.png") # save as png