如何通过networkx制作可点击的图表?
Posted
技术标签:
【中文标题】如何通过networkx制作可点击的图表?【英文标题】:How to make a click-able graph by networkx? 【发布时间】:2012-10-05 09:20:31 【问题描述】:我正在尝试在 python 中生成可点击的图形图像。 一开始直接调用graphviz,后来发现networkxhttp://networkx.lanl.gov。
我希望我的程序能够获取有关在用户单击图形的 (x,y) 坐标处显示哪个节点的信息。 我想我可以使用打开并显示图形的 pyplot 窗口,在鼠标单击时使用 (x,y) 坐标,但我需要某种图像映射来知道哪个 节点已在该坐标处可视化!
你能告诉我是否/如何做到吗?
【问题讨论】:
【参考方案1】:感谢http://groups.google.com/group/networkx-discuss 的好心人,我解决了这个问题 (http://groups.google.com/group/networkx-discuss/browse_thread/thread/aac227e1fb2a4719):
以下(部分)代码在 Tkinter 中工作,允许创建包含 networkx 图的 matplotlib 窗口(顺便说一句,非阻塞),并在您单击给定节点时执行过程 visitNode()。
import networkx as nx
import matplotlib.pyplot as plt
import pylab
class AnnoteFinder: # thanks to http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
"""
callback for matplotlib to visit a node (display an annotation) when points are clicked on. The
point which is closest to the click and within xtol and ytol is identified.
"""
def __init__(self, xdata, ydata, annotes, axis=None, xtol=None, ytol=None):
self.data = zip(xdata, ydata, annotes)
if xtol is None: xtol = ((max(xdata) - min(xdata))/float(len(xdata)))/2
if ytol is None: ytol = ((max(ydata) - min(ydata))/float(len(ydata)))/2
self.xtol = xtol
self.ytol = ytol
if axis is None: axis = pylab.gca()
self.axis= axis
self.drawnAnnotations =
self.links = []
def __call__(self, event):
if event.inaxes:
clickX = event.xdata
clickY = event.ydata
if self.axis is None or self.axis==event.inaxes:
annotes = []
for x,y,a in self.data:
if clickX-self.xtol < x < clickX+self.xtol and clickY-self.ytol < y < clickY+self.ytol :
dx,dy=x-clickX,y-clickY
annotes.append((dx*dx+dy*dy,x,y, a) )
if annotes:
annotes.sort() # to select the nearest node
distance, x, y, annote = annotes[0]
self.visitNode(annote)
def visitNode(self, annote): # Visit the selected node
# do something with the annote value
print "visitNode", annote
fig = plt.figure() ax = fig.add_subplot(111) ax.set_title('select nodes to navigate there')
G=nx.MultiDiGraph() # directed graph G = nx.wheel_graph(5)
pos=nx.spring_layout(G) # the layout gives us the nodes position x,y,annotes=[],[],[] for key in pos:
d=pos[key]
annotes.append(key)
x.append(d[0])
y.append(d[1]) nx.draw(G,pos,font_size=8)
af = AnnoteFinder(x,y, annotes) fig.canvas.mpl_connect('button_press_event', af)
plt.show()
【讨论】:
很抱歉,粘贴带有未定义名称和损坏标识的不完整/无效代码有什么意义?它会帮助你以外的人吗? @minerals 我已提交修改以更正您提到的问题【参考方案2】:我在@minerals 发布的解决方案中发现了几个不完整的代码段。在这里,我发布了 python3 的工作代码段
import networkx as nx
import matplotlib.pyplot as plt
import graphistry
from pylab import *
class AnnoteFinder: # thanks to http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
"""
callback for matplotlib to visit a node (display an annotation) when points are clicked on. The
point which is closest to the click and within xtol and ytol is identified.
"""
def __init__(self, xdata, ydata, annotes, axis=None, xtol=None, ytol=None):
self.data = list(zip(xdata, ydata, annotes))
if xtol is None: xtol = ((max(xdata) - min(xdata))/float(len(xdata)))/2
if ytol is None: ytol = ((max(ydata) - min(ydata))/float(len(ydata)))/2
self.xtol = xtol
self.ytol = ytol
if axis is None: axis = gca()
self.axis= axis
self.drawnAnnotations =
self.links = []
def __call__(self, event):
if event.inaxes:
clickX = event.xdata
clickY = event.ydata
print(dir(event),event.key)
if self.axis is None or self.axis==event.inaxes:
annotes = []
smallest_x_dist = float('inf')
smallest_y_dist = float('inf')
for x,y,a in self.data:
if abs(clickX-x)<=smallest_x_dist and abs(clickY-y)<=smallest_y_dist :
dx, dy = x - clickX, y - clickY
annotes.append((dx*dx+dy*dy,x,y, a) )
smallest_x_dist=abs(clickX-x)
smallest_y_dist=abs(clickY-y)
print(annotes,'annotate')
# if clickX-self.xtol < x < clickX+self.xtol and clickY-self.ytol < y < clickY+self.ytol :
# dx,dy=x-clickX,y-clickY
# annotes.append((dx*dx+dy*dy,x,y, a) )
print(annotes,clickX,clickY,self.xtol,self.ytol )
if annotes:
annotes.sort() # to select the nearest node
distance, x, y, annote = annotes[0]
self.drawAnnote(event.inaxes, x, y, annote)
def drawAnnote(self, axis, x, y, annote):
if (x, y) in self.drawnAnnotations:
markers = self.drawnAnnotations[(x, y)]
for m in markers:
m.set_visible(not m.get_visible())
self.axis.figure.canvas.draw()
else:
t = axis.text(x, y, "%s" % (annote), )
m = axis.scatter([x], [y], marker='d', c='r', zorder=100)
self.drawnAnnotations[(x, y)] = (t, m)
self.axis.figure.canvas.draw()
df = pd.DataFrame("LOAD YOUR DATA")
# Build your graph
G = nx.from_pandas_edgelist(df, 'from', 'to')
pos = nx.spring_layout(G,k=0.1, iterations=20) # the layout gives us the nodes position x,y,annotes=[],[],[] for key in pos:
x, y, annotes = [], [], []
for key in pos:
d = pos[key]
annotes.append(key)
x.append(d[0])
y.append(d[1])
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)
ax.set_title('select nodes to navigate there')
nx.draw(G, pos, font_size=6,node_color='#A0CBE2', edge_color='#BB0000', width=0.1,
node_size=2,with_labels=True)
af = AnnoteFinder(x, y, annotes)
connect('button_press_event', af)
show()
【讨论】:
【参考方案3】:[更新亚历山德罗的回答]
这是 alessandro 对 python 3 的更新答案。
注意不推荐使用pylab。
import networkx as nx
import matplotlib.pyplot as plt
import pylab
class AnnoteFinder: # thanks to http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
"""
callback for matplotlib to visit a node (display an annotation) when points are clicked on. The
point which is closest to the click and within xtol and ytol is identified.
"""
def __init__(self, xdata, ydata, annotes2, axis=None, xtol=None, ytol=None):
self.data = zip(xdata, ydata, annotes2)
if xtol is None:
xtol = ((max(xdata) - min(xdata)) / float(len(xdata))) / 2
if ytol is None:
ytol = ((max(ydata) - min(ydata)) / float(len(ydata))) / 2
self.xtol = xtol
self.ytol = ytol
if axis is None:
axis = pylab.gca()
self.axis = axis
self.drawnAnnotations =
self.links = []
def __call__(self, event):
if event.inaxes:
clickX = event.xdata
clickY = event.ydata
if self.axis is None or self.axis == event.inaxes:
annotes2 = []
for x2, y2, a in self.data:
if clickX - self.xtol < x2 < clickX + self.xtol and clickY - self.ytol < y2 < clickY + self.ytol:
dx, dy = x2 - clickX, y2 - clickY
annotes2.append((dx * dx + dy * dy, x2, y2, a))
if annotes2:
annotes2.sort() # to select the nearest node
distance, x2, y2, annote = annotes2[0]
self.visitnode(annote)
def visitNode(self, annote): # Visit the selected node
# do something with the annote value
print("visitNode", annote)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('select nodes to navigate there')
G = nx.MultiDiGraph() # directed graph G = nx.wheel_graph(5)
pos = nx.spring_layout(G) # the layout gives us the nodes position
x, y, annotes = [], [], []
for key in pos:
d = pos[key]
annotes.append(key)
x.append(d[0])
y.append(d[1])
nx.draw(G, pos, font_size=8)
af = AnnoteFinder(x, y, annotes)
fig.canvas.mpl_connect('button_press_event', af)
plt.show()
【讨论】:
以上是关于如何通过networkx制作可点击的图表?的主要内容,如果未能解决你的问题,请参考以下文章