如何解决如何处理PyViz / datashader网络示例中的节点单击?
我查看了datashader/Networks和PyViz/network_packets(它们也使用了数据着色器)。我想在选择节点时获得python代码的回调(在节点ID上调用函数,即fun(node_id)
)。如何在PyViz / datashader中完成此操作?
解决方法
我已经尽力用datashader或PyViz来实现它,但是从您如何表达这个问题看来,在网络图节点上获得回调比对特定技术更为重要。 >
这是一种基于node_id
网络图的maccdc2012_edges
回调的解决方案,它使用与指定技术非常相似的NetworkX。
已声明的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,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,a) )
smallest_x_dist=abs(clickX-x)
smallest_y_dist=abs(clickY-y)
if annotes:
annotes.sort() # to select the nearest node
distance,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,annotes=[],[],[] for key in pos:
x,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,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.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()
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。