2015-06-27 16 views
5

Áp phích đầu tiên ở đây, vì vậy hãy nhẹ nhàng. :)Đặt hình dạng nút động trong mạng với matplotlib

Tôi đang cố gắng để vẽ đồ thị một mạng lưới các nhân vật của các loại khác nhau trong Networkx và muốn thiết lập hình dạng nút khác nhau đối với từng loại. Ví dụ, tôi muốn nhân vật là vòng tròn, sinh vật là hình tam giác, v.v. Tôi đã cố gắng tìm ra điều này trong vài giờ và đã tìm kiếm SO rộng rãi, nhưng tôi đã không tìm thấy cách để đạt được điều này khác hơn là đặt các node_lists khác nhau cho từng loại ký tự và hiển thị chúng một cách riêng biệt, điều này dường như không trực quan.

Vấn đề là tôi không thể truy cập vào các giá trị điển node_shape từ bên trong:

nx.draw_networkx_nodes(G, pos) 

Tôi đã thử nhiều giải pháp trong đó có cố gắng truy cập các thuộc tính nút, tạo ra một từ điển bên ngoài hoặc danh sách và truy cập nó từ bên trong cuộc gọi, thiết lập một danh sách hiểu hoặc lặp và không có gì có vẻ làm việc.

Hoặc tôi chuyển danh sách được kéo vào bán buôn, từ điển, chức năng không thể băm, hoặc thể hiện của danh sách như shape_list.pop(0), trong trường hợp đó, hàm chỉ lấy giá trị đầu tiên và áp dụng nó cho tất cả các nút.

Tôi có thể đặt màu bằng cách tạo danh sách node_colors riêng biệt được lặp lại bởi hàm và thậm chí đã thử tạo từ điển để node_shape được kích hoạt bởi node_color, nhưng điều đó cũng không hoạt động.

Tôi đang hy vọng sẽ sử dụng mã như là một add-on cho một ứng dụng web phát triển bằng Python 3.4 và Django 1,8, vì vậy Graphviz không phải là một lựa chọn.

Cảm ơn bạn trước vì đã hỗ trợ hoặc tham khảo các thư viện thay thế.

Đây là mã của tôi:

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

một số lỗi thụt lề của bạn có lỗi. Bạn có thể lấy những gì bạn đã đăng và đảm bảo rằng sao chép/dán tạo ra mã khả thi? – Joel

+1

Vì vậy, câu trả lời cơ bản (như tôi nhớ lại) là mỗi lệnh vẽ đồ thị phải sử dụng cùng một hình dạng cho mỗi nút. Vì vậy, nếu bạn muốn vẽ nhiều hình dạng khác nhau, bạn sẽ cần phải gọi 'draw_networkx_nodes' mỗi lần. Nó sử dụng 'matplotlib.pyplot.scatter', (http://matplotlib.org/api/pyplot_api.html # matplotlib.pyplot.scatter) đó là lý do tại sao nó có hạn chế này. – Joel

+0

Tôi đã cập nhật mã. Cảm ơn, Joel. Có một thư viện khác làm việc với Python3 có thể làm điều này không? – ToferC

Trả lời

3

tôi sợ rằng điều này sẽ phải được thực hiện bằng nhiều đèo.

Ý tưởng chính là sử dụng layout để nhận vị trí của các nút và sau đó sử dụng draw_networkx_nodes nhiều lần cho n các lớp nút khác nhau.

Ví dụ:

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

kết quả cuối cùng trông giống như sau:

enter image description here

Hope this helps.

2

Dưới đây là một biến thể nhỏ trên bài đăng trước đó (bởi A_A). Mỏ sử dụng nhiều thứ tương tự chỉ đặt một đường vẽ và bao gồm một số nhãn, và hiển thị biểu đồ hai bên. Nó có thể hữu ích cho người khác trên internet vì vậy tôi đang đặt nó lên đây. 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 
Các vấn đề liên quan