我有一个滑块,它将限制绘图中看到的节点数量。但是,使用滑块时,工具提示检查节点属性将停止运行并显示???
。
此外,使用滑块时,节点的颜色无法正常工作。在Chrome中检查控制台时,会出现以下错误:Uncaught Error: attempted to retrieve property array for nonexistent field 'node_colors'
。我相信这是由于传递到node_renderer.glyph
代码中的数组长度不匹配。当前着色将所有source
值标记为绿色,target
值标记为蓝色。
所描述的解决方案的完整代码如下所示:
import networkx as nx
from bokeh.io import show, output_file
from bokeh.models import Plot, Range1d, MultiLine, Circle, TapTool, OpenURL, HoverTool, CustomJS, Slider, Column
from bokeh.models.graphs import from_networkx, EdgesAndLinkedNodes
from bokeh.palettes import Spectral4
from dask.dataframe.core import DataFrame
import pandas as pd
data = {'source': ['A', 'A', 'A', 'B', 'B', 'B'], 'target': ['C', 'D', 'E', 'F', 'G', 'H'], 'source_count': [15, 15, 15, 25, 25, 25], 'target_count': [10, 20, 30, 10, 20, 30]}
df = pd.DataFrame(data)
net_graph = nx.from_pandas_edgelist(df, 'source', 'target')
for index, row in df.iterrows():
net_graph.nodes[row['source']]['yearly_count'] = row['source_count']
net_graph.nodes[row['target']]['yearly_count'] = row['target_count']
node_colors = []
for node in net_graph:
if node in df["source"].values:
node_colors.append("green")
else: node_colors.append("maroon")
graph_plot = Plot(plot_width = 800, plot_height = 600, x_range = Range1d(-1.1, 1.1), y_range = Range1d(-1.1, 1.1))
node_hover_tool = HoverTool(tooltips = [("Name", "@index"), ("Yearly Count", "@yearly_count")])
graph_plot.add_tools(node_hover_tool)
graph_setup = from_networkx(net_graph, nx.spring_layout, scale = 1, center = (0, 0))
graph_setup.node_renderer.data_source.data['node_colors'] = node_colors
graph_setup.node_renderer.glyph = Circle(size = 20, fill_color = 'node_colors')
graph_setup.edge_renderer.glyph = MultiLine(line_color = "red", line_alpha = 0.8, line_width = 1)
graph_plot.renderers.append(graph_setup)
code = """
var new_start = start.slice();
var new_end = end.slice();
new_index = end.slice();
new_start = new_start.splice(0, cb_obj.value)
new_end = new_end.splice(0, cb_obj.value)
new_index = ['A','B'].concat(new_end)
new_data_edge = {'start': new_start, 'end': new_end};
new_data_nodes = {'index': new_index};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.node_renderer.data_source.data = new_data_nodes;
"""
callback = CustomJS(args = dict(graph_setup = graph_setup,
start = df['source'].values,
end = df['target'].values), code = code)
slider = Slider(title = 'Slider', start = 0, end = 6, value = 6)
slider.js_on_change('value', callback)
layout = Column(graph_plot, slider)
show(layout)
示例代码中包含的数据是整个DataFrame的片段。
任何人都可以提供的任何帮助将不胜感激。
请用以下代码替换您的回调代码:
code = """
var new_start = start.slice();
var new_end = end.slice();
var new_index = ndata['index'].slice();
var new_node_colors = ndata['node_colors'].slice();
var new_yearly_count = ndata['yearly_count'].slice();
new_start = new_start.splice(0, cb_obj.value)
new_end = new_end.splice(0, cb_obj.value)
new_data_edge = {'start': new_start, 'end': new_end};
new_data_nodes = {};
new_data_nodes['index'] = new_index.splice(0, cb_obj.value);
new_data_nodes['node_colors'] = new_node_colors.splice(0, cb_obj.value);
new_data_nodes['yearly_count'] = new_yearly_count.splice(0, cb_obj.value);
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.node_renderer.data_source.data = new_data_nodes;
"""
并将其添加到您的Python代码中:
import copy
backup_node_data = copy.deepcopy(graph_setup.node_renderer.data_source.data)
callback = CustomJS(args = dict(graph_setup = graph_setup,
start = df['source'].values,
end = df['target'].values,
ndata = backup_node_data),
code = code)
或者用以下代码替换整个代码:
import networkx as nx
from bokeh.io import show, output_file
from bokeh.models import Plot, Range1d, MultiLine, Circle, TapTool, OpenURL, HoverTool, CustomJS, Slider, Column
from bokeh.models.graphs import from_networkx, EdgesAndLinkedNodes
from bokeh.palettes import Spectral4
from dask.dataframe.core import DataFrame
import pandas as pd
import copy
data = {'source': ['A', 'A', 'A', 'B', 'B', 'B'], 'target': ['C', 'D', 'E', 'F', 'G', 'H'], 'source_count': [15, 15, 15, 25, 25, 25], 'target_count': [10, 20, 30, 10, 20, 30]}
df = pd.DataFrame(data)
net_graph = nx.from_pandas_edgelist(df, 'source', 'target')
for index, row in df.iterrows():
net_graph.nodes[row['source']]['yearly_count'] = row['source_count']
net_graph.nodes[row['target']]['yearly_count'] = row['target_count']
node_colors = []
for node in net_graph:
if node in df["source"].values:
node_colors.append("green")
else:
node_colors.append("maroon")
graph_plot = Plot(plot_width = 800, plot_height = 600, x_range = Range1d(-1.1, 1.1), y_range = Range1d(-1.1, 1.1))
node_hover_tool = HoverTool(tooltips = [("Name", "@index"), ("Yearly Count", "@yearly_count")], show_arrow = False)
graph_plot.add_tools(node_hover_tool)
graph_setup = from_networkx(net_graph, nx.spring_layout, scale = 1, center = (0, 0))
graph_setup.node_renderer.data_source.data['node_colors'] = node_colors
graph_setup.node_renderer.glyph = Circle(size = 20, fill_color = 'node_colors')
graph_setup.edge_renderer.glyph = MultiLine(line_color = "red", line_alpha = 0.8, line_width = 1)
graph_plot.renderers.append(graph_setup)
backup_node_data = copy.deepcopy(graph_setup.node_renderer.data_source.data)
code = """
var new_start = start.slice();
var new_end = end.slice();
var new_index = ndata['index'].slice();
var new_node_colors = ndata['node_colors'].slice();
var new_yearly_count = ndata['yearly_count'].slice();
new_start = new_start.splice(0, cb_obj.value)
new_end = new_end.splice(0, cb_obj.value)
new_data_edge = {'start': new_start, 'end': new_end};
new_data_nodes = {};
new_data_nodes['index'] = new_index.splice(0, cb_obj.value);
new_data_nodes['node_colors'] = new_node_colors.splice(0, cb_obj.value);
new_data_nodes['yearly_count'] = new_yearly_count.splice(0, cb_obj.value);
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.node_renderer.data_source.data = new_data_nodes;
"""
callback = CustomJS(args = dict(graph_setup = graph_setup,
start = df['source'].values,
end = df['target'].values,
ndata = backup_node_data),
code = code)
slider = Slider(title = 'Slider', start = 0, end = 8, value = 8)
slider.js_on_change('value', callback)
layout = Column(graph_plot, slider)
show(layout)
结果:
另一个始终离开中心节点的版本:
import networkx as nx
from bokeh.io import show, output_file
from bokeh.models import Plot, Range1d, MultiLine, Circle, TapTool, OpenURL, HoverTool, CustomJS, Slider, Column
from bokeh.models.graphs import from_networkx, EdgesAndLinkedNodes
from bokeh.palettes import Spectral4
from dask.dataframe.core import DataFrame
import pandas as pd
import copy
data = {'source': ['A', 'A', 'A', 'B', 'B', 'B'], 'target': ['C', 'D', 'E', 'F', 'G', 'H'], 'source_count': [15, 15, 15, 25, 25, 25], 'target_count': [10, 20, 30, 10, 20, 30]}
df = pd.DataFrame(data)
net_graph = nx.from_pandas_edgelist(df, 'source', 'target')
for index, row in df.iterrows():
net_graph.nodes[row['source']]['yearly_count'] = row['source_count']
net_graph.nodes[row['target']]['yearly_count'] = row['target_count']
node_colors = []
for node in net_graph:
if node in df["source"].values:
node_colors.append("green")
else:
node_colors.append("maroon")
graph_plot = Plot(plot_width = 800, plot_height = 600, x_range = Range1d(-1.1, 1.1), y_range = Range1d(-1.1, 1.1))
node_hover_tool = HoverTool(tooltips = [("Name", "@index"), ("Yearly Count", "@yearly_count")])
graph_plot.add_tools(node_hover_tool)
graph_setup = from_networkx(net_graph, nx.spring_layout, scale = 1, center = (0, 0))
graph_setup.node_renderer.data_source.data['node_colors'] = node_colors
graph_setup.node_renderer.glyph = Circle(size = 20, fill_color = 'node_colors')
graph_setup.edge_renderer.glyph = MultiLine(line_color = "red", line_alpha = 0.8, line_width = 1)
graph_plot.renderers.append(graph_setup)
a_index = graph_setup.node_renderer.data_source.data['index'].index("A")
b_index = graph_setup.node_renderer.data_source.data['index'].index("B")
if a_index != 0:
index_item = graph_setup.node_renderer.data_source.data[field][a_index]
new_data = graph_setup.node_renderer.data_source.data[field][0:a_index] + graph_setup.node_renderer.data_source.data[field][a_index + 1:]
new_data.insert(0, index_item)
graph_setup.node_renderer.data_source.data[field] = new_data
if b_index != 1:
for field in graph_setup.node_renderer.data_source.data:
index_item = graph_setup.node_renderer.data_source.data[field][b_index]
new_data = graph_setup.node_renderer.data_source.data[field][0:b_index] + graph_setup.node_renderer.data_source.data[field][b_index + 1:]
new_data.insert(1, index_item)
graph_setup.node_renderer.data_source.data[field] = new_data
backup_node_data = copy.deepcopy(graph_setup.node_renderer.data_source.data)
backup_edge_data = copy.deepcopy(graph_setup.edge_renderer.data_source.data)
code = """
var new_start = start.slice();
var new_end = end.slice();
var new_index = ndata['index'].slice();
var new_node_colors = ndata['node_colors'].slice();
var new_yearly_count = ndata['yearly_count'].slice();
new_start = new_start.splice(0, cb_obj.value)
new_end = new_end.splice(0, cb_obj.value)
new_data_edge = {'start': new_start, 'end': new_end};
new_data_nodes = {};
new_data_nodes['index'] = new_index.splice(0, cb_obj.value);
new_data_nodes['node_colors'] = new_node_colors.splice(0, cb_obj.value);
new_data_nodes['yearly_count'] = new_yearly_count.splice(0, cb_obj.value);
console.log(new_data_edge)
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.node_renderer.data_source.data = new_data_nodes;
graph_setup.edge_renderer.data_source.change.emit();
graph_setup.node_renderer.data_source.change.emit();
"""
callback = CustomJS(args = dict(graph_setup = graph_setup,
start = df['source'].values,
end = df['target'].values,
ndata = backup_node_data,
edata = backup_edge_data),
code = code)
slider = Slider(title = 'Slider', start = 2, end = 8, value = 8)
slider.js_on_change('value', callback)
layout = Column(graph_plot, slider)
show(layout)