Skip to content

Commit eb38330

Browse files
committed
Add some extra graphviz examples
1 parent e8ad333 commit eb38330

File tree

1 file changed

+52
-26
lines changed

1 file changed

+52
-26
lines changed

viz.md

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,18 +1503,12 @@ of which are outlined below.
15031503

15041504
A 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)
15451539
d3arg.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
15821578
import 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"]
15831601
from matplotlib import pyplot as plt
1602+
import string
15841603
15851604
def 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-
15941611
pos=get_graphviz_positions(G)
15951612
edge_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+
16001620
plt.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

Comments
 (0)