From ef0a0cc1198598e035e153c221c013f7bb190094 Mon Sep 17 00:00:00 2001 From: Yan Wong Date: Sat, 29 May 2021 15:52:50 +0100 Subject: [PATCH 1/3] Fill out viz tutorial --- data/viz_root_mut.ts | Bin 0 -> 6348 bytes data/viz_ts_full.ts | Bin 0 -> 26084 bytes viz.md | 760 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 746 insertions(+), 14 deletions(-) create mode 100644 data/viz_root_mut.ts create mode 100644 data/viz_ts_full.ts diff --git a/data/viz_root_mut.ts b/data/viz_root_mut.ts new file mode 100644 index 0000000000000000000000000000000000000000..dfbcfb1fba5f64d9330fe648230ffdb4f0e439c9 GIT binary patch literal 6348 zcmb`LPi$316vi(qf>laIkx~uThDBp6uZ2>IQF;lQMj?U3V#=!CzBjME;r;X8`x2Jw&EQ_hHy$v9PRQ-Ldob}P_zuW} z51K(1FL_IQ2R!Z1f;Y46|E|T<2jDkDCJ%L62aNxz#VXZ^kbe;*=|=X%;D!1`SQ z&-!;`Az3{A{$%m<;OUQccF+3#2A=WW1%HPPSTfiDFZf3=f7UU7@>{zM*aQB1N1plb z0)IdFu7@zl=ve<}z;oMOdB&dv|F9LG=c`>BoR6*e-H!P){u@?&^7Nk-|9y+^b&Svb zy9gdX8=foA{4ay&{*%wjpX>Ss{CX(*9rGvuJ9r#p&z0x;*Wlo-mgo8Fx<#J#c@#YB z@0vgBzY{#`PoDl+C%fnTA&Vb$tUv2lwASy+vwn5(*hbHlXZ+W}qjTmN|MV^9&-#4` zehUiB{CS@3x>M%!rg6Qig<^P|1pGyV_YH^JZ~$NgjcU%_v+c-Q!A5t#hEV|?!4 zHt@W^$Y<4${1f1Leijhlm4C{L|CJ-p`aEy(KRfcQ|0H~k|t^7~$ATv;#p83B99@oA{p6h9YdyIeD;ujp_bN%P6^^?!4ANTKb@U*|Y%KBXb z&+m_3$M1O7?+SQ+e~{1meq;Uqu=sw*_~bX@;(;5_m1q8sf#>^Ap8m7;|4HyXKZB0> zGyY-l{C*{$6`%Wm96ay;QN+*UIsdAazYjj^{4@Wzto)CFzq?)GplAGZ;Q9MGyUO~V zxAG_N8vk31Kj|2s_5abzpFHc6Q3mfic>W$NApZ6YP!(oWoXgLKrGgojR53B5vPyzN zkOa%6scNwpE6Zdm&d;hcENeleDlmw`nc1XRjmklis|00*;hc)%uv!T#1$9D)mk3i~ zC00?QwNuroprU1Skt$c`f>LuPoC^!}pcLndrC=u3rcFhunh(TDD;-i#PjQW{bT=`_ zdef9y^|D-`f**!r4>EB zm-foYOkuf%ge;{w-9}lf*6O83HH<95mUMBBep~6_z2%gNQH!c`suEQ4I{8TD^_#F# zLoX>)NkVT)xt6wWDgngm<+{?Ir%I|algvuZ;#?zC66trVv864Q?f8$CT5(o_1&xzA<9oZDju_^p^a^i$>4hz_;bE zy&kzw>us*XmTf<%gV*gR@$oR(kwi+xjfnDp zK*?R2+_Bsz(QnX?tU>bSUdehSZ~1h;)doR9ukL%%0+r;?L zf${MJ<6@M~i+QlN5_dI?b!VR9Px6%f+`i*w?6;9Gt&MVGx81e+kGQngM4j_KG~c3m477{m$s6;JsPu~kC69DpXH65!++yb*@1L>Y>d}vT_igsKoca5cO$U4Yt}j1ZJoR{=ztTfmbIjjA z_-whByfQgCInkV8?PdRX9-EUb2VK5V^L^L{`8m_;_i1`i4UO(C4i60P4)zXAj|}e_ Vm=20V10%afhIfvPj^>A_)&Ekpoq_-W literal 0 HcmV?d00001 diff --git a/data/viz_ts_full.ts b/data/viz_ts_full.ts new file mode 100644 index 0000000000000000000000000000000000000000..40f0bfba0db0049eba750284195bbe36658709bb GIT binary patch literal 26084 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 XsPGsg-Uzp)$HduUjf9xAsObLzjFNjA literal 0 HcmV?d00001 diff --git a/viz.md b/viz.md index 9b0def5f..7ac58af2 100644 --- a/viz.md +++ b/viz.md @@ -3,8 +3,8 @@ jupytext: text_representation: extension: .md format_name: myst - format_version: 0.12 - jupytext_version: 1.9.1 + format_version: 0.13 + jupytext_version: 1.10.3 kernelspec: display_name: Python 3 language: python @@ -13,24 +13,756 @@ kernelspec: (sec_tskit_viz)= -# Visualisation +# Visualization -- apologies to EN-US users, this is going to be annoying: colour and color. +**Yan Wong** -## Simple SVG +It is often helpful to visualize a single tree --- or multiple trees along a tree +sequence --- together with sites and mutations. {ref}`Tskit ` +provides functions to do this, outputting either plain ascii or unicode text, or the more +flexible [Scalable Vector Graphics](https://www.w3.org/TR/SVG11/) (SVG) format. +This tutorial illustrates various examples, based on a few 50kb tree sequences generated by +{ref}`msprime `, in which genomes have been sampled from one of 3 +contemporary populations (labelled _A_, _B_, and _C_). -- A guide to doing the most common things we want to do using SVG (colouring - nodes, etc, using draw()) -## Converting SVG +```{code-cell} ipython3 +:"tags": ["hide-input"] +import msprime +import numpy as np +import tskit -- Guide to converting SVG to various outputs. +ts_full = tskit.load("data/viz_ts_full.ts") +### NB: ts_full was made with the following msprime 1.0 commands: +# dem = msprime.Demography.from_species_tree("((A:900,B:900)ab:100,C:1000)abc;", initial_size=1e3) +# samples = {"A": 20, "B": 20, "C":20} # take 20 diploids from terminal populations A, B, C +# ts_full = msprime.sim_ancestry( +# samples, demography=dem, sequence_length=5e4, recombination_rate=1e-8, random_seed=1234 +# ) +first_4_nodes = [0, 1, 2, 3] # ids of the first 4 sample nodes (here, 2 individuals from A) +eight_nodes = first_4_nodes + [40, 41, 80, 81] # Add nodes from individuals in B & C +ts_tiny = ts_full.simplify(first_4_nodes) # a tiny 4-tip TS +ts_small = ts_full.simplify(eight_nodes) # a small 8-tip TS +``` -## Advanced SVG +If you just want a quick look at visualization possibilities, you might want to skip to +the {ref}`sec_tskit_viz_examples`. -- How to do fancy things with the SVG output using CSS etc. Can be a "TODO" - for the first version. +:::{note} +This tutorial is primarily focussed on showing a tree sequence as a set of marginal +trees along a genome. The section titled {ref}`sec_tskit_viz_other_visualizations` +provides examples of other representations of tree sequences, or the processes that +can create them. +::: -## Text output -- Demonstrate some of the things we can do with unicode output. +## Text format + +The {meth}`TreeSequence.draw_text ` and +{meth}`Tree.draw_text ` methods provide +a quick way to print out a tree sequence, or an individual tree within it. They are +primarily useful for looking at topologies in small datasets (e.g. fewer than 20 sampled +genomes), and do not display mutations. + +```{code-cell} ipython3 +# Print a tree sequence +print(ts_tiny.draw_text()) + +print("The first tree in the tree sequence above, but replacing some node ids with names:") +print(ts_tiny.first().draw_text( + # An example of how to change or omit node labels: unspecified nodes are omitted + # The same convention applies to SVG graphics + node_labels={0: "Alice", 1: "Bob", 2:"Chris", 3: "Dora", 6: "MRCA"} +)) +``` + + +## SVG format + +Most users will want to use the SVG drawing functions +{meth}`TreeSequence.draw_svg ` and +{meth}`Tree.draw_svg ` for visualization. +Being a vectorised format, SVG files are suitable for presentations, publication, and +{ref}`editing or converting ` to other graphic formats. Some +basic forms of animation are also possible. The SVG output can also be displayed in a +Jupyter notebook using the IPython `display` and `SVG` functions: + +```{code-cell} ipython3 +from IPython.display import display, SVG +svg_size = (800, 250) # Height and width for the SVG: optional but useful for this notebook +svg_string = ts_tiny.draw_svg( + size=svg_size, + y_axis=True, y_label=" ", # optional: show a time scale on the left + time_scale="rank", x_scale="treewise", # Match the axis coordinate systems to the text view +) +display(SVG(svg_string)) # If the last line in a cell, wrapping this in display() is not needed +``` + +By default, sample nodes are drawn as black squares, and non-sample nodes are drawn as +black circles (but see below for ways to e.g. hide or colour these node symbols - NB. +apologies to US readers: the British spelling of "colour" will be used in the rest of +this tutorial). + + +### Axes and scaling + +For ease of drawing, the text representation and the SVG image above use unconventional +non-linear X and Y coordinate systems. By default, the SVG output uses a more +conventional linear scale for both time (Y) and genome position (X), and indicates the +position of each tree along the genome by an alternating shaded background. Although +more intuitive, linear scales can obscure some features of the trees, for example +causing labels to overlap: + +```{code-cell} ipython3 +SVG(ts_tiny.draw_svg(size=svg_size, y_axis=True)) +``` + +One way to avoid overlapping labels on the Y axis is to use the ``y_ticks`` parameter, +which will be used in most subsequent examples in this tutorial. + +### Larger tree sequences + +So far, we have plotted only very small tree sequences. To visualize larger tree +sequences it is sometimes advisable to focus on a small region of the genome, possibly +even a single tree. The ``x_lim`` parameter allows you to plot the part of a tree +sequence that spans a particular genomic region: here's a slighly larger tree sequence +with 8 samples, but where we've restricted the amount of the tree sequence we plot: + +```{code-cell} ipython3 +x_limits = [5000, 15000] +# Create evenly-spaced y tick positions to avoid overlap +y_tick_pos = [0, 1000, 2000, 3000, 4000] + +print("The tree sequence between positions {} and {} ...".format(*x_limits)) +display(SVG(ts_small.draw_svg(y_axis=True, y_ticks=y_tick_pos, x_lim=x_limits))) + +third_tree = ts_small.at_index(2) +print("... or just look at (say) the third tree") +display(SVG(third_tree.draw_svg())) +``` + +As the number of sample nodes increases, internal nodes often bunch up at recent time +points, obscuring relationships. Setting ``time_scale="rank"``, as in the first SVG plot, +is one way to solve this. Another is to use a log-scale on the time axis, which can be +done by specifying ``time_scale="log_time"``, as below. To compare node times across the +plot, this example also uses the ``y_gridlines`` option, which puts a very faint grid +line at each y tick (if you are finding the lines difficult to see, note that the line +intensity, along with many other plot features, can be modified through +{ref}`styling `, which we also use in this example to avoid +overlapping text by shrinking the node labels and rotating those associated with leaves; +styling is detailed {ref}`later in this tutorial `). + +(sec_tskit_viz_large_tree_sequence)= + +```{code-cell} ipython3 +print("A larger tree, on a log timescale") +wide_fmt = (1200, 250) +# Create a stylesheet that shrinks labels and rotates leaf labels, to avoid overlap +node_label_style = ( + ".node > .lab {font-size: 80%}" + ".leaf > .lab {text-anchor: start; transform: rotate(90deg) translate(6px)}" +) +SVG(ts_full.first().draw_svg( + size=wide_fmt, + time_scale="log_time", + y_gridlines=True, + y_axis=True, + y_ticks=[1, 10, 100, 1000], + style=node_label_style)) +``` + +### Plotting mutations + +The SVG visualization also allows mutations to displayed on the tree or tree sequence. +Here are the same plots as above but where the tree sequence now contains mutations. Each +mutation is plotted as a red cross on the branch where it occurs. This mutation symbol is +placed either at the mutation's known time, or spaced evenly along the branch (if the +mutation time is unknown or the ``time_scale`` parameter has been set to ``"rank"``). +By default, each mutation is also labelled with its {class}`mutation ID`. + +If the X axis is shown (which it is by default when drawing a tree sequence, but not +when drawing an individual tree) then the sites are plotted using a black tickmark above +the axis line. Each plotted mutation at the site is then overlaid on top of this +as a red downwards-pointing chevron. + +```{code-cell} ipython3 +ts_mutated = msprime.sim_mutations(ts_small, rate=1e-7, random_seed=342) +SVG(ts_mutated.draw_svg(y_axis=True, y_ticks=y_tick_pos, x_lim=x_limits)) +``` + +Note that, unusually, the rightmost site on the axis has more than one stacked chevron, +indicating that multiple mutations in the tree occur at the same site. These could be +mutations to different allelic states, or recurrent/back mutations. In this case the +mutations, 14 and 15 (above nodes 1 and 6) are recurrent mutations from T to G. + +```{code-cell} ipython3 +:"tags": ["hide-input"] +site_descr = str(next(ts_mutated.at_index(2).sites())) +print(site_descr.replace("[", "[\n ").replace("),", "),\n ").replace("],", "],\n")) +``` + + +#### Which mutations are shown? + +When using the ``x_lim`` parameter, only the mutations in the plotted region are shown. +For the third tree in the tree sequence visualization above, we thus haven't plotted +mutations above position 15000. We can see *all* the mutations in the tree by changing the +plot region, or simply plotting the tree itself: + + +```{code-cell} ipython3 +third_tree = ts_mutated.at_index(2) +print(f"The third tree in the mutated tree sequence, which covers {third_tree.interval}") +SVG(third_tree.draw_svg(size=(200, 300))) +``` + +(sec_tskit_viz_extra_mutations)= + +However, when plotting a single tree it may not be evident that identical branches +may exist in several adjacent trees, indicating an {ref}`edge ` +that persists across adjacent trees. For instance the rightmost branch in the tree above, +from node 10 down to 7, exists in the previous two trees too. Indeed, this edge has a +mutation on it at position 6295, in the first tree. This mutation is not plotted in the +tree above, but if you want *all* the mutations on each edge to be plotted, you can set +the ``all_edge_mutations`` parameter to ``True``. This adds any extra mutations that are +associated with an edge in the tree but which fall outside the interval of that tree; by +default these mutations are drawn in a slightly different shade (e.g. mutation 64 below). + +```{code-cell} ipython3 +SVG(third_tree.draw_svg(size=(200, 300), all_edge_mutations=True)) +``` + +### Labelling + +Although the default node and mutation labels show unique identifiers, they are't +terribly intuituive. The ``node_labels`` and ``mutation_labels`` parameters can be used +to set more meaningful labels (for example from the tree sequence {ref}`sec_metadata`). + +```{code-cell} ipython3 +nd_labels = {} # An array of labels for the nodes +for n in ts_mutated.nodes(): + # Set sample node labels from metadata. Here we use the population name, but you might want + # to use the *individual* name instead, if the individuals in your tree sequence have names + if n.is_sample(): + nd_labels[n.id] = ts_mutated.population(n.population).metadata["name"] + +mut_labels = {} # An array of labels for the mutations +for mut in ts_mutated.mutations(): # Make pretty labels showing the change in state + site = ts_mutated.site(mut.site) + older_mut = mut.parent >= 0 # is there an older mutation at the same position? + prev = ts_mutated.mutation(mut.parent).derived_state if older_mut else site.ancestral_state + mut_labels[mut.id] = f"{prev}→{mut.derived_state}" + +SVG(ts_mutated.draw_svg( + y_axis=True, y_ticks=y_tick_pos, x_lim=x_limits, + node_labels=nd_labels, + mutation_labels=mut_labels + )) +``` + +(sec_tskit_viz_styling)= + +### Styling + +The SVG output produced by tskit contains a large number of +[classes](https://www.w3.org/TR/SVG2/styling.html#ClassAttribute) which can be used to +target different elements of the drawing, allowing them to be hidden, styled, or +otherwise manipulated. This is done by passing a +[cascading style sheet (CSS)](https://www.w3.org/TR/SVG/styling.html) string to +``draw_svg``. A common use of styles is to colour nodes by their population: + +```{code-cell} ipython3 +styles = [] +# Create a style for each population, programmatically (or just type the string by hand) +for colour, p in zip(['red', 'green', 'blue'], ts_full.populations()): + # target the symbols only (class "sym") + s = f".node.p{p.id} > .sym " + "{" + f"fill: {colour}" + "}" + styles.append(s) + print(f'"{s}" applies to nodes from population {p.metadata["name"]} (id {p.id})') +css_string = " ".join(styles) +print(f'CSS string applied:\n "{css_string}"') + +SVG(ts_full.first().draw_svg( + size=wide_fmt, + node_labels={}, # Remove all node labels for a clearer viz + style=css_string, # Apply the stylesheet +)) +``` + +Colouring nodes by population makes it immediately clear that, while the tree structure +does not exactly reflect the population divisions, there's still considerable +population substructure present in this larger tree. + +:::{todo} +The (older) ``Tree.draw()`` function also has a ``node_colour`` argument that can be +used to colour tree nodes, which is used in some of the other tskit tutorials. Under the +hood, this function simply sets appropriate SVG styles on nodes. We intend to make it +easier to set colours in a similar way: see https://github.com/tskit-dev/tskit/issues/579. +::: + + +The CSS string used to style the tree above takes advantage of the general classes defined +in a tskit SVG file: a node symbol always has a class named ``sym``, which is contained +within a [grouping element](https://www.w3.org/TR/SVG2/struct.html#Groups) of class +``node``. Moreover, elements such as ``node`` have *additional* classes, such as ``p1``, +indicating that the node in this case belongs to the population with ID 1. + +Here are the general tskit SVG classes that can be used to style specific elements. + +* ``tree``: a grouping element containing each tree +* ``node``: a grouping element within a tree, containing a node and its descendant + elements such as a node symbol, an edge, mutations, and other nodes. +* ``mut``: a grouping element containing a mutation symbol and label +* ``extra``: an extra class for mutations {ref}`outside the tree ` +* ``lab``: a label element (for a node, mutation, axis, tick number, etc.) +* ``sym``: a symbol element (e.g. a node, mutation, or site symbol) +* ``edge``: an edge element (i.e. a branch in a tree) +* ``root``, ``leaf`` and ``sample``: additional classes applied to a node group if the + node is a root node, a leaf node or a sample node +* ``rgt`` and ``lft``: additional classes applied to labels for left- or + right-justification + +A few extra classes may be used to style the plot decorations: + +* ``axes``: a grouping element containing the X and Y axes, if either are present +* ``x-axis``, ``y-axis``: more specific grouping elements contained within ``axes`` +* ``tick``: a single tick on an axis, containing a tickmark line and a label +* ``site``: a grouping element representing a site (plotted on the X axis), containing a + site symbol (a tick line) and zero or more mutation groups, each containing a + chevron-shaped mutation symbol +* ``background``: the shaded background of a tree sequence plot +* ``grid``: a gridline + +The additional classes are primarily based on the IDs of trees, nodes, parent (ancestor) +nodes, individuals, populations, mutations, and sites. These class names start with a +single letter (respectively `t`, `n`, `a`, `i`, `p`, `m`, and `s`) followed by a +numerical ID. A comprehensive example is the rightmost leaf (node 7) in the third tree +in the mutated tree sequence, plotted in the previous section but one. In that tree +sequence, node 7 is a leaf and also a +{ref}`sample node `. It belongs to +an {ref}`individual ` with ID 3, comes from the population with +ID 2, and has an immediate ancestor (parent) node with ID 10. It also has mutations with +IDs 16 and 17 above it, and those mutations are associated with site IDs 15 and 16. +In the SVG file, the node (containing a symbol and label) is therefore represented by +the following element: + +``` +... +``` + +This makes it easy to e.g. colour branches that are shared between trees (i.e. that +have the same parent and child): + +```{code-cell} ipython3 +css_string = ".a15.n9 > .edge {stroke: cyan; stroke-width: 2px}" # branches from 15->9 +SVG(ts_small.draw_svg(time_scale="rank", size=wide_fmt, style=css_string)) +``` + +:::{note} +Branches in multiple trees that have the same parent and child do not always +correspond to a single {ref}`edge ` +in a tree sequence: for example, edges have the +additional constraint that they must belong to _adjacent_ trees. +::: + +#### Moving graphical elements + +We can also use styles to transform elements of the drawing, shifting them into different +locations or changing their orientation. For example, +{ref}`earlier in this tutorial ` we used the +following CSS string to rotate leaf labels: + +```css +.leaf > .lab {text-anchor: start; transform: rotate(90deg) translate(6px)} +``` + +Transformations not only allow us to shift e.g. labels about, but also change the size +of symbols, which can create rather different formatting styles: + +```{code-cell} ipython3 +css_string = ( + # Draw large yellow circles for nodes ... + ".node > .sym {transform: scale(2.2); fill: yellow; stroke: black; stroke-width: 0.5px}" + + # ...but for leaf nodes, override the yellow circle using a more specific CSS target + ".node.leaf > .sym {transform: scale(1); fill:black}" + + # Override default node text position to be based at (0, 0) relative to the node pos + # Note that the .tree specifier is needed to make this more specific than the default + # positioning which is targetted at ".lab.lft" and ".lab.rgt" + ".tree .node > .lab {transform: translate(0, 0); text-anchor: middle; font-size: 7pt}" + + # For leaf nodes, override the above positioning using a subsequent CSS style + ".node.leaf > .lab {transform: translate(0, 12px); font-size: 10pt}" +) +SVG(ts_small.first().draw_svg(style=css_string)) +``` + +:::{note} +Using ``transform`` in styles is an SVG2 feature, and has not yet been implemented in +the software programs Inkscape or librsvg. Therefore if you are +{ref}`converting or editing ` the examples above, symbol sizes +and label positions may be incorrect. The ``symbol_size`` option can be used if you want +to simply change the size of all symbols in the plot, but otherwise you may need +to use the ``chromium`` workaround documented {ref}`here `. +::: + + + +#### Styling and SVG structure + +To take full advantage of the SVG styling capabilities in tskit, it is worth knowing how +the SVG file is structured. In particular tskit SVGs use a hierarchical grouping +structure that reflects the tree topology. This allows easy styling and manipulation of +both individual elements and entire subtrees. Currently, the hierarchical structure of a +simple 2-tip SVG tree produced by tskit looks something like this: + +``` + + + + + + + + + Mutation 0 + + + Node 1 + + + + + Node 0 + + + + Root (Node 2) + + + +``` + +And in a tree sequence plot, the SVG simply consists of a set of such trees, together +with groups containing the background and axes, if required. +``` + + + + + ... + ... + ... + ... + + + + +``` + +#### Styling subtrees + +The nested grouping structure makes it easy to target a node and all its descendants. +For instance, here's how to draw all the edges of node 13 and its descendants using a +thicker blue line: + +```{code-cell} ipython3 +edge_style = ".n13 .edge {stroke: blue; stroke-width: 2px}" +nd_labs = {n: n for n in [0, 1, 2, 3, 4, 5, 6, 7, 13, 17]} +SVG(ts_mutated.draw_svg(x_lim=x_limits, node_labels=nd_labs, style=edge_style)) +``` + +This might not be quite what you expected: the branch leading from node 13 to its parent +(node 17) has also been coloured. That's because the SVG node group deliberately contains +the branch that leads to the parent (this can be helpful, for example, for hiding the +entire subtree leading to node 13, using e.g. ``.n13 {visibility: hidden}``). To colour +the branches *descending* from node 13, you therefore need to target the nodes nested +at least one level deep within the `n13` group. One way to do that is to add an +extra ``.node`` class to the style, e.g. + +```{code-cell} ipython3 +edge_style = ".n13 .node .edge {stroke: blue; stroke-width: 2px}" +# NB to target the edges in only (say) the 1st tree you could use ".t0 .n13 .node .edge ..." +SVG(ts_mutated.draw_svg(x_lim=x_limits, node_labels=nd_labs, style=edge_style)) +``` + +If you want to colour the branches descending from a particular mutation (say mutation 7) +then you need to colour not only the edges, but also *part* of an edge (i.e. the line +that connects a mutation downwards to its associated node). The tskit SVG format provides +a special ```` element to enable this, which is normally made invisible using +``fill: none stroke: none`` in the default stylesheet. Here's an example of activating +this normally-hidden line: + +```{code-cell} ipython3 +default_muts = ".mut .lab {fill: gray} .mut .sym {stroke: gray}" # all other muts in gray +m8_mut = ( + ".m8 .node .edge, " # the descendant edges + ".mut.m8 line, " # activate the hidden line between the mutation and the node + ".mut.m8 .sym " # the mutation symbols on the tree and the axis + "{stroke: red; stroke-width: 2px}" + ".mut.m8 .lab {fill: red}" # colour the label "8" in red too +) +css_string = default_muts + m8_mut +SVG(ts_mutated.draw_svg(x_lim=x_limits, node_labels=nd_labs, style=css_string)) +``` + +#### Restricting styling + +Sometimes the hierarchical nesting leads to styles being applied too widely. For example, +since style selectors include all the descendants of a target, to target just the node +itself (and not its descendants) a slightly different specification is required, +involving, the "``>``" symbol, or +[child combinator](https://www.w3.org/TR/selectors-3/#child-combinators) (we have, +in fact, used it in several previous examples). The following plot shows the difference +when all decendant symbols are targetted, versus just the immediate child symbol: + +```{code-cell} ipython3 +node_style1 = ".n13 .sym {fill: yellow}" # All symbols under node 13 +node_style2 = ".n15 > .sym {fill: cyan}" # Only symbols that are an immediate child of node 15 +css_string = node_style1 + node_style2 +SVG(ts_small.draw_svg(y_axis=True, y_ticks=y_tick_pos, x_lim=x_limits, style=css_string)) +``` + +Another example of modifying the style target is *negation*. This is needed, for example, +to target nodes that are *not* leaves (i.e. internal nodes). One way to do this is to +target *all* the node symbols first, then replace the style with a more specific +targetting of the leaf symbols only: + +```{code-cell} ipython3 +hide_internal_symlabs = ".node > .sym, .node > .lab {visibility: hidden}" +show_leaf_symlabs = ".node.leaf > .sym, .node.leaf > .lab {visibility: visible}" +css_string = hide_internal_symlabs + show_leaf_symlabs +SVG(ts_small.draw_svg(y_axis=True, y_ticks=y_tick_pos, x_lim=x_limits, style=css_string)) +``` + +Alternatively, the ``:not`` selector can be used to target nodes that are *not* leaves, +so the following style specification should produce the same effect: + +``` +style_string = ".node:not(.leaf) > .sym, .node:not(.leaf) > .lab {visibility: hidden}" +``` + +#### More about styling + +NOTE: if your SVG is embedded directly into an HTML page (a common way for jupyter +notebooks to render SVGs), then according to the HTML specifications, any styles applied +to one SVG will apply to all SVGs in the document. To avoid this confusing state of +affairs, we recommend that you tag the SVG with a unique ID using the +``root_svg_attributes`` parameter, then prepend this ID to the style string: + +```{code-cell} ipython3 +SVG(ts_small.draw_svg( + x_lim=x_limits, + root_svg_attributes={'id': "myUID"}, + style="#myUID .background * {fill: #00FF00}", # apply any old style to this specific SVG +)) +``` + +SVG styles allow a huge amount of flexibility in formatting your plot, even extending to +animations. Feel free to browse the {ref}`examples ` for +inspiration. + + +(sec_tskit_viz_converting)= + +### Converting and editing SVG + +#### Converting + +[Inkscape](https://inkscape.org) is an open source SVG editor that can also be scripted +to output bitmap files. + +[Imagemagick](https://imagemagick.org/) is a common piece of software used to convert +between image formats. It can +be configured to delegate to one of several different SVG libraries when converting +SVGs to bitmap formats. Currently, both the librsvg library or the Inkscape library +produce reasonable output, although librsvg currently misaligns some labels due to +[ignoring certain SVG properties](https://gitlab.gnome.org/GNOME/librsvg/-/issues/414). + +(sec_tskit_viz_converting_note)= + +:::{note} +A few styling properties, such as the ``transform`` property, are SVG2 features, +and have not yet been implemented in Inkscape or librsvg. +These properties will therefore not be applied to elements when converted with these +tools. A workaround is to convert the SVG to PDF first, using e.g. the +programmable chromium engine: +``` +chromium --headless --print-to-pdf=out.pdf in.svg +``` +The resulting PDF file can be converted by Inkscape, retaining the correct +transformations. +::: + +#### Editing the SVG + +- Editing can be done in [Inkscape](https://inkscape.org) (subject to the note above) + +:::{todo} +Tips on how to cope with the hierarchical grouping when editing (e.g. in Inkscape using +``Extensions menu > Arrange > Deep Ungroup``, but note that this will mess with the styles!) +::: + + +(sec_tskit_viz_examples)= +## Examples + +### Text examples + +#### Tree orientation +In the text format, trees (but not tree sequences) can be displayed in different orientations +```{code-cell} ipython3 +:"tags": ["hide-input"] +from IPython.display import HTML +orient = "top", "left", "bottom", "right" +html = [] +for o in orient: + tree_string = ts_small.first().draw_text(orientation=o) + html.append(f"
{tree_string}
") +HTML((" "*10).join(html)) +``` + +(sec_tskit_viz_SVG_examples)= +### SVG examples + +#### A standard ts plot +Note that this tree sequence also illustrates a few features which are not normally +produced e.g. by ``msprime`` simulations, in particular a "empty" site (with no +associated mutations) at position 50, and some mutations that occur above root nodes in +the trees. Graphically, root mutations necessitate a line above the root node on which to +place them, so each tree in this SVG has a nominal "root branch" at the top. Normally, +root branches are not drawn, unless the `force_root_branch` parameter is specified. +```{code-cell} ipython3 +:"tags": ["hide-input"] +ts = tskit.load("data/viz_root_mut.ts") +SVG(ts.draw_svg()) +``` + +#### A fancy formatted plot + +Here we have activated the Y axis, and changed the node style. In particular, we have +coloured nodes by time, and increased the internal node symbol size while moving +the internal node labels into the symbol; node labels have also been plotted in a +sans-serif font. Axis tick labels have been changed to avoid potential overlapping (some +Y tick labels have been removed, and the X tick labels rotated). + +```{code-cell} ipython3 +:"tags": ["hide-input"] +import numpy as np +seed=370009 +ts = msprime.sim_ancestry(7, ploidy=1, sequence_length=1000, random_seed=seed, recombination_rate=0.001) +y_ticks = ts.tables.nodes.time +# Thin the tick values so we don't get labels within 0.01 of each other +y_ticks = np.delete(y_ticks, np.argwhere(np.ediff1d(y_ticks) <= 0.01)) + +svg = ts.draw_svg(size=(1000, 350), y_axis=True, y_gridlines=True, y_ticks=y_ticks, style=( + ".tree .lab {font-family: sans-serif}" + ".x-axis .tick .lab {text-anchor: start; transform: rotate(90deg) translate(8px)}" + ".y-axis .grid {stroke: #DDDDDD}" + + ".tree :not(.leaf).node > .lab {transform: translate(0,0); text-anchor:middle; fill: white}" + + ".tree :not(.leaf).node > .sym {transform: scale(3.5)}" + + "".join(f".tree .n{n.id} > .sym {{fill: hsl({int((1-n.time/ts.max_root_time)*260)}, 50%, 50%)}}" for n in ts.nodes()) +)) +SVG(svg) +``` + +#### Highlighted mutations + +Specific mutations can be given a different colour. Moreover, the descendant lineages of +specific mutations can be coloured and the branch colours overlay each other as expected. +Note that in this example, internal node labels and symbols have been hidden for clarity. + +```{code-cell} ipython3 +:"tags": ["hide-input"] +ts = tskit.load("data/viz_root_mut.ts") +css_string = ( + ".edge {stroke: grey}" + ".mut .sym{stroke:pink} .mut text{fill:pink}" + ".mut.m2 .sym, .m2>line, .m2>.node .edge{stroke:blue} .mut.m2 .lab{fill:blue}" + ".mut.m3 .sym, .m3>line, .m3>.node .edge{stroke:cyan} .mut.m3 .lab{fill:cyan}" + ".mut.m4 .sym, .m4>line, .m4>.node .edge{stroke:red} .mut.m4 .lab{fill:red}" + # Hide internal node labels & symbols + ".node:not(.leaf) > .sym, .node:not(.leaf) > .lab {visibility: hidden}" +) +SVG(ts.draw_svg(style=css_string, time_scale="rank", x_lim=[0, 30])) +``` + +#### Leaf, sample & isolated nodes +By default, sample nodes are square and non-sample nodes circular. However, neither need +to be at time 0. Moreover, leaves need not be samples, and samples need not be leaves. +Here we change the previous tree sequence to make some leaves non-samples and some samples +internal nodes. To highlight the change, we have plotted sample nodes in green, and +leaf nodes (if not samples) in blue. + +```{code-cell} ipython3 +:"tags": ["hide-input"] +tables = tskit.load("data/viz_root_mut.ts").dump_tables() +tables.mutations.clear() +tables.sites.clear() +tables.nodes.flags = np.array([0, 0, 1, 1, 0, 0, 0, 1, 1, 0], dtype=tables.nodes.flags.dtype) +ts = tables.tree_sequence() +css_string=".leaf .sym {fill: blue} .sample > .sym {fill: green}" +SVG(ts.draw_svg(style=css_string, x_scale="treewise", time_scale="rank", y_axis=True, y_gridlines=True, x_lim=[0, 10])) +``` + +:::{note} +By definition, if a node is a sample, it must be present in every tree. This +means that there can be sample nodes which are "isolated" in a tree. These are drawn +unconnected to the main topology in one or more trees (e.g. nodes 7 and 8 above). +::: + + +```{code-cell} ipython3 +:"tags": ["hide-input"] +print("SVG animations - TODO") +``` + + +(sec_tskit_viz_other_visualizations)= +## Other visualizations + +As well as visualizing a tree sequence as, well, a sequence of local trees, or by plotting +{ref}`statistical summaries `, other visualizations are possible, some +of which are outlined below. + +### Graph representations + +A tree sequence can be treated as a specific form of (directed) +[graph](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)) consisting +of nodes connected by edges. Standard graph visualization software, such as +[graphviz](https://graphviz.org) can therefore be used to represent tree sequence +topologies. This is a relatively common approach to visualizing the full +"Ancestral Recombination Graph" or ARG (a structure in which some nodes are +"recombination nodes", and which is possible to +{ref}`represent as a tree sequence `). + + +:::{todo} +Link to the ARG tutorial, +[once it is created](https://github.com/tskit-dev/tutorials/issues/43), and show a +picture like this: + +![A tree sequence (ARG) as a graph](https://user-images.githubusercontent.com/36134434/109398193-2ec6b700-7933-11eb-9cbf-99fdfab46df0.png) +(from [here](https://github.com/tskit-dev/tutorials/issues/43#issuecomment-787124425)) +::: + +### Demographic processes + +If you are generating a tree sequence via a {ref}`` +model, then you can visualize a schematic of the demography itself (rather than the +resulting tree sequence) using the [demesdraw](https://grahamgower.github.io/demesdraw/) +software. + +:::{todo} +Add a nice example e.g. + +![Demesdraw example](https://grahamgower.github.io/demesdraw/_images/quickstart_6_0.svg) +::: + +### Geography + +:::{todo} +How to get lat/long information out of a tree sequence and plot ancestors (or a tree) +on a geographical landscape. +::: From f1b75d94ca99f4f13ce5300d3518280ecd2bbd23 Mon Sep 17 00:00:00 2001 From: Yan Wong Date: Wed, 2 Jun 2021 15:53:07 +0100 Subject: [PATCH 2/3] Add an animation --- viz.md | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/viz.md b/viz.md index 7ac58af2..3fd8eb31 100644 --- a/viz.md +++ b/viz.md @@ -712,10 +712,132 @@ means that there can be sample nodes which are "isolated" in a tree. These are d unconnected to the main topology in one or more trees (e.g. nodes 7 and 8 above). ::: +#### Animation + +The classes attached to the SVG also allow elements to be animated. Here's a +[d3.js](https://d3js.org)-based animation of sucessive subtree-prune-and-regraft (SPR) +operations, using the {ref}`ARG representation ` of a +tree sequence to allow identification of pruned edges. ```{code-cell} ipython3 :"tags": ["hide-input"] -print("SVG animations - TODO") +css_string = ".node:not(.sample) > .lab, .node:not(.sample) > .sym {visibility: hidden}" +html_string = r""" +
%s
+ + + + + +""" + +ts = msprime.sim_ancestry( + 5, ploidy=1, + sequence_length=10000, + recombination_rate=0.00005, + random_seed=6787, model="smc_prime", record_full_arg=True) +# record_full_arg needed to track recombination nodes (branch positions) +# random_seed chosen to produce a ts whose leaves are plotted in the same order + +HTML(html_string % (ts.draw_svg(style=css_string), ts.num_trees)) ``` From 76548bffc6c25cc46bc00037e5dcf15ce429db05 Mon Sep 17 00:00:00 2001 From: Yan Wong Date: Thu, 3 Jun 2021 15:42:41 +0100 Subject: [PATCH 3/3] Remove references to non-stable tskit doc links This commit can be reverted once a new tskit version is released --- viz.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/viz.md b/viz.md index 3fd8eb31..d40f8132 100644 --- a/viz.md +++ b/viz.md @@ -58,8 +58,8 @@ can create them. ## Text format -The {meth}`TreeSequence.draw_text ` and -{meth}`Tree.draw_text ` methods provide +The ``TreeSequence.draw_text`` and +``Tree.draw_text`` methods provide a quick way to print out a tree sequence, or an individual tree within it. They are primarily useful for looking at topologies in small datasets (e.g. fewer than 20 sampled genomes), and do not display mutations. @@ -339,7 +339,7 @@ single letter (respectively `t`, `n`, `a`, `i`, `p`, `m`, and `s`) followed by a numerical ID. A comprehensive example is the rightmost leaf (node 7) in the third tree in the mutated tree sequence, plotted in the previous section but one. In that tree sequence, node 7 is a leaf and also a -{ref}`sample node `. It belongs to +sample node. It belongs to an {ref}`individual ` with ID 3, comes from the population with ID 2, and has an immediate ancestor (parent) node with ID 10. It also has mutations with IDs 16 and 17 above it, and those mutations are associated with site IDs 15 and 16. @@ -871,7 +871,7 @@ picture like this: ### Demographic processes -If you are generating a tree sequence via a {ref}`` +If you are generating a tree sequence via a {ref}`Demes ` model, then you can visualize a schematic of the demography itself (rather than the resulting tree sequence) using the [demesdraw](https://grahamgower.github.io/demesdraw/) software.