Python 如何处理PyViz/datashader网络示例中的节点单击?
我查看了and(它也使用datashader)。我想在节点选择上获得对python代码的回调(在节点id上调用函数,即Python 如何处理PyViz/datashader网络示例中的节点单击?,python,graph,callback,datashader,pyviz,Python,Graph,Callback,Datashader,Pyviz,我查看了and(它也使用datashader)。我想在节点选择上获得对python代码的回调(在节点id上调用函数,即fun(node\u id))。如何在PyViz/datashader中实现这一点?我已经做出了很大的努力,用datashader或PyViz实现了这一点,但是,从你如何表达这个问题来看,在networks graph节点上获得回调似乎比具体技术更重要 这是一种基于maccdc2012\u edges网络图的node\u id回调解决方案,使用与指定技术非常类似的NetworkX
fun(node\u id)
)。如何在PyViz/datashader中实现这一点?我已经做出了很大的努力,用datashader或PyViz实现了这一点,但是,从你如何表达这个问题来看,在networks graph节点上获得回调似乎比具体技术更重要
这是一种基于maccdc2012\u edges
网络图的node\u id
回调解决方案,使用与指定技术非常类似的NetworkX
声明的my\u callback(node\u id)
callback,符合您的要求,在单击特定节点时触发
我已经限制在前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()
将熊猫作为pd导入
将networkx导入为nx
将matplotlib.pyplot作为plt导入
进口葡萄
从派拉布进口*
类注释查找器:#感谢http://www.scipy.org/Cookbook/Matplotlib/Interactive_Plotting
"""
单击点时matplotlib访问节点(显示注释)的回调
确定了xtol和ytol内最靠近咔哒声的点。
"""
定义初始化(self、扩展数据、ydata、注释、回调=None、阈值=None、axis=None、xtol=None、ytol=None):
self.data=list(zip(扩展数据、扩展数据、注释))
如果xtol为None:xtol=((最大(扩展数据)-min(扩展数据))/float(len(扩展数据))/2
如果ytol为None:ytol=((最大(ydata)-min(ydata))/float(len(ydata))/2
self.xtol=xtol
伊托尔
如果轴为无:轴=gca()
自定轴=自定轴
self.drawnAnnotations={}
self.links=[]
self.callback=回调
self.threshold=阈值为1.0e-3时的阈值
定义调用(自身、事件):
如果event.inaxes:
单击X=event.xdata
clickY=event.ydata
如果self.axis为None或self.axis==event.inaxes:
注释=[]
最小距离=浮点('inf')
最小距离=浮动('inf')
对于self.data中的x,y,a:
如果abs(clickX-x)参见,其中显示了如何将可单击的抽取节点子集覆盖到更大的datashader渲染网络图上。如果这对你有效,请将其作为你问题的答案发布!还要注意,Datashader是HoloViz.org的一部分,而PyViz涵盖了Python中的所有viz工具。