Bokeh Networkx 图形滑块未正确更新
Bokeh Networkx graph slider not updating correctly
这是我面临的问题的最小工作示例:
我正在使用 Networkx 构建一个简单的图形,然后使用 Bokeh 显示它,添加一个滑块以仅显示权重大于滑块值的边。
不幸的是,当值增加时,这非常有效,即滑块向右移动,而当滑块值减小时它停止工作(一些边缘重新出现,但随后,当单击图形时,一切都爆炸了)。
在 customJS 回调函数中,我正在修改边缘数据,并且在将它的每一部分打印到控制台时,它们都按预期工作,但在浏览器控制台中我得到一个 Shape Mismatch 错误,即使未指定正在比较的两个形状。
import pandas as pd
import networkx as nx
from bokeh.io import show
from bokeh.plotting import figure, from_networkx
from bokeh.models import CustomJS, Slider
from bokeh.layouts import row, column
import copy
df = pd.DataFrame(data={'Source': {0: 'A', 1: 'A', 2: 'A', 3: 'B', 4: 'B', 5: 'C'},
'Target': {0: 'B', 1: 'C', 2: 'D', 3: 'C', 4: 'D', 5: 'D'},
'Weight': {0: 0, 1: 1, 2: 1.5, 3: 0.6, 4: 3, 5: 4}})
G = nx.from_pandas_edgelist(df, 'Source', 'Target', 'Weight')
plot = figure(title='Attempt')
network_graph = from_networkx(G, nx.circular_layout, scale=1, center=(0, 0))
plot.renderers.append(network_graph)
# save edge data to select only a subset of the edges
backup_edge_data = copy.deepcopy(network_graph.edge_renderer.data_source.data)
slider = Slider(start=0, end=4, value=0, step=.2)
# the last line of this object (the one with change.emit()) is probably unnecessary
code = """
const old_Weight = edata["Weight"];
const old_start = edata["start"];
const old_end = edata["end"];
let acceptableIndexes = old_Weight.reduce(function(acc, curr, index) {
if (curr >= cb_obj.value) {
acc.push(index);
}
return acc;
}, []);
const new_Weight = acceptableIndexes.map(i => old_Weight[i]);
const new_start = acceptableIndexes.map(i => old_start[i]);
const new_end = acceptableIndexes.map(i => old_end[i]);
const new_data_edge = {'Weight': new_Weight,
'start': new_start,
'end': new_end};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.edge_renderer.data_source.change.emit();
"""
callback = CustomJS(args = dict(graph_setup = network_graph,
edata = backup_edge_data),
code = code)
slider.js_on_change('value', callback)
layout = row(
plot,
column(slider),
)
show(layout)
image of the networkx graph and the slider
我发现这实际上是由于 bug 造成的:
https://discourse.bokeh.org/t/dynamic-layout-behavior-changes-between-bokeh-2-2-3-and-bokeh-2-3-0/7594
为了在 bokeh 库未更新之前解决此问题(我使用的是 2.4.0 版),我修改了 customJS 代码以添加占位符数据以匹配初始数据源维度:
code = """
const old_Weight = edata["Weight"];
const old_start = edata["start"];
const old_end = edata["end"];
let acceptableIndexes = old_Weight.reduce(function(acc, curr, index) {
if (curr >= cb_obj.value) {
acc.push(index);
}
return acc;
}, []);
\ compute how many fake edges have to be added
const num_ph = old_Weight.length - acceptableIndexes.length
\ create an array of that dimension with fake value '9999'
const placeholder = Array(num_ph).fill('9999')
\ for each new value, concatenate the new array with the placeholder array
const new_Weight = acceptableIndexes.map(i => old_Weight[i]).concat(placeholder);
const new_start = acceptableIndexes.map(i => old_start[i]).concat(placeholder);
const new_end = acceptableIndexes.map(i => old_end[i]).concat(placeholder);
const new_data_edge = {'Weight': new_Weight,
'start': new_start,
'end': new_end};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.edge_renderer.data_source.change.emit();
"""
这是我面临的问题的最小工作示例:
我正在使用 Networkx 构建一个简单的图形,然后使用 Bokeh 显示它,添加一个滑块以仅显示权重大于滑块值的边。 不幸的是,当值增加时,这非常有效,即滑块向右移动,而当滑块值减小时它停止工作(一些边缘重新出现,但随后,当单击图形时,一切都爆炸了)。 在 customJS 回调函数中,我正在修改边缘数据,并且在将它的每一部分打印到控制台时,它们都按预期工作,但在浏览器控制台中我得到一个 Shape Mismatch 错误,即使未指定正在比较的两个形状。
import pandas as pd
import networkx as nx
from bokeh.io import show
from bokeh.plotting import figure, from_networkx
from bokeh.models import CustomJS, Slider
from bokeh.layouts import row, column
import copy
df = pd.DataFrame(data={'Source': {0: 'A', 1: 'A', 2: 'A', 3: 'B', 4: 'B', 5: 'C'},
'Target': {0: 'B', 1: 'C', 2: 'D', 3: 'C', 4: 'D', 5: 'D'},
'Weight': {0: 0, 1: 1, 2: 1.5, 3: 0.6, 4: 3, 5: 4}})
G = nx.from_pandas_edgelist(df, 'Source', 'Target', 'Weight')
plot = figure(title='Attempt')
network_graph = from_networkx(G, nx.circular_layout, scale=1, center=(0, 0))
plot.renderers.append(network_graph)
# save edge data to select only a subset of the edges
backup_edge_data = copy.deepcopy(network_graph.edge_renderer.data_source.data)
slider = Slider(start=0, end=4, value=0, step=.2)
# the last line of this object (the one with change.emit()) is probably unnecessary
code = """
const old_Weight = edata["Weight"];
const old_start = edata["start"];
const old_end = edata["end"];
let acceptableIndexes = old_Weight.reduce(function(acc, curr, index) {
if (curr >= cb_obj.value) {
acc.push(index);
}
return acc;
}, []);
const new_Weight = acceptableIndexes.map(i => old_Weight[i]);
const new_start = acceptableIndexes.map(i => old_start[i]);
const new_end = acceptableIndexes.map(i => old_end[i]);
const new_data_edge = {'Weight': new_Weight,
'start': new_start,
'end': new_end};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.edge_renderer.data_source.change.emit();
"""
callback = CustomJS(args = dict(graph_setup = network_graph,
edata = backup_edge_data),
code = code)
slider.js_on_change('value', callback)
layout = row(
plot,
column(slider),
)
show(layout)
image of the networkx graph and the slider
我发现这实际上是由于 bug 造成的: https://discourse.bokeh.org/t/dynamic-layout-behavior-changes-between-bokeh-2-2-3-and-bokeh-2-3-0/7594
为了在 bokeh 库未更新之前解决此问题(我使用的是 2.4.0 版),我修改了 customJS 代码以添加占位符数据以匹配初始数据源维度:
code = """
const old_Weight = edata["Weight"];
const old_start = edata["start"];
const old_end = edata["end"];
let acceptableIndexes = old_Weight.reduce(function(acc, curr, index) {
if (curr >= cb_obj.value) {
acc.push(index);
}
return acc;
}, []);
\ compute how many fake edges have to be added
const num_ph = old_Weight.length - acceptableIndexes.length
\ create an array of that dimension with fake value '9999'
const placeholder = Array(num_ph).fill('9999')
\ for each new value, concatenate the new array with the placeholder array
const new_Weight = acceptableIndexes.map(i => old_Weight[i]).concat(placeholder);
const new_start = acceptableIndexes.map(i => old_start[i]).concat(placeholder);
const new_end = acceptableIndexes.map(i => old_end[i]).concat(placeholder);
const new_data_edge = {'Weight': new_Weight,
'start': new_start,
'end': new_end};
graph_setup.edge_renderer.data_source.data = new_data_edge;
graph_setup.edge_renderer.data_source.change.emit();
"""