@@ -1503,18 +1503,12 @@ of which are outlined below.
15031503
15041504A tree sequence can be treated as a specific form of (directed)
15051505[ graph] ( https://en.wikipedia.org/wiki/Graph_(discrete_mathematics) ) consisting
1506- of nodes connected by edges. Standard graph manipulation and visualization software,
1507- such as [ graphviz] ( https://graphviz.org ) can therefore be used to represent tree sequence
1508- topologies. The {ref}` sec_args ` tutorial shows how a tree sequence can be converted into
1509- a networkx graph, and then plotted using graphviz, as below:
1510-
1511- ``` {code-cell} ipython3
1512- ```
1513-
1514- There is also a recent project, [ tskit_arg_visualizer] ( https://github.com/kitchensjn/tskit_arg_visualizer )
1515- which will directly draw a ` tskit ` graph. Below is an example, using the ` variable_edge_width ` function to
1516- highlight the spans of genome inherited through different routes. The plot is interactive: try
1517- dragging a node or hovering over a genomic segment.
1506+ of nodes connected by edges. Standard graph visualization software,
1507+ such as [ graphviz] ( https://graphviz.org ) can therefore be used to depict tree sequence
1508+ topologies. Alternatively, the [ tskit_arg_visualizer] ( https://github.com/kitchensjn/tskit_arg_visualizer )
1509+ project will draw a interactive ` tskit ` graph directly. Below is an example, which uses the
1510+ ` variable_edge_width ` option to highlight the spans of genome inherited through different routes.
1511+ Nodes can be dragged horizontally and embedded trees highlighted:
15181512
15191513``` {code-cell} ipython3
15201514:"tags": ["hide-input"]
@@ -1545,10 +1539,12 @@ d3arg = tskit_arg_visualizer.D3ARG.from_ts(ts=full_arg_ts)
15451539d3arg.draw(width=500, height=500, edge_type="ortho", sample_order=tip_order)
15461540```
15471541
1548- For a more general treatment of the tree sequence as a graph, standard graph visualization
1549- packages such as [ graphviz] ( https://graphviz.org ) can be useful. For this, it can be helpful
1550- convert the tree sequence to a networkx graph first, as described in the {ref}` sec_args_other_analysis `
1551- section of the {ref}` sec_args ` tutorial.
1542+ For more general graph plots, it can be helpful convert the tree sequence to a
1543+ [ networkx] ( https://networkx.org ) graph first, as described in the
1544+ {ref}` sec_args_other_analysis ` section of the {ref}` sec_args ` tutorial.
1545+ This provides interfaces to graph plotting software such as
1546+ [ graphviz] ( https://graphviz.org ) , which provides the ` dot ` layout engine for
1547+ directed graphs:
15521548
15531549``` {code-cell} ipython3
15541550:"tags": ["hide-input"]
@@ -1580,32 +1576,62 @@ def to_networkx_graph(ts, interval_lists=False):
15801576
15811577``` {code-cell} ipython3
15821578import networkx as nx
1579+ from IPython.display import SVG
1580+
1581+ def graphviz_svg(networkx_graph):
1582+ AG = nx.drawing.nx_agraph.to_agraph(networkx_graph) # Convert to graphviz "agraph"
1583+ nodes_at_time_0 = [k for k, v in networkx_graph.nodes(data=True) if v['time'] == 0]
1584+ AG.add_subgraph(nodes_at_time_0, rank='same') # put time=0 at same rank
1585+ return AG.draw(prog="dot", format="svg")
1586+
1587+ G = to_networkx_graph(ts, interval_lists=True) # Function from the ARG tutorial
1588+ print("Converted `ts` to a networkx graph named `G`")
1589+ print("Plotting using graphviz...")
1590+ SVG(graphviz_svg(G))
1591+ ```
1592+
1593+ Alternatively, you can read the ` graphviz ` positions back into ` networkx `
1594+ and use the ` networkx ` drawing functionality, which
1595+ relies upon the [ matplotlib] ( https://matplotlib.org ) library. This
1596+ allows modification of node colours and symbols, labels, rotations,
1597+ annotations, etc., as shown below:
1598+
1599+ ``` {code-cell} ipython3
1600+ :"tags": ["hide-input"]
15831601from matplotlib import pyplot as plt
1602+ import string
15841603
15851604def get_graphviz_positions(networkx_graph):
15861605 AG = nx.drawing.nx_agraph.to_agraph(networkx_graph) # Convert to graphviz "agraph"
1587- AG.add_subgraph(ts.samples(), rank='same') # put samples at same rank
1606+ nodes_at_time_0 = [k for k, v in networkx_graph.nodes(data=True) if v['time'] == 0]
1607+ AG.add_subgraph(nodes_at_time_0, rank='same') # put time=0 at same rank
15881608 AG.layout(prog="dot") # create the layout, storing positions in the "pos" attribute
15891609 return {n: [float(x) for x in AG.get_node(n).attr["pos"].split(",")] for n in G.nodes()}
15901610
1591- G = to_networkx_graph(ts, interval_lists=True) # Function from the ARG tutorial
1592- print("Converted `ts` to a networkx graph named `G`")
1593-
15941611pos=get_graphviz_positions(G)
15951612edge_labels = {
15961613 edge[0:2]: "\n".join([f"[{int(i.left)},{int(i.right)})" for i in edge[2]["intervals"]])
15971614 for edge in G.edges(data=True)
15981615}
15991616
1617+ samples = set(ts.samples())
1618+ nonsamples = set(range(ts.num_nodes)) - samples
1619+
16001620plt.figure(figsize=(10, 4))
1601- nx.draw(G, pos, with_labels=True, node_color="#00BBFF", node_size=150, font_size=9)
1602- nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=5);
1621+ # Sample nodes as dark green squares with white text
1622+ nx.draw(G, pos, node_color="#007700", font_size=9, node_size=250, node_shape="s", nodelist=samples, edgelist=[])
1623+ nx.draw_networkx_labels(G, pos, font_size=9, labels={u: u for u in samples}, font_color="white")
1624+ # Others as blue circles with alphabetic labels
1625+ nx.draw(G, pos, node_color="#22CCFF", font_size=9, edgecolors="black", nodelist=nonsamples, edgelist=[])
1626+ nx.draw_networkx_labels(G, pos, font_size=9, labels={u: string.ascii_lowercase[u] for u in nonsamples})
1627+
1628+ nx.draw_networkx_edges(G, pos, edge_color="lightgrey", arrows=False, width=2);
1629+ nx.draw_networkx_edge_labels(G, pos, edge_labels, font_size=7, rotate=False);
16031630```
16041631
1605- Since this plot uses [ matplotlib] ( https://matplotlib.org ) , it is relatively easy to change node
1606- colours, labels, rotations, etc, and to annotate the plot. However, it can be tricky to use the
1607- graphviz layout engine to get particular layouts, for example to place nodes at specific
1608- vertical (time) positions.
1632+ Note, however, that finding node and edge layout positions that avoid too much overlap
1633+ can be tricky, even for the graphviz layout engine, and there is no easy functionality
1634+ to place nodes at specific vertical (time) positions.
16091635
16101636(sec_tskit_viz_other_demographic)=
16111637
0 commit comments