| """Unit tests for matplotlib drawing functions.""" |
|
|
| import itertools |
| import os |
| import warnings |
|
|
| import pytest |
|
|
| import networkx as nx |
|
|
| mpl = pytest.importorskip("matplotlib") |
| np = pytest.importorskip("numpy") |
| mpl.use("PS") |
| plt = pytest.importorskip("matplotlib.pyplot") |
| plt.rcParams["text.usetex"] = False |
|
|
|
|
| barbell = nx.barbell_graph(4, 6) |
|
|
| defaults = { |
| "node_pos": None, |
| "node_visible": True, |
| "node_color": "#1f78b4", |
| "node_size": 300, |
| "node_label": { |
| "size": 12, |
| "color": "#000000", |
| "family": "sans-serif", |
| "weight": "normal", |
| "alpha": 1.0, |
| "background_color": None, |
| "background_alpha": None, |
| "h_align": "center", |
| "v_align": "center", |
| "bbox": None, |
| }, |
| "node_shape": "o", |
| "node_alpha": 1.0, |
| "node_border_width": 1.0, |
| "node_border_color": "face", |
| "edge_visible": True, |
| "edge_width": 1.0, |
| "edge_color": "#000000", |
| "edge_label": { |
| "size": 12, |
| "color": "#000000", |
| "family": "sans-serif", |
| "weight": "normal", |
| "alpha": 1.0, |
| "bbox": {"boxstyle": "round", "ec": (1.0, 1.0, 1.0), "fc": (1.0, 1.0, 1.0)}, |
| "h_align": "center", |
| "v_align": "center", |
| "pos": 0.5, |
| "rotate": True, |
| }, |
| "edge_style": "-", |
| "edge_alpha": 1.0, |
| |
| "edge_arrowstyle": "-", |
| "edge_arrowsize": 0, |
| "edge_curvature": "arc3", |
| "edge_source_margin": 0, |
| "edge_target_margin": 0, |
| } |
|
|
|
|
| @pytest.mark.parametrize( |
| ("param_name", "param_value", "expected"), |
| ( |
| ("node_color", None, defaults["node_color"]), |
| ("node_color", "#FF0000", "red"), |
| ("node_color", "color", "lime"), |
| ), |
| ) |
| def test_display_arg_handling_node_color(param_name, param_value, expected): |
| G = nx.path_graph(4) |
| nx.set_node_attributes(G, "#00FF00", "color") |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas, **{param_name: param_value}) |
| assert mpl.colors.same_color(canvas.get_children()[0].get_edgecolors()[0], expected) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize( |
| ("param_value", "expected"), |
| ( |
| (None, (1, 1, 1, 1)), |
| (0.5, (0.5, 0.5, 0.5, 0.5)), |
| ("n_alpha", (1.0, 1 / 2, 1 / 3, 0.25)), |
| ), |
| ) |
| def test_display_arg_handling_node_alpha(param_value, expected): |
| G = nx.path_graph(4) |
| nx.set_node_attributes(G, {n: 1 / (n + 1) for n in G.nodes()}, "n_alpha") |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas, node_alpha=param_value) |
| assert all( |
| canvas.get_children()[0].get_fc()[:, 3] == expected |
| ) |
| plt.close() |
|
|
|
|
| def test_display_node_position(): |
| G = nx.path_graph(4) |
| nx.set_node_attributes(G, {n: (n, n) for n in G.nodes()}, "pos") |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas, node_pos="pos") |
| assert np.all( |
| canvas.get_children()[0].get_offsets().data == [[0, 0], [1, 1], [2, 2], [3, 3]] |
| ) |
| plt.close() |
|
|
|
|
| def test_display_line_collection(): |
| G = nx.karate_club_graph() |
| nx.set_edge_attributes( |
| G, {(u, v): "-|>" if (u + v) % 2 else "-" for u, v in G.edges()}, "arrowstyle" |
| ) |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas, edge_arrowsize=10) |
| |
| lc = [ |
| l |
| for l in canvas.get_children() |
| if isinstance(l, mpl.collections.LineCollection) |
| ][0] |
| assert len(lc.get_paths()) == sum([1 for u, v in G.edges() if (u + v) % 2]) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize( |
| ("edge_color", "expected"), |
| ( |
| (None, "black"), |
| ("r", "red"), |
| ((1.0, 1.0, 0.0), "yellow"), |
| ((0, 1, 0, 1), "lime"), |
| ("color", "blue"), |
| ("#0000FF", "blue"), |
| ), |
| ) |
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_edge_single_color(edge_color, expected, graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_edge_attributes(G, "#0000FF", "color") |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, edge_color=edge_color, canvas=canvas) |
| if G.is_directed(): |
| colors = [ |
| f.get_fc() |
| for f in canvas.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| else: |
| colors = [ |
| l |
| for l in canvas.collections |
| if isinstance(l, mpl.collections.LineCollection) |
| ][0].get_colors() |
| assert all(mpl.colors.same_color(c, expected) for c in colors) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_edge_multiple_colors(graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_edge_attributes(G, {(0, 1): "#FF0000", (1, 2): (0, 0, 1)}, "color") |
| ax = plt.figure().add_subplot(111) |
| nx.display(G, canvas=ax) |
| expected = ["red", "blue"] |
| if G.is_directed(): |
| colors = [ |
| f.get_fc() |
| for f in ax.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| else: |
| colors = [ |
| l for l in ax.collections if isinstance(l, mpl.collections.LineCollection) |
| ][0].get_colors() |
| assert mpl.colors.same_color(colors, expected) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_edge_position(graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_node_attributes(G, {n: (n, n) for n in G.nodes()}, "pos") |
| ax = plt.figure().add_subplot(111) |
| nx.display(G, canvas=ax) |
| if G.is_directed(): |
| end_points = [ |
| (f.get_path().vertices[0, :], f.get_path().vertices[-2, :]) |
| for f in ax.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| else: |
| line_collection = [ |
| l for l in ax.collections if isinstance(l, mpl.collections.LineCollection) |
| ][0] |
| end_points = [ |
| (p.vertices[0, :], p.vertices[-1, :]) for p in line_collection.get_paths() |
| ] |
| expected = [((0, 0), (1, 1)), ((1, 1), (2, 2))] |
| |
| |
| threshold = 0.05 |
| for a, e in zip(end_points, expected): |
| act_start, act_end = a |
| exp_start, exp_end = e |
| assert all(abs(act_start - exp_start) < (threshold, threshold)) and all( |
| abs(act_end - exp_end) < (threshold, threshold) |
| ) |
| plt.close() |
|
|
|
|
| def test_display_position_function(): |
| G = nx.karate_club_graph() |
|
|
| def fixed_layout(G): |
| return nx.spring_layout(G, seed=314159) |
|
|
| pos = fixed_layout(G) |
| ax = plt.figure().add_subplot(111) |
| nx.display(G, node_pos=fixed_layout, canvas=ax) |
| |
| act_pos = { |
| n: tuple(p) for n, p in zip(G.nodes(), ax.get_children()[0].get_offsets().data) |
| } |
| for n in G.nodes(): |
| assert all(pos[n] == act_pos[n]) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_edge_colormaps(graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_edge_attributes(G, {(0, 1): 0, (1, 2): 1}, "weight") |
| cmap = mpl.colormaps["plasma"] |
| nx.apply_matplotlib_colors(G, "weight", "color", cmap, nodes=False) |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas) |
| mapper = mpl.cm.ScalarMappable(cmap=cmap) |
| mapper.set_clim(0, 1) |
| expected = [mapper.to_rgba(0), mapper.to_rgba(1)] |
| if G.is_directed(): |
| colors = [ |
| f.get_facecolor() |
| for f in canvas.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| else: |
| colors = [ |
| l |
| for l in canvas.collections |
| if isinstance(l, mpl.collections.LineCollection) |
| ][0].get_colors() |
| assert mpl.colors.same_color(expected[0], G.edges[0, 1]["color"]) |
| assert mpl.colors.same_color(expected[1], G.edges[1, 2]["color"]) |
| assert mpl.colors.same_color(expected, colors) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_node_colormaps(graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_node_attributes(G, {0: 0, 1: 0.5, 2: 1}, "weight") |
| cmap = mpl.colormaps["plasma"] |
| nx.apply_matplotlib_colors(G, "weight", "color", cmap) |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas) |
| mapper = mpl.cm.ScalarMappable(cmap=cmap) |
| mapper.set_clim(0, 1) |
| expected = [mapper.to_rgba(0), mapper.to_rgba(0.5), mapper.to_rgba(1)] |
| colors = [ |
| s for s in canvas.collections if isinstance(s, mpl.collections.PathCollection) |
| ][0].get_edgecolors() |
| assert mpl.colors.same_color(expected[0], G.nodes[0]["color"]) |
| assert mpl.colors.same_color(expected[1], G.nodes[1]["color"]) |
| assert mpl.colors.same_color(expected[2], G.nodes[2]["color"]) |
| assert mpl.colors.same_color(expected, colors) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize( |
| ("param_value", "expected"), |
| ( |
| (None, [defaults["edge_width"], defaults["edge_width"]]), |
| (5, [5, 5]), |
| ("width", [5, 10]), |
| ), |
| ) |
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_edge_width(param_value, expected, graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_edge_attributes(G, {(0, 1): 5, (1, 2): 10}, "width") |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, edge_width=param_value, canvas=canvas) |
| if G.is_directed(): |
| widths = [ |
| f.get_linewidth() |
| for f in canvas.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| else: |
| widths = list( |
| [ |
| l |
| for l in canvas.collections |
| if isinstance(l, mpl.collections.LineCollection) |
| ][0].get_linewidths() |
| ) |
| assert widths == expected |
|
|
|
|
| @pytest.mark.parametrize( |
| ("param_value", "expected"), |
| ( |
| (None, [defaults["edge_style"], defaults["edge_style"]]), |
| (":", [":", ":"]), |
| ("style", ["-", ":"]), |
| ), |
| ) |
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_display_edge_style(param_value, expected, graph_type): |
| G = nx.path_graph(3, create_using=graph_type) |
| nx.set_edge_attributes(G, {(0, 1): "-", (1, 2): ":"}, "style") |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, edge_style=param_value, canvas=canvas) |
| if G.is_directed(): |
| styles = [ |
| f.get_linestyle() |
| for f in canvas.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| else: |
| |
| linestyles = {(0, None): "-", (0, (1, 1.65)): ":"} |
| styles = [ |
| linestyles[(s[0], tuple(s[1]) if s[1] is not None else None)] |
| for s in [ |
| l |
| for l in canvas.collections |
| if isinstance(l, mpl.collections.LineCollection) |
| ][0].get_linestyles() |
| ] |
| assert styles == expected |
| plt.close() |
|
|
|
|
| def test_display_node_labels(): |
| G = nx.path_graph(4) |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas, node_label={"size": 20}) |
| labels = [t for t in canvas.get_children() if isinstance(t, mpl.text.Text)] |
| for n, l in zip(G.nodes(), labels): |
| assert l.get_text() == str(n) |
| assert l.get_size() == 20.0 |
| plt.close() |
|
|
|
|
| def test_display_edge_labels(): |
| G = nx.path_graph(4) |
| canvas = plt.figure().add_subplot(111) |
| |
| |
| nx.set_edge_attributes(G, {(u, v): {"label": u + v} for u, v in G.edges()}) |
| nx.display(G, canvas=canvas, edge_label={"color": "r"}, node_label=None) |
| labels = [t for t in canvas.get_children() if isinstance(t, mpl.text.Text)] |
| print(labels) |
| for e, l in zip(G.edges(), labels): |
| assert l.get_text() == str(e[0] + e[1]) |
| assert l.get_color() == "r" |
| plt.close() |
|
|
|
|
| def test_display_multigraph_non_integer_keys(): |
| G = nx.MultiGraph() |
| G.add_nodes_from(["A", "B", "C", "D"]) |
| G.add_edges_from( |
| [ |
| ("A", "B", "0"), |
| ("A", "B", "1"), |
| ("B", "C", "-1"), |
| ("B", "C", "1"), |
| ("C", "D", "-1"), |
| ("C", "D", "0"), |
| ] |
| ) |
| nx.set_edge_attributes( |
| G, {e: f"arc3,rad={0.2 * int(e[2])}" for e in G.edges(keys=True)}, "curvature" |
| ) |
| canvas = plt.figure().add_subplot(111) |
| nx.display(G, canvas=canvas) |
| rads = [ |
| f.get_connectionstyle().rad |
| for f in canvas.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| assert rads == [0.0, 0.2, -0.2, 0.2, -0.2, 0.0] |
| plt.close() |
|
|
|
|
| def test_display_raises_for_bad_arg(): |
| G = nx.karate_club_graph() |
| with pytest.raises(nx.NetworkXError): |
| nx.display(G, bad_arg="bad_arg") |
| plt.close() |
|
|
|
|
| def test_display_arrow_size(): |
| G = nx.path_graph(4, create_using=nx.DiGraph) |
| nx.set_edge_attributes( |
| G, {(u, v): (u + v + 2) ** 2 for u, v in G.edges()}, "arrowsize" |
| ) |
| ax = plt.axes() |
| nx.display(G, canvas=ax) |
| assert [9, 25, 49] == [ |
| f.get_mutation_scale() |
| for f in ax.get_children() |
| if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| plt.close() |
|
|
|
|
| def test_display_mismatched_edge_position(): |
| """ |
| This test ensures that a error is raised for incomplete position data. |
| """ |
| G = nx.path_graph(5) |
| |
| nx.set_node_attributes(G, {0: (0, 0), 1: (1, 1), 2: (2, 2), 4: (4, 4)}, "pos") |
| |
| nx.set_node_attributes(G, {n: n < 4 for n in G.nodes()}, "visible") |
| |
| |
| with pytest.raises(nx.NetworkXError): |
| nx.display(G) |
|
|
|
|
| |
| |
| @pytest.mark.parametrize("node_shape", ("o", "s")) |
| def test_display_edge_margins(node_shape): |
| """ |
| Test that there is a wider gap between the node and the start of an |
| incident edge when min_source_margin is specified. |
| |
| This test checks that the use os min_{source/target}_margin edge |
| attributes result is shorter (more padding) between the edges and |
| source and target nodes. |
| |
| |
| As a crude visual example, let 's' and 't' represent source and target |
| nodes, respectively: |
| |
| Default: |
| s-----------------------------t |
| |
| With margins: |
| s ----------------------- t |
| |
| """ |
| ax = plt.figure().add_subplot(111) |
| G = nx.DiGraph([(0, 1)]) |
| nx.set_node_attributes(G, {0: (0, 0), 1: (1, 1)}, "pos") |
| |
| nx.display(G, canvas=ax, node_shape=node_shape) |
| default_arrow = [ |
| f for f in ax.get_children() if isinstance(f, mpl.patches.FancyArrowPatch) |
| ][0] |
| default_extent = default_arrow.get_extents().corners()[::2, 0] |
| |
| ax = plt.figure().add_subplot(111) |
| nx.display( |
| G, |
| canvas=ax, |
| edge_source_margin=100, |
| edge_target_margin=100, |
| node_shape=node_shape, |
| ) |
| padded_arrow = [ |
| f for f in ax.get_children() if isinstance(f, mpl.patches.FancyArrowPatch) |
| ][0] |
| padded_extent = padded_arrow.get_extents().corners()[::2, 0] |
|
|
| |
| assert padded_extent[0] > default_extent[0] |
| |
| assert padded_extent[1] < default_extent[1] |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize("ticks", [False, True]) |
| def test_display_hide_ticks(ticks): |
| G = nx.path_graph(3) |
| nx.set_node_attributes(G, {n: (n, n) for n in G.nodes()}, "pos") |
| ax = plt.axes() |
| nx.display(G, hide_ticks=ticks) |
| for axis in [ax.xaxis, ax.yaxis]: |
| assert bool(axis.get_ticklabels()) != ticks |
|
|
| plt.close() |
|
|
|
|
| def test_display_self_loop(): |
| ax = plt.axes() |
| G = nx.DiGraph() |
| G.add_node(0) |
| G.add_edge(0, 0) |
| nx.set_node_attributes(G, {0: (0, 0)}, "pos") |
| nx.display(G, canvas=ax) |
| arrow = [ |
| f for f in ax.get_children() if isinstance(f, mpl.patches.FancyArrowPatch) |
| ][0] |
| bbox = arrow.get_extents() |
| print(bbox.width) |
| print(bbox.height) |
| assert bbox.width > 0 and bbox.height > 0 |
|
|
| plt.delaxes(ax) |
| plt.close() |
|
|
|
|
| def test_display_remove_pos_attr(): |
| """ |
| If the pos attribute isn't provided or is a function, display computes the layout |
| and adds it to the graph. We need to ensure that this new attribute is removed from |
| the returned graph. |
| """ |
| G = nx.karate_club_graph() |
| nx.display(G) |
| assert nx.get_node_attributes(G, "display's position attribute name") == {} |
|
|
|
|
| @pytest.fixture |
| def subplots(): |
| fig, ax = plt.subplots() |
| yield fig, ax |
| plt.delaxes(ax) |
| plt.close() |
|
|
|
|
| @pytest.mark.parametrize( |
| "function", |
| [ |
| nx.draw_circular, |
| nx.draw_kamada_kawai, |
| nx.draw_planar, |
| nx.draw_random, |
| nx.draw_spectral, |
| nx.draw_spring, |
| nx.draw_shell, |
| nx.draw_forceatlas2, |
| ], |
| ) |
| def test_draw(function, subplots, tmp_path): |
| if function == nx.draw_kamada_kawai: |
| pytest.importorskip("scipy", reason="draw_kamada_kawai requires scipy") |
| fig, _ = subplots |
| options = {"node_color": "black", "node_size": 100, "width": 3} |
| function(barbell, **options) |
| fig.savefig(tmp_path / "test.ps") |
|
|
|
|
| def test_draw_shell_nlist(subplots, tmp_path): |
| fig, _ = subplots |
| nlist = [list(range(4)), list(range(4, 10)), list(range(10, 14))] |
| nx.draw_shell(barbell, nlist=nlist) |
| fig.savefig(tmp_path / "test.ps") |
|
|
|
|
| def test_draw_bipartite(subplots, tmp_path): |
| fig, _ = subplots |
| G = nx.complete_bipartite_graph(2, 5) |
| nx.draw_bipartite(G) |
| fig.savefig(tmp_path / "test.ps") |
|
|
|
|
| def test_edge_colormap(): |
| colors = range(barbell.number_of_edges()) |
| nx.draw_spring( |
| barbell, edge_color=colors, width=4, edge_cmap=plt.cm.Blues, with_labels=True |
| ) |
| |
|
|
|
|
| def test_draw_networkx_edge_labels(subplots, tmp_path): |
| fig, _ = subplots |
| edge = (0, 1) |
| G = nx.DiGraph([edge]) |
| pos = {n: (n, n) for n in G} |
| nx.draw(G, pos=pos) |
| nx.draw_networkx_edge_labels(G, pos, edge_labels={edge: "edge"}) |
| fig.savefig(tmp_path / "test.ps") |
|
|
|
|
| def test_arrows(): |
| nx.draw_spring(barbell.to_directed()) |
| |
|
|
|
|
| @pytest.mark.parametrize( |
| ("edge_color", "expected"), |
| ( |
| (None, "black"), |
| ("r", "red"), |
| (["r"], "red"), |
| ((1.0, 1.0, 0.0), "yellow"), |
| ([(1.0, 1.0, 0.0)], "yellow"), |
| ((0, 1, 0, 1), "lime"), |
| ([(0, 1, 0, 1)], "lime"), |
| ("#0000ff", "blue"), |
| (["#0000ff"], "blue"), |
| ), |
| ) |
| @pytest.mark.parametrize("edgelist", (None, [(0, 1)])) |
| def test_single_edge_color_undirected(edge_color, expected, edgelist): |
| """Tests ways of specifying all edges have a single color for edges |
| drawn with a LineCollection""" |
|
|
| G = nx.path_graph(3) |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos=nx.random_layout(G), edgelist=edgelist, edge_color=edge_color |
| ) |
| assert mpl.colors.same_color(drawn_edges.get_color(), expected) |
|
|
|
|
| @pytest.mark.parametrize( |
| ("edge_color", "expected"), |
| ( |
| (None, "black"), |
| ("r", "red"), |
| (["r"], "red"), |
| ((1.0, 1.0, 0.0), "yellow"), |
| ([(1.0, 1.0, 0.0)], "yellow"), |
| ((0, 1, 0, 1), "lime"), |
| ([(0, 1, 0, 1)], "lime"), |
| ("#0000ff", "blue"), |
| (["#0000ff"], "blue"), |
| ), |
| ) |
| @pytest.mark.parametrize("edgelist", (None, [(0, 1)])) |
| def test_single_edge_color_directed(edge_color, expected, edgelist): |
| """Tests ways of specifying all edges have a single color for edges drawn |
| with FancyArrowPatches""" |
|
|
| G = nx.path_graph(3, create_using=nx.DiGraph) |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos=nx.random_layout(G), edgelist=edgelist, edge_color=edge_color |
| ) |
| for fap in drawn_edges: |
| assert mpl.colors.same_color(fap.get_edgecolor(), expected) |
|
|
|
|
| def test_edge_color_tuple_interpretation(): |
| """If edge_color is a sequence with the same length as edgelist, then each |
| value in edge_color is mapped onto each edge via colormap.""" |
| G = nx.path_graph(6, create_using=nx.DiGraph) |
| pos = {n: (n, n) for n in range(len(G))} |
|
|
| |
| for ec in ((0, 0, 1), (0, 0, 1, 1)): |
| |
| drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=ec) |
| for fap in drawn_edges: |
| assert mpl.colors.same_color(fap.get_edgecolor(), ec) |
| |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos, edgelist=[(0, 1), (1, 2)], edge_color=ec |
| ) |
| for fap in drawn_edges: |
| assert mpl.colors.same_color(fap.get_edgecolor(), ec) |
|
|
| |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos, edgelist=[(0, 1), (1, 2), (2, 3)], edge_color=(0, 0, 1, 1) |
| ) |
| for fap in drawn_edges: |
| assert mpl.colors.same_color(fap.get_edgecolor(), "blue") |
|
|
| |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos, edgelist=[(0, 1), (1, 2), (2, 3), (3, 4)], edge_color=(0, 0, 1) |
| ) |
| for fap in drawn_edges: |
| assert mpl.colors.same_color(fap.get_edgecolor(), "blue") |
|
|
| |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos, edgelist=[(0, 1), (1, 2), (2, 3)], edge_color=(0, 0, 1) |
| ) |
| assert mpl.colors.same_color( |
| drawn_edges[0].get_edgecolor(), drawn_edges[1].get_edgecolor() |
| ) |
| for fap in drawn_edges: |
| assert not mpl.colors.same_color(fap.get_edgecolor(), "blue") |
|
|
| |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos, edgelist=[(0, 1), (1, 2), (2, 3), (3, 4)], edge_color=(0, 0, 1, 1) |
| ) |
| assert mpl.colors.same_color( |
| drawn_edges[0].get_edgecolor(), drawn_edges[1].get_edgecolor() |
| ) |
| assert mpl.colors.same_color( |
| drawn_edges[2].get_edgecolor(), drawn_edges[3].get_edgecolor() |
| ) |
| for fap in drawn_edges: |
| assert not mpl.colors.same_color(fap.get_edgecolor(), "blue") |
|
|
|
|
| def test_fewer_edge_colors_than_num_edges_directed(): |
| """Test that the edge colors are cycled when there are fewer specified |
| colors than edges.""" |
| G = barbell.to_directed() |
| pos = nx.random_layout(barbell) |
| edgecolors = ("r", "g", "b") |
| drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=edgecolors) |
| for fap, expected in zip(drawn_edges, itertools.cycle(edgecolors)): |
| assert mpl.colors.same_color(fap.get_edgecolor(), expected) |
|
|
|
|
| def test_more_edge_colors_than_num_edges_directed(): |
| """Test that extra edge colors are ignored when there are more specified |
| colors than edges.""" |
| G = nx.path_graph(4, create_using=nx.DiGraph) |
| pos = nx.random_layout(barbell) |
| edgecolors = ("r", "g", "b", "c") |
| drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=edgecolors) |
| for fap, expected in zip(drawn_edges, edgecolors[:-1]): |
| assert mpl.colors.same_color(fap.get_edgecolor(), expected) |
|
|
|
|
| def test_edge_color_string_with_global_alpha_undirected(): |
| edge_collection = nx.draw_networkx_edges( |
| barbell, |
| pos=nx.random_layout(barbell), |
| edgelist=[(0, 1), (1, 2)], |
| edge_color="purple", |
| alpha=0.2, |
| ) |
| ec = edge_collection.get_color().squeeze() |
| assert len(edge_collection.get_paths()) == 2 |
| assert mpl.colors.same_color(ec[:-1], "purple") |
| assert ec[-1] == 0.2 |
|
|
|
|
| def test_edge_color_string_with_global_alpha_directed(): |
| drawn_edges = nx.draw_networkx_edges( |
| barbell.to_directed(), |
| pos=nx.random_layout(barbell), |
| edgelist=[(0, 1), (1, 2)], |
| edge_color="purple", |
| alpha=0.2, |
| ) |
| assert len(drawn_edges) == 2 |
| for fap in drawn_edges: |
| ec = fap.get_edgecolor() |
| assert mpl.colors.same_color(ec[:-1], "purple") |
| assert ec[-1] == 0.2 |
|
|
|
|
| @pytest.mark.parametrize("graph_type", (nx.Graph, nx.DiGraph)) |
| def test_edge_width_default_value(graph_type): |
| """Test the default linewidth for edges drawn either via LineCollection or |
| FancyArrowPatches.""" |
| G = nx.path_graph(2, create_using=graph_type) |
| pos = {n: (n, n) for n in range(len(G))} |
| drawn_edges = nx.draw_networkx_edges(G, pos) |
| if isinstance(drawn_edges, list): |
| drawn_edges = drawn_edges[0] |
| assert drawn_edges.get_linewidth() == 1 |
|
|
|
|
| @pytest.mark.parametrize( |
| ("edgewidth", "expected"), |
| ( |
| (3, 3), |
| ([3], 3), |
| ), |
| ) |
| def test_edge_width_single_value_undirected(edgewidth, expected): |
| G = nx.path_graph(4) |
| pos = {n: (n, n) for n in range(len(G))} |
| drawn_edges = nx.draw_networkx_edges(G, pos, width=edgewidth) |
| assert len(drawn_edges.get_paths()) == 3 |
| assert drawn_edges.get_linewidth() == expected |
|
|
|
|
| @pytest.mark.parametrize( |
| ("edgewidth", "expected"), |
| ( |
| (3, 3), |
| ([3], 3), |
| ), |
| ) |
| def test_edge_width_single_value_directed(edgewidth, expected): |
| G = nx.path_graph(4, create_using=nx.DiGraph) |
| pos = {n: (n, n) for n in range(len(G))} |
| drawn_edges = nx.draw_networkx_edges(G, pos, width=edgewidth) |
| assert len(drawn_edges) == 3 |
| for fap in drawn_edges: |
| assert fap.get_linewidth() == expected |
|
|
|
|
| @pytest.mark.parametrize( |
| "edgelist", |
| ( |
| [(0, 1), (1, 2), (2, 3)], |
| None, |
| [(0, 1), (1, 2)], |
| ), |
| ) |
| def test_edge_width_sequence(edgelist): |
| G = barbell.to_directed() |
| pos = nx.random_layout(G) |
| widths = (0.5, 2.0, 12.0) |
| drawn_edges = nx.draw_networkx_edges(G, pos, edgelist=edgelist, width=widths) |
| for fap, expected_width in zip(drawn_edges, itertools.cycle(widths)): |
| assert fap.get_linewidth() == expected_width |
|
|
|
|
| def test_edge_color_with_edge_vmin_vmax(): |
| """Test that edge_vmin and edge_vmax properly set the dynamic range of the |
| color map when num edges == len(edge_colors).""" |
| G = nx.path_graph(3, create_using=nx.DiGraph) |
| pos = nx.random_layout(G) |
| |
| drawn_edges = nx.draw_networkx_edges(G, pos, edge_color=[0, 1.0]) |
| orig_colors = [e.get_edgecolor() for e in drawn_edges] |
| |
| drawn_edges = nx.draw_networkx_edges( |
| G, pos, edge_color=[0.2, 0.8], edge_vmin=0.2, edge_vmax=0.8 |
| ) |
| scaled_colors = [e.get_edgecolor() for e in drawn_edges] |
| assert mpl.colors.same_color(orig_colors, scaled_colors) |
|
|
|
|
| def test_directed_edges_linestyle_default(): |
| """Test default linestyle for edges drawn with FancyArrowPatches.""" |
| G = nx.path_graph(4, create_using=nx.DiGraph) |
| pos = {n: (n, n) for n in range(len(G))} |
|
|
| |
| drawn_edges = nx.draw_networkx_edges(G, pos) |
| assert len(drawn_edges) == 3 |
| for fap in drawn_edges: |
| assert fap.get_linestyle() == "solid" |
|
|
|
|
| @pytest.mark.parametrize( |
| "style", |
| ( |
| "dashed", |
| "--", |
| (1, (1, 1)), |
| ), |
| ) |
| def test_directed_edges_linestyle_single_value(style): |
| """Tests support for specifying linestyles with a single value to be applied to |
| all edges in ``draw_networkx_edges`` for FancyArrowPatch outputs |
| (e.g. directed edges).""" |
|
|
| G = nx.path_graph(4, create_using=nx.DiGraph) |
| pos = {n: (n, n) for n in range(len(G))} |
|
|
| drawn_edges = nx.draw_networkx_edges(G, pos, style=style) |
| assert len(drawn_edges) == 3 |
| for fap in drawn_edges: |
| assert fap.get_linestyle() == style |
|
|
|
|
| @pytest.mark.parametrize( |
| "style_seq", |
| ( |
| ["dashed"], |
| ["--"], |
| [(1, (1, 1))], |
| ["--", "-", ":"], |
| ["--", "-"], |
| ["--", "-", ":", "-."], |
| ), |
| ) |
| def test_directed_edges_linestyle_sequence(style_seq): |
| """Tests support for specifying linestyles with sequences in |
| ``draw_networkx_edges`` for FancyArrowPatch outputs (e.g. directed edges).""" |
|
|
| G = nx.path_graph(4, create_using=nx.DiGraph) |
| pos = {n: (n, n) for n in range(len(G))} |
|
|
| drawn_edges = nx.draw_networkx_edges(G, pos, style=style_seq) |
| assert len(drawn_edges) == 3 |
| for fap, style in zip(drawn_edges, itertools.cycle(style_seq)): |
| assert fap.get_linestyle() == style |
|
|
|
|
| def test_return_types(): |
| from matplotlib.collections import LineCollection, PathCollection |
| from matplotlib.patches import FancyArrowPatch |
|
|
| G = nx.frucht_graph(create_using=nx.Graph) |
| dG = nx.frucht_graph(create_using=nx.DiGraph) |
| pos = nx.spring_layout(G, seed=42) |
| dpos = nx.spring_layout(dG, seed=42) |
| |
| nodes = nx.draw_networkx_nodes(G, pos) |
| assert isinstance(nodes, PathCollection) |
| |
| edges = nx.draw_networkx_edges(dG, dpos, arrows=True) |
| assert isinstance(edges, list) |
| if len(edges) > 0: |
| assert isinstance(edges[0], FancyArrowPatch) |
| edges = nx.draw_networkx_edges(dG, dpos, arrows=False) |
| assert isinstance(edges, LineCollection) |
| edges = nx.draw_networkx_edges(G, dpos, arrows=None) |
| assert isinstance(edges, LineCollection) |
| edges = nx.draw_networkx_edges(dG, pos, arrows=None) |
| assert isinstance(edges, list) |
| if len(edges) > 0: |
| assert isinstance(edges[0], FancyArrowPatch) |
|
|
|
|
| def test_labels_and_colors(): |
| G = nx.cubical_graph() |
| pos = nx.spring_layout(G, seed=42) |
| |
| nx.draw_networkx_nodes( |
| G, pos, nodelist=[0, 1, 2, 3], node_color="r", node_size=500, alpha=0.75 |
| ) |
| nx.draw_networkx_nodes( |
| G, |
| pos, |
| nodelist=[4, 5, 6, 7], |
| node_color="b", |
| node_size=500, |
| alpha=[0.25, 0.5, 0.75, 1.0], |
| ) |
| |
| nx.draw_networkx_edges(G, pos, width=1.0, alpha=0.5) |
| nx.draw_networkx_edges( |
| G, |
| pos, |
| edgelist=[(0, 1), (1, 2), (2, 3), (3, 0)], |
| width=8, |
| alpha=0.5, |
| edge_color="r", |
| ) |
| nx.draw_networkx_edges( |
| G, |
| pos, |
| edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)], |
| width=8, |
| alpha=0.5, |
| edge_color="b", |
| ) |
| nx.draw_networkx_edges( |
| G, |
| pos, |
| edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)], |
| arrows=True, |
| min_source_margin=0.5, |
| min_target_margin=0.75, |
| width=8, |
| edge_color="b", |
| ) |
| |
| labels = {} |
| labels[0] = r"$a$" |
| labels[1] = r"$b$" |
| labels[2] = r"$c$" |
| labels[3] = r"$d$" |
| labels[4] = r"$\alpha$" |
| labels[5] = r"$\beta$" |
| labels[6] = r"$\gamma$" |
| labels[7] = r"$\delta$" |
| colors = {n: "k" if n % 2 == 0 else "r" for n in range(8)} |
| nx.draw_networkx_labels(G, pos, labels, font_size=16) |
| nx.draw_networkx_labels(G, pos, labels, font_size=16, font_color=colors) |
| nx.draw_networkx_edge_labels(G, pos, edge_labels=None, rotate=False) |
| nx.draw_networkx_edge_labels(G, pos, edge_labels={(4, 5): "4-5"}) |
| |
|
|
|
|
| def test_axes(subplots): |
| fig, ax = subplots |
| nx.draw(barbell, ax=ax) |
| nx.draw_networkx_edge_labels(barbell, nx.circular_layout(barbell), ax=ax) |
|
|
|
|
| def test_empty_graph(): |
| G = nx.Graph() |
| nx.draw(G) |
|
|
|
|
| def test_draw_empty_nodes_return_values(): |
| |
| import matplotlib.collections |
|
|
| G = nx.Graph([(1, 2), (2, 3)]) |
| DG = nx.DiGraph([(1, 2), (2, 3)]) |
| pos = nx.circular_layout(G) |
| assert isinstance( |
| nx.draw_networkx_nodes(G, pos, nodelist=[]), mpl.collections.PathCollection |
| ) |
| assert isinstance( |
| nx.draw_networkx_nodes(DG, pos, nodelist=[]), mpl.collections.PathCollection |
| ) |
|
|
| |
| |
| assert nx.draw_networkx_edges(G, pos, edgelist=[], arrows=True) == [] |
| assert nx.draw_networkx_edges(G, pos, edgelist=[], arrows=False) == [] |
| assert nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=False) == [] |
| assert nx.draw_networkx_edges(DG, pos, edgelist=[], arrows=True) == [] |
|
|
|
|
| def test_multigraph_edgelist_tuples(): |
| |
| G = nx.path_graph(3, create_using=nx.MultiDiGraph) |
| nx.draw_networkx(G, edgelist=[(0, 1, 0)]) |
| nx.draw_networkx(G, edgelist=[(0, 1, 0)], node_size=[10, 20, 0]) |
|
|
|
|
| def test_alpha_iter(): |
| pos = nx.random_layout(barbell) |
| fig = plt.figure() |
| |
| fig.add_subplot(131) |
| nx.draw_networkx_nodes(barbell, pos, alpha=[0.1, 0.2]) |
| |
| num_nodes = len(barbell.nodes) |
| alpha = [x / num_nodes for x in range(num_nodes)] |
| colors = range(num_nodes) |
| fig.add_subplot(132) |
| nx.draw_networkx_nodes(barbell, pos, node_color=colors, alpha=alpha) |
| |
| alpha.append(1) |
| fig.add_subplot(133) |
| nx.draw_networkx_nodes(barbell, pos, alpha=alpha) |
|
|
|
|
| def test_multiple_node_shapes(subplots): |
| fig, ax = subplots |
| G = nx.path_graph(4) |
| nx.draw(G, node_shape=["o", "h", "s", "^"], ax=ax) |
| scatters = [ |
| s for s in ax.get_children() if isinstance(s, mpl.collections.PathCollection) |
| ] |
| assert len(scatters) == 4 |
|
|
|
|
| def test_individualized_font_attributes(subplots): |
| G = nx.karate_club_graph() |
| fig, ax = subplots |
| nx.draw( |
| G, |
| ax=ax, |
| font_color={n: "k" if n % 2 else "r" for n in G.nodes()}, |
| font_size={n: int(n / (34 / 15) + 5) for n in G.nodes()}, |
| ) |
| for n, t in zip( |
| G.nodes(), |
| [ |
| t |
| for t in ax.get_children() |
| if isinstance(t, mpl.text.Text) and len(t.get_text()) > 0 |
| ], |
| ): |
| expected = "black" if n % 2 else "red" |
|
|
| assert mpl.colors.same_color(t.get_color(), expected) |
| assert int(n / (34 / 15) + 5) == t.get_size() |
|
|
|
|
| def test_individualized_edge_attributes(subplots): |
| G = nx.karate_club_graph() |
| fig, ax = subplots |
| arrowstyles = ["-|>" if (u + v) % 2 == 0 else "-[" for u, v in G.edges()] |
| arrowsizes = [10 * (u % 2 + v % 2) + 10 for u, v in G.edges()] |
| nx.draw(G, ax=ax, arrows=True, arrowstyle=arrowstyles, arrowsize=arrowsizes) |
| arrows = [ |
| f for f in ax.get_children() if isinstance(f, mpl.patches.FancyArrowPatch) |
| ] |
| for e, a in zip(G.edges(), arrows): |
| assert a.get_mutation_scale() == 10 * (e[0] % 2 + e[1] % 2) + 10 |
| expected = ( |
| mpl.patches.ArrowStyle.BracketB |
| if sum(e) % 2 |
| else mpl.patches.ArrowStyle.CurveFilledB |
| ) |
| assert isinstance(a.get_arrowstyle(), expected) |
|
|
|
|
| def test_error_invalid_kwds(): |
| with pytest.raises(ValueError, match="Received invalid argument"): |
| nx.draw(barbell, foo="bar") |
|
|
|
|
| def test_draw_networkx_arrowsize_incorrect_size(): |
| G = nx.DiGraph([(0, 1), (0, 2), (0, 3), (1, 3)]) |
| arrowsize = [1, 2, 3] |
| with pytest.raises( |
| ValueError, match="arrowsize should have the same length as edgelist" |
| ): |
| nx.draw(G, arrowsize=arrowsize) |
|
|
|
|
| @pytest.mark.parametrize("arrowsize", (30, [10, 20, 30])) |
| def test_draw_edges_arrowsize(arrowsize): |
| G = nx.DiGraph([(0, 1), (0, 2), (1, 2)]) |
| pos = {0: (0, 0), 1: (0, 1), 2: (1, 0)} |
| edges = nx.draw_networkx_edges(G, pos=pos, arrowsize=arrowsize) |
|
|
| arrowsize = itertools.repeat(arrowsize) if isinstance(arrowsize, int) else arrowsize |
|
|
| for fap, expected in zip(edges, arrowsize): |
| assert isinstance(fap, mpl.patches.FancyArrowPatch) |
| assert fap.get_mutation_scale() == expected |
|
|
|
|
| @pytest.mark.parametrize("arrowstyle", ("-|>", ["-|>", "-[", "<|-|>"])) |
| def test_draw_edges_arrowstyle(arrowstyle): |
| G = nx.DiGraph([(0, 1), (0, 2), (1, 2)]) |
| pos = {0: (0, 0), 1: (0, 1), 2: (1, 0)} |
| edges = nx.draw_networkx_edges(G, pos=pos, arrowstyle=arrowstyle) |
|
|
| arrowstyle = ( |
| itertools.repeat(arrowstyle) if isinstance(arrowstyle, str) else arrowstyle |
| ) |
|
|
| arrow_objects = { |
| "-|>": mpl.patches.ArrowStyle.CurveFilledB, |
| "-[": mpl.patches.ArrowStyle.BracketB, |
| "<|-|>": mpl.patches.ArrowStyle.CurveFilledAB, |
| } |
|
|
| for fap, expected in zip(edges, arrowstyle): |
| assert isinstance(fap, mpl.patches.FancyArrowPatch) |
| assert isinstance(fap.get_arrowstyle(), arrow_objects[expected]) |
|
|
|
|
| def test_np_edgelist(): |
| |
| nx.draw_networkx(barbell, edgelist=np.array([(0, 2), (0, 3)])) |
|
|
|
|
| def test_draw_nodes_missing_node_from_position(): |
| G = nx.path_graph(3) |
| pos = {0: (0, 0), 1: (1, 1)} |
| with pytest.raises(nx.NetworkXError, match="has no position"): |
| nx.draw_networkx_nodes(G, pos) |
|
|
|
|
| def test_draw_networkx_nodes_node_shape_list_with_scalar_color(subplots): |
| """Ensure draw_networkx_nodes works when node_shape is a Python list. |
| |
| This covers the case where node_shape is a sequence (list) and node_color |
| is a single scalar color, which should be supported. |
| """ |
| fig, ax = subplots |
|
|
| G = nx.empty_graph(5) |
| pos = {i: (i, i) for i in G} |
|
|
| shapes = ["o", "^", "o", "^", "o"] |
|
|
| nodes = nx.draw_networkx_nodes( |
| G, |
| pos, |
| node_color="red", |
| node_shape=shapes, |
| ax=ax, |
| ) |
| |
| assert len(nodes.get_offsets()) > 0 |
| |
| |
| |
|
|
|
|
| |
| |
| @pytest.mark.parametrize("node_shape", ("o", "s")) |
| def test_draw_edges_min_source_target_margins(node_shape, subplots): |
| """Test that there is a wider gap between the node and the start of an |
| incident edge when min_source_margin is specified. |
| |
| This test checks that the use of min_{source/target}_margin kwargs result |
| in shorter (more padding) between the edges and source and target nodes. |
| As a crude visual example, let 's' and 't' represent source and target |
| nodes, respectively: |
| |
| Default: |
| s-----------------------------t |
| |
| With margins: |
| s ----------------------- t |
| |
| """ |
| |
| |
| fig, ax = subplots |
| G = nx.DiGraph([(0, 1)]) |
| pos = {0: (0, 0), 1: (1, 0)} |
| |
| |
| default_patch = nx.draw_networkx_edges(G, pos, ax=ax, node_shape=node_shape)[0] |
| default_extent = default_patch.get_extents().corners()[::2, 0] |
| |
| |
| padded_patch = nx.draw_networkx_edges( |
| G, |
| pos, |
| ax=ax, |
| node_shape=node_shape, |
| min_source_margin=100, |
| min_target_margin=100, |
| )[0] |
| padded_extent = padded_patch.get_extents().corners()[::2, 0] |
|
|
| |
| |
| assert padded_extent[0] > default_extent[0] |
| |
| assert padded_extent[1] < default_extent[1] |
|
|
|
|
| |
| |
| @pytest.mark.parametrize("node_shape", ("o", "s")) |
| def test_draw_edges_min_source_target_margins_individual(node_shape, subplots): |
| """Test that there is a wider gap between the node and the start of an |
| incident edge when min_source_margin is specified. |
| |
| This test checks that the use of min_{source/target}_margin kwargs result |
| in shorter (more padding) between the edges and source and target nodes. |
| As a crude visual example, let 's' and 't' represent source and target |
| nodes, respectively: |
| |
| Default: |
| s-----------------------------t |
| |
| With margins: |
| s ----------------------- t |
| |
| """ |
| |
| |
| fig, ax = subplots |
| G = nx.DiGraph([(0, 1), (1, 2)]) |
| pos = {0: (0, 0), 1: (1, 0), 2: (2, 0)} |
| |
| |
| default_patch = nx.draw_networkx_edges(G, pos, ax=ax, node_shape=node_shape) |
| default_extent = [d.get_extents().corners()[::2, 0] for d in default_patch] |
| |
| |
| padded_patch = nx.draw_networkx_edges( |
| G, |
| pos, |
| ax=ax, |
| node_shape=node_shape, |
| min_source_margin=[98, 102], |
| min_target_margin=[98, 102], |
| ) |
| padded_extent = [p.get_extents().corners()[::2, 0] for p in padded_patch] |
| for d, p in zip(default_extent, padded_extent): |
| |
| |
| assert p[0] > d[0] |
| |
| assert p[1] < d[1] |
|
|
|
|
| def test_nonzero_selfloop_with_single_node(subplots): |
| """Ensure that selfloop extent is non-zero when there is only one node.""" |
| |
| fig, ax = subplots |
| |
| G = nx.DiGraph() |
| G.add_node(0) |
| G.add_edge(0, 0) |
| |
| patch = nx.draw_networkx_edges(G, {0: (0, 0)})[0] |
| |
| bbox = patch.get_extents() |
| assert bbox.width > 0 and bbox.height > 0 |
|
|
|
|
| def test_nonzero_selfloop_with_single_edge_in_edgelist(subplots): |
| """Ensure that selfloop extent is non-zero when only a single edge is |
| specified in the edgelist. |
| """ |
| |
| fig, ax = subplots |
| |
| G = nx.path_graph(2, create_using=nx.DiGraph) |
| G.add_edge(1, 1) |
| pos = {n: (n, n) for n in G.nodes} |
| |
| patch = nx.draw_networkx_edges(G, pos, edgelist=[(1, 1)])[0] |
| |
| bbox = patch.get_extents() |
| assert bbox.width > 0 and bbox.height > 0 |
|
|
|
|
| def test_apply_alpha(): |
| """Test apply_alpha when there is a mismatch between the number of |
| supplied colors and elements. |
| """ |
| nodelist = [0, 1, 2] |
| colorlist = ["r", "g", "b"] |
| alpha = 0.5 |
| rgba_colors = nx.drawing.nx_pylab.apply_alpha(colorlist, alpha, nodelist) |
| assert all(rgba_colors[:, -1] == alpha) |
|
|
|
|
| def test_draw_edges_toggling_with_arrows_kwarg(): |
| """ |
| The `arrows` keyword argument is used as a 3-way switch to select which |
| type of object to use for drawing edges: |
| - ``arrows=None`` -> default (FancyArrowPatches for directed, else LineCollection) |
| - ``arrows=True`` -> FancyArrowPatches |
| - ``arrows=False`` -> LineCollection |
| """ |
| import matplotlib.collections |
| import matplotlib.patches |
|
|
| UG = nx.path_graph(3) |
| DG = nx.path_graph(3, create_using=nx.DiGraph) |
| pos = {n: (n, n) for n in UG} |
|
|
| |
| for G in (UG, DG): |
| edges = nx.draw_networkx_edges(G, pos, arrows=True) |
| assert len(edges) == len(G.edges) |
| assert isinstance(edges[0], mpl.patches.FancyArrowPatch) |
|
|
| |
| for G in (UG, DG): |
| edges = nx.draw_networkx_edges(G, pos, arrows=False) |
| assert isinstance(edges, mpl.collections.LineCollection) |
|
|
| |
| edges = nx.draw_networkx_edges(UG, pos) |
| assert isinstance(edges, mpl.collections.LineCollection) |
| edges = nx.draw_networkx_edges(DG, pos) |
| assert len(edges) == len(G.edges) |
| assert isinstance(edges[0], mpl.patches.FancyArrowPatch) |
|
|
|
|
| @pytest.mark.parametrize("drawing_func", (nx.draw, nx.draw_networkx)) |
| def test_draw_networkx_arrows_default_undirected(drawing_func, subplots): |
| import matplotlib.collections |
|
|
| G = nx.path_graph(3) |
| fig, ax = subplots |
| drawing_func(G, ax=ax) |
| assert any(isinstance(c, mpl.collections.LineCollection) for c in ax.collections) |
| assert not ax.patches |
|
|
|
|
| @pytest.mark.parametrize("drawing_func", (nx.draw, nx.draw_networkx)) |
| def test_draw_networkx_arrows_default_directed(drawing_func, subplots): |
| import matplotlib.collections |
|
|
| G = nx.path_graph(3, create_using=nx.DiGraph) |
| fig, ax = subplots |
| drawing_func(G, ax=ax) |
| assert not any( |
| isinstance(c, mpl.collections.LineCollection) for c in ax.collections |
| ) |
| assert ax.patches |
|
|
|
|
| def test_edgelist_kwarg_not_ignored(subplots): |
| |
| G = nx.path_graph(3) |
| G.add_edge(0, 0) |
| fig, ax = subplots |
| nx.draw(G, edgelist=[(0, 1), (1, 2)], ax=ax) |
| assert not ax.patches |
|
|
|
|
| @pytest.mark.parametrize( |
| ("G", "expected_n_edges"), |
| ([nx.DiGraph(), 2], [nx.MultiGraph(), 4], [nx.MultiDiGraph(), 4]), |
| ) |
| def test_draw_networkx_edges_multiedge_connectionstyle(G, expected_n_edges): |
| """Draws edges correctly for 3 types of graphs and checks for valid length""" |
| for i, (u, v) in enumerate([(0, 1), (0, 1), (0, 1), (0, 2)]): |
| G.add_edge(u, v, weight=round(i / 3, 2)) |
| pos = {n: (n, n) for n in G} |
| |
| for conn_style in [ |
| "arc3,rad=0.1", |
| ["arc3,rad=0.1", "arc3,rad=0.1"], |
| ["arc3,rad=0.1", "arc3,rad=0.1", "arc3,rad=0.2"], |
| ]: |
| nx.draw_networkx_edges(G, pos, connectionstyle=conn_style) |
| arrows = nx.draw_networkx_edges(G, pos, connectionstyle=conn_style) |
| assert len(arrows) == expected_n_edges |
|
|
|
|
| @pytest.mark.parametrize( |
| ("G", "expected_n_edges"), |
| ([nx.DiGraph(), 2], [nx.MultiGraph(), 4], [nx.MultiDiGraph(), 4]), |
| ) |
| def test_draw_networkx_edge_labels_multiedge_connectionstyle(G, expected_n_edges): |
| """Draws labels correctly for 3 types of graphs and checks for valid length and class names""" |
| for i, (u, v) in enumerate([(0, 1), (0, 1), (0, 1), (0, 2)]): |
| G.add_edge(u, v, weight=round(i / 3, 2)) |
| pos = {n: (n, n) for n in G} |
| |
| arrows = nx.draw_networkx_edges( |
| G, pos, connectionstyle=["arc3,rad=0.1", "arc3,rad=0.1", "arc3,rad=0.1"] |
| ) |
| for conn_style in [ |
| "arc3,rad=0.1", |
| ["arc3,rad=0.1", "arc3,rad=0.2"], |
| ["arc3,rad=0.1", "arc3,rad=0.1", "arc3,rad=0.1"], |
| ]: |
| text_items = nx.draw_networkx_edge_labels(G, pos, connectionstyle=conn_style) |
| assert len(text_items) == expected_n_edges |
| for ti in text_items.values(): |
| assert ti.__class__.__name__ == "CurvedArrowText" |
|
|
|
|
| def test_draw_networkx_edge_label_multiedge(): |
| G = nx.MultiGraph() |
| G.add_edge(0, 1, weight=10) |
| G.add_edge(0, 1, weight=20) |
| edge_labels = nx.get_edge_attributes(G, "weight") |
| pos = {n: (n, n) for n in G} |
| text_items = nx.draw_networkx_edge_labels( |
| G, |
| pos, |
| edge_labels=edge_labels, |
| connectionstyle=["arc3,rad=0.1", "arc3,rad=0.2"], |
| ) |
| assert len(text_items) == 2 |
|
|
|
|
| def test_draw_networkx_edge_label_empty_dict(): |
| """Regression test for draw_networkx_edge_labels with empty dict. See |
| gh-5372.""" |
| G = nx.path_graph(3) |
| pos = {n: (n, n) for n in G.nodes} |
| assert nx.draw_networkx_edge_labels(G, pos, edge_labels={}) == {} |
|
|
|
|
| def test_draw_networkx_edges_undirected_selfloop_colors(subplots): |
| """When an edgelist is supplied along with a sequence of colors, check that |
| the self-loops have the correct colors.""" |
| fig, ax = subplots |
| |
| edgelist = [(1, 3), (1, 2), (2, 3), (1, 1), (3, 3), (2, 2)] |
| edge_colors = ["pink", "cyan", "black", "red", "blue", "green"] |
|
|
| G = nx.Graph(edgelist) |
| pos = {n: (n, n) for n in G.nodes} |
| nx.draw_networkx_edges(G, pos, ax=ax, edgelist=edgelist, edge_color=edge_colors) |
|
|
| |
| assert len(ax.patches) == 3 |
|
|
| |
| |
| |
| sl_points = np.array(edgelist[-3:]) + np.array([0, 0.1]) |
|
|
| |
| |
| for fap, clr, slp in zip(ax.patches, edge_colors[-3:], sl_points): |
| assert fap.get_path().contains_point(slp) |
| assert mpl.colors.same_color(fap.get_edgecolor(), clr) |
|
|
|
|
| @pytest.mark.parametrize( |
| "fap_only_kwarg", |
| ( |
| {"arrowstyle": "-"}, |
| {"arrowsize": 20}, |
| {"connectionstyle": "arc3,rad=0.2"}, |
| {"min_source_margin": 10}, |
| {"min_target_margin": 10}, |
| ), |
| ) |
| def test_user_warnings_for_unused_edge_drawing_kwargs(fap_only_kwarg, subplots): |
| """Users should get a warning when they specify a non-default value for |
| one of the kwargs that applies only to edges drawn with FancyArrowPatches, |
| but FancyArrowPatches aren't being used under the hood.""" |
| G = nx.path_graph(3) |
| pos = {n: (n, n) for n in G} |
| fig, ax = subplots |
| |
| |
| kwarg_name = list(fap_only_kwarg.keys())[0] |
| with pytest.warns( |
| UserWarning, match=f"\n\nThe {kwarg_name} keyword argument is not applicable" |
| ): |
| nx.draw_networkx_edges(G, pos, ax=ax, **fap_only_kwarg) |
| |
| |
| with warnings.catch_warnings(): |
| |
| warnings.simplefilter("error") |
| warnings.filterwarnings("ignore", category=DeprecationWarning) |
| nx.draw_networkx_edges(G, pos, ax=ax, arrows=True, **fap_only_kwarg) |
|
|
|
|
| @pytest.mark.parametrize("draw_fn", (nx.draw, nx.draw_circular)) |
| def test_no_warning_on_default_draw_arrowstyle(draw_fn, subplots): |
| |
| fig, ax = subplots |
| G = nx.cycle_graph(5) |
| with warnings.catch_warnings(record=True) as w: |
| draw_fn(G, ax=ax) |
| assert len(w) == 0 |
|
|
|
|
| @pytest.mark.parametrize("hide_ticks", [False, True]) |
| @pytest.mark.parametrize( |
| "method", |
| [ |
| nx.draw_networkx, |
| nx.draw_networkx_edge_labels, |
| nx.draw_networkx_edges, |
| nx.draw_networkx_labels, |
| nx.draw_networkx_nodes, |
| ], |
| ) |
| def test_hide_ticks(method, hide_ticks, subplots): |
| G = nx.path_graph(3) |
| pos = {n: (n, n) for n in G.nodes} |
| _, ax = subplots |
| method(G, pos=pos, ax=ax, hide_ticks=hide_ticks) |
| for axis in [ax.xaxis, ax.yaxis]: |
| assert bool(axis.get_ticklabels()) != hide_ticks |
|
|
|
|
| @pytest.mark.parametrize( |
| "style", ["angle", "angle3", "arc", "arc3,rad=0.0", "bar,fraction=0.1"] |
| ) |
| def test_edge_label_all_connectionstyles(subplots, style): |
| """ |
| Check that FancyArrowPatches with all `connectionstyle`s are supported |
| in edge label rendering. See gh-7735 and gh-8106. |
| """ |
| fig, ax = subplots |
| edge = (0, 1) |
| G = nx.DiGraph([edge]) |
| pos = {n: (n, 0) for n in G} |
|
|
| name = style.split(",")[0] |
| labels = nx.draw_networkx_edge_labels( |
| G, pos, edge_labels={edge: "edge"}, connectionstyle=style |
| ) |
|
|
| hmid = (pos[0][0] + pos[1][0]) / 2 |
| vmid = (pos[0][1] + pos[1][1]) / 2 |
| if name in {"arc", "arc3"}: |
| assert labels[edge].x, labels[edge].y == pytest.approx((hmid, vmid)) |
| elif name == "bar": |
| assert labels[edge].y < vmid |
|
|
|
|
| @pytest.mark.parametrize("label_pos", [-0.1, 1.1]) |
| def test_edge_label_label_pos(subplots, label_pos): |
| """ |
| Check that label positions can be extrapolated outside [0, 1]. |
| """ |
| fig, ax = subplots |
| edge = (0, 1) |
| G = nx.DiGraph([edge]) |
| pos = {n: (n, n) for n in G} |
| lbl = nx.draw_networkx_edge_labels( |
| G, pos, edge_labels={edge: "edge"}, label_pos=label_pos, connectionstyle="angle" |
| ) |
|
|
| assert lbl[edge].x, lbl[edge].y == pytest.approx((label_pos, label_pos)) |
|
|