def test_from_networkx_with_bad_attributes() -> None: G = nx.Graph() G.add_nodes_from([(0, {"index": "a", "attr_1": 10}), (1, {"index": "b", "attr_1": 20})]) G.add_edges_from([[0, 1]]) with pytest.warns(UserWarning): renderer = bpg.from_networkx(G, nx.circular_layout) assert renderer.node_renderer.data_source.data["index"] == [0, 1] assert renderer.node_renderer.data_source.data["attr_1"] == [10, 20] G = nx.Graph() G.add_nodes_from([0, 1]) G.add_edges_from([(0, 1, {"start": "A", "attr_1": 10})]) with pytest.warns(UserWarning): renderer = bpg.from_networkx(G, nx.circular_layout) assert renderer.edge_renderer.data_source.data["start"] == [0] assert renderer.edge_renderer.data_source.data["end"] == [1] assert renderer.edge_renderer.data_source.data["attr_1"] == [10] G = nx.Graph() G.add_nodes_from([0, 1]) G.add_edges_from([(0, 1, {"end": "A", "attr_1": 10})]) with pytest.warns(UserWarning): renderer = bpg.from_networkx(G, nx.circular_layout) assert renderer.edge_renderer.data_source.data["start"] == [0] assert renderer.edge_renderer.data_source.data["end"] == [1] assert renderer.edge_renderer.data_source.data["attr_1"] == [10]
def test_from_networkx_errors_with_mixed_attributes() -> None: G = nx.Graph() G.add_nodes_from([(0, {"attr_1": [1, 2], "attr_2": 10}), (1, {}), (2, {"attr_1": 3, "attr_2": 30})]) with pytest.raises(ValueError): bpg.from_networkx(G, nx.circular_layout) G = nx.Graph() G.add_edges_from([(0, 1, {"attr_1": [1, 11]}), (0, 2, {"attr_1": 2, "attr_2": 10})]) with pytest.raises(ValueError): bpg.from_networkx(G, nx.circular_layout)
def test_from_networkx_with_sequence_attributes(typ): G = nx.Graph() G.add_nodes_from([(0, { "attr_1": typ([1, 2]), "attr_2": 10 }), (1, {}), (2, { "attr_1": typ([3]), "attr_2": 30 })]) G.add_edges_from([(0, 1, { "attr_1": typ([1, 11]) }), (0, 2, { "attr_1": typ([2, 22]), "attr_2": 10 })]) renderer = bpg.from_networkx(G, nx.circular_layout) assert renderer.node_renderer.data_source.data["index"] == [0, 1, 2] assert renderer.node_renderer.data_source.data["attr_1"] == [[1, 2], [], [3]] assert renderer.node_renderer.data_source.data["attr_2"] == [10, None, 30] assert renderer.edge_renderer.data_source.data["start"] == [0, 0] assert renderer.edge_renderer.data_source.data["end"] == [1, 2] assert renderer.edge_renderer.data_source.data["attr_1"] == [[1, 11], [2, 22]] assert renderer.edge_renderer.data_source.data["attr_2"] == [None, 10]
def test_from_networkx_with_scalar_attributes(): G = nx.Graph() G.add_nodes_from([(0, { "attr_1": "a", "attr_2": 10 }), (1, { "attr_1": "b" }), (2, { "attr_1": "c", "attr_2": 30 })]) G.add_edges_from([(0, 1, { "attr_1": "A" }), (0, 2, { "attr_1": "B", "attr_2": 10 })]) renderer = bpg.from_networkx(G, nx.circular_layout) assert renderer.node_renderer.data_source.data["index"] == [0, 1, 2] assert renderer.node_renderer.data_source.data["attr_1"] == ["a", "b", "c"] assert renderer.node_renderer.data_source.data["attr_2"] == [10, None, 30] assert renderer.edge_renderer.data_source.data["start"] == [0, 0] assert renderer.edge_renderer.data_source.data["end"] == [1, 2] assert renderer.edge_renderer.data_source.data["attr_1"] == ["A", "B"] assert renderer.edge_renderer.data_source.data["attr_2"] == [None, 10]
def test_from_networkx_method_with_kwargs() -> None: G=nx.Graph() G.add_nodes_from([0,1,2,3]) G.add_edges_from([[0,1], [0,2], [2,3]]) renderer = bpg.from_networkx(G, nx.circular_layout, scale=2) gl = renderer.layout_provider.graph_layout assert set(gl.keys()) == {0, 1, 2, 3} assert_allclose(gl[0], np.array([2.0, 0.0]), atol=1e-7)
def test_from_networkx_method() -> None: G=nx.Graph() G.add_nodes_from([0,1,2,3]) G.add_edges_from([[0,1], [0,2], [2,3]]) renderer = bpg.from_networkx(G, nx.circular_layout) assert renderer.node_renderer.data_source.data["index"] == [0,1,2,3] assert renderer.edge_renderer.data_source.data["start"] == [0,0,2] assert renderer.edge_renderer.data_source.data["end"] == [1,2,3] gl = renderer.layout_provider.graph_layout assert set(gl.keys()) == {0, 1, 2, 3} assert_allclose(gl[0], np.array([1.0, 0.0]), atol=1e-7)
def test_from_networkx_with_missing_layout() -> None: G = nx.Graph() G.add_nodes_from([0, 1, 2]) G.add_edges_from([[0, 1], [0, 2]]) missing_fixed_layout = {0: [0, 1], 1: [-1, 0]} with pytest.warns(UserWarning): renderer = bpg.from_networkx(G, missing_fixed_layout) gl = renderer.layout_provider.graph_layout assert set(gl.keys()) == {0, 1} assert renderer.layout_provider.graph_layout[0] == missing_fixed_layout[0] assert renderer.layout_provider.graph_layout[1] == missing_fixed_layout[1]
def test_from_networkx_fixed_layout(): G = nx.Graph() G.add_nodes_from([0, 1, 2]) G.add_edges_from([[0, 1], [0, 2]]) fixed_layout = {0: [0, 1], 1: [-1, 0], 2: [1, 0]} renderer = bpg.from_networkx(G, fixed_layout) assert renderer.node_renderer.data_source.data["index"] == [0, 1, 2] assert renderer.edge_renderer.data_source.data["start"] == [0, 0] assert renderer.edge_renderer.data_source.data["end"] == [1, 2] gl = renderer.layout_provider.graph_layout assert set(gl.keys()) == set([0, 1, 2]) assert renderer.layout_provider.graph_layout[0] == fixed_layout[0] assert renderer.layout_provider.graph_layout[1] == fixed_layout[1] assert renderer.layout_provider.graph_layout[2] == fixed_layout[2]
def create_bokeh(G, title, fname, extract_size=15, feature_size=10): # Add attributes this way try: max_weight = max(nx.get_edge_attributes(G, 'weight').values()) except ValueError: max_weight = 1.0 calc_alpha = lambda x: 0.1 + 0.6 * (x / max_weight) edge_attrs = {(s,e): calc_alpha(d['weight']) for s,e,d in G.edges(data=True)} node_attrs = { k: { "_size": feature_size if v['_type']=='spin' else extract_size, "_num_members": len(eval(v.get('members', '[]'))) } for k,v in G.nodes(data=True) } nx.set_edge_attributes(G, edge_attrs, "_alpha") nx.set_node_attributes(G, node_attrs) # Show with Bokeh plot = Plot(plot_width=1100, plot_height=700, x_range=Range1d(-1.1, 1.1), y_range=Range1d(-1.1, 1.1)) plot.title.text = title node_hover_tool = HoverTool(tooltips=[("Name", "@index"), ("Members", "@members")]) plot.add_tools(node_hover_tool, WheelZoomTool(), BoxZoomTool(), ResetTool(), TapTool()) # Tried this but default layout is just better... # def layout(x, **kwargs): # return nx.spring_layout(x, **kwargs, k=1.1/sqrt(x.number_of_nodes()),iterations=10000) # graph_renderer = from_networkx(G, layout, scale=1, center=(0, 0)) graph_renderer = from_networkx(G, nx.spring_layout, scale=1, center=(0, 0)) graph_renderer.node_renderer.glyph = Circle(size='_size', fill_color="_color") graph_renderer.node_renderer.selection_glyph = Circle(size=15, fill_color="orange")#works graph_renderer.node_renderer.hover_glyph = Circle(size=15, fill_color="red")#works graph_renderer.edge_renderer.glyph = MultiLine(line_alpha="_alpha", line_width=1) plot.renderers.append(graph_renderer) # Add filter slider df = nx.to_pandas_edgelist(G) max_members = max(nx.get_node_attributes(G,"_num_members").values()) backup_node_data = graph_renderer.node_renderer.data_source.data backup_edge_data = graph_renderer.edge_renderer.data_source.data code = """ const min_nodes = cb_obj.value[0]; const max_nodes = cb_obj.value[1]; var to_keep; to_keep = ndata.index.map((v,i) =>{ if (ndata._type[i] !== "spin"){return i} else{if(ndata._num_members[i]<=max_nodes && ndata._num_members[i]>=min_nodes) {return i}} }).filter(x => Boolean(x) | x === 0); let keep_names = ndata.index.filter((_,i)=>to_keep.includes(i)) let new_nodes = {}; Object.keys(ndata).forEach(k => { new_nodes[k] = ndata[k].filter((_,i)=>to_keep.includes(i)); }) let new_edges = {}; Object.keys(edata).forEach(k => { new_edges[k] = edata[k].filter((_,i)=>{ let end = false; let start = false; if (keep_names.includes(edata.start[i])) start = true if (keep_names.includes(edata.end[i])) end = true return end && start; }); }); // Update graph graph_setup.node_renderer.data_source.data = new_nodes; graph_setup.edge_renderer.data_source.data = new_edges; graph_setup.node_renderer.data_source.change.emit(); graph_setup.edge_renderer.data_source.change.emit(); """ min_cb = CustomJS(args=dict(graph_setup=graph_renderer, start=df['source'].values, end=df['target'].values, ndata=backup_node_data, edata=backup_edge_data, type="min"), code=code) min_slider = RangeSlider(title='Filter by Number of Members', start=1, end=max_members, value=(1, max_members)) min_slider.js_on_change('value', min_cb) output_file(fname) layout = Column(plot, min_slider) save(layout)