2024-05-18 23:41:31 发布
网友
我研究了datashader/Networks和PyViz/network_packets(也使用datashader)。我希望在节点选择上获得对python代码的回调(调用节点id上的函数,即fun(node_id))。如何在PyViz/datashader中实现这一点
fun(node_id)
我已经花了很大的力气用datashader或PyViz来实现它,但是,从你如何表达这个问题来看,在networks graph节点上获得回调似乎比具体的技术更重要
这是一个基于maccdc2012_edges网络图的node_id回调解决方案,使用与指定技术非常类似的NetworkX
maccdc2012_edges
node_id
声明的my_callback(node_id)回调,根据您的要求,在单击特定节点时触发
my_callback(node_id)
我已经限制在前10行,以便它可以清楚地看到,并添加了滚动缩放为您的方便
import pandas as pd 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, callback = None, threshold=None, 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 = [] self.callback = callback self.threshold = threshold if threshold else 1.0e-3 def __call__(self, event): if event.inaxes: clickX = event.xdata clickY = event.ydata 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) if annotes: annotes.sort() # to select the nearest node distance, x, y, annote = annotes[0] print(distance) if distance < self.threshold: if self.callback: self.callback(annote) # https://notebooks.azure.com/seanreed1111/projects/PYVIZ1/html/data/maccdc2012_edges.parq df = pd.read_parquet('maccdc2012_edges.parq').head(10) def my_callback(node_id): print(f'Clicked {node_id}') # Build your graph G = nx.from_pandas_edgelist(df, 'source', 'target') 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) nx.draw(G, pos, font_size=6, node_color='skyblue', edge_color='#BB0000', width=0.5, node_size=200, with_labels=True) af = AnnoteFinder(x, y, annotes, my_callback) connect('button_press_event', af) class ZoomPan: def __init__(self): self.press = None self.cur_xlim = None self.cur_ylim = None self.x0 = None self.y0 = None self.x1 = None self.y1 = None self.xpress = None self.ypress = None def zoom_factory(self, ax, base_scale = 2.): def zoom(event): cur_xlim = ax.get_xlim() cur_ylim = ax.get_ylim() xdata = event.xdata # get event x location ydata = event.ydata # get event y location if event.button == 'down': # deal with zoom in scale_factor = 1 / base_scale elif event.button == 'up': # deal with zoom out scale_factor = base_scale else: # deal with something that should never happen scale_factor = 1 print(event.button) new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0]) rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0]) ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)]) ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)]) ax.figure.canvas.draw() fig = ax.get_figure() # get the figure of interest fig.canvas.mpl_connect('scroll_event', zoom) return zoom def pan_factory(self, ax): def onPress(event): if event.inaxes != ax: return self.cur_xlim = ax.get_xlim() self.cur_ylim = ax.get_ylim() self.press = self.x0, self.y0, event.xdata, event.ydata self.x0, self.y0, self.xpress, self.ypress = self.press def onRelease(event): self.press = None ax.figure.canvas.draw() def onMotion(event): if self.press is None: return if event.inaxes != ax: return dx = event.xdata - self.xpress dy = event.ydata - self.ypress self.cur_xlim -= dx self.cur_ylim -= dy ax.set_xlim(self.cur_xlim) ax.set_ylim(self.cur_ylim) ax.figure.canvas.draw() fig = ax.get_figure() # get the figure of interest # attach the call back fig.canvas.mpl_connect('button_press_event',onPress) fig.canvas.mpl_connect('button_release_event',onRelease) fig.canvas.mpl_connect('motion_notify_event',onMotion) #return the function return onMotion scale = 1.1 zp = ZoomPan() figZoom = zp.zoom_factory(ax, base_scale = scale) figPan = zp.pan_factory(ax) show()
免责声明:此解决方案是基于以下线程的调整:12
我已经花了很大的力气用datashader或PyViz来实现它,但是,从你如何表达这个问题来看,在networks graph节点上获得回调似乎比具体的技术更重要
这是一个基于
maccdc2012_edges
网络图的node_id
回调解决方案,使用与指定技术非常类似的NetworkX声明的
my_callback(node_id)
回调,根据您的要求,在单击特定节点时触发我已经限制在前10行,以便它可以清楚地看到,并添加了滚动缩放为您的方便
免责声明:此解决方案是基于以下线程的调整:12
相关问题 更多 >
编程相关推荐