从Bokeh中的NetworkX Graph获取选定字形

时间:2018-12-03 22:40:24

标签: javascript networkx bokeh bokehjs

我试图从Bokeh中的GraphRender对象获取使用框选择选择的节点的索引,以创建链接的数据表。 (我希望能够获得所选节点的索引)

问题有点类似于:JavaScript callback to get selected glyph index in Bokeh,但是我无法使用他们提出的解决方案来解决它。

下面是完整的代码,我试图通过自定义JS回调解决它,但我无法做到这一点。

非常感谢您的帮助。预先感谢!

(注意:这是我的第一个问题,因此,如果需要更多信息,请告诉我。)

import pandas as pd
import numpy as np

from bokeh.layouts import row, widgetbox, column
from bokeh.models import ColumnDataSource, CustomJS, StaticLayoutProvider, Oval, Circle
from bokeh.models import HoverTool, TapTool, BoxSelectTool, GraphRenderer
from bokeh.models.widgets import RangeSlider, Button, DataTable, TableColumn, NumberFormatter
from bokeh.io import curdoc, show, output_notebook
from bokeh.plotting import figure

import networkx as nx

from bokeh.io import show, output_file
from bokeh.plotting import figure
from bokeh.models.graphs import from_networkx, NodesAndLinkedEdges, EdgesAndLinkedNodes, NodesOnly

# Import / instantiate networkx graph
G = nx.Graph()

G.add_edge('a', 'b', weight=0.6)
G.add_edge('a', 'c', weight=0.2)
G.add_edge('c', 'd', weight=0.1)
G.add_edge('c', 'e', weight=0.7)
G.add_edge('c', 'f', weight=0.9)
G.add_edge('a', 'd', weight=0.3)

# Node Characteristics
node_name = list(G.nodes())
positions = nx.spring_layout(G)

node_size = [k*4 for k in range(len(G.nodes()))]
nx.set_node_attributes(G, node_size, 'node_size')
visual_attributes=ColumnDataSource(
    pd.DataFrame.from_dict({k:v for k,v in G.nodes(data=True)},orient='index'))

# Edge characteristics
start_edge = [start_edge for (start_edge, end_edge) in G.edges()]
end_edge = [end_edge for (start_edge, end_edge) in G.edges()]
weight = list(nx.get_edge_attributes(G,'weight').values())

edge_df = pd.DataFrame({'source':start_edge, 'target':end_edge, 'weight':weight})

# Create full graph from edgelist 
G = nx.from_pandas_edgelist(edge_df,edge_attr=True)

# Convert full graph to Bokeh network for node coordinates and instantiate Bokeh graph object 
G_source = from_networkx(G, nx.spring_layout, scale=2, center=(0,0))
graph = GraphRenderer()

# Update loop where the magic happens
def update():
    selected_df = edge_df[(edge_df['weight'] >= slider.value[0]) & (edge_df['weight'] <= slider.value[1])]
    sub_G = nx.from_pandas_edgelist(selected_df,edge_attr=True)
    sub_graph = from_networkx(sub_G, nx.spring_layout, scale=2, center=(0,0))
    graph.edge_renderer.data_source.data = sub_graph.edge_renderer.data_source.data
    graph.node_renderer.data_source.data = G_source.node_renderer.data_source.data
    graph.node_renderer.data_source.add(node_size,'node_size')

def selected_points(attr,old,new):
    selected_idx = graph.node_renderer.selected.indices #does not work
    print(selected_idx)

# Slider which changes values to update the graph
slider = RangeSlider(title="Weights", start=0, end=1, value=(0.25, 0.75), step=0.10)
slider.on_change('value', lambda attr, old, new: update())

# Plot object which is updated 
plot = figure(title="Meetup Network Analysis", x_range=(-1.1,1.1), y_range=(-1.1,1.1),
             tools = "pan,wheel_zoom,box_select,reset,box_zoom,crosshair", plot_width=800, plot_height=800)

# Assign layout for nodes, render graph, and add hover tool
graph.layout_provider = StaticLayoutProvider(graph_layout=positions)
graph.node_renderer.glyph = Circle(size='node_size')
graph.selection_policy = NodesOnly()
plot.renderers.append(graph)
plot.tools.append(HoverTool(tooltips=[('Name', '@index')]))

# Set layout
layout = column(slider,plot)

# does not work
#graph.node_renderer.data_source.on_change("selected", selected_points)

# Create Bokeh server object
curdoc().add_root(layout)
update()

1 个答案:

答案 0 :(得分:0)

与其将侦听器放在graph.node_renderer.data_source.on_change上,不如使用它:

graph.node_renderer.data_source.selected.on_change("indices", selected_points)

这将触发服务器端响应。

洪逸