grahamgower / demesdraw Goto Github PK
View Code? Open in Web Editor NEWDrawing functions for Demes demographic models
Home Page: https://grahamgower.github.io/demesdraw/
License: ISC License
Drawing functions for Demes demographic models
Home Page: https://grahamgower.github.io/demesdraw/
License: ISC License
Use msprime.DemographyDebugger.coalescence_rate_trajectory()
.
$ python -Werror -m demesdraw tubes model_B.yaml --log-time
Traceback (most recent call last):
File "/usr/lib/python3.9/runpy.py", line 197, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/usr/lib/python3.9/runpy.py", line 87, in _run_code
exec(code, run_globals)
File "/home/grg/.local/lib/python3.9/site-packages/demesdraw/__main__.py", line 117, in <module>
cli()
File "/home/grg/.local/lib/python3.9/site-packages/demesdraw/__main__.py", line 113, in cli
args.func(args)
File "/home/grg/.local/lib/python3.9/site-packages/demesdraw/__main__.py", line 72, in __call__
demesdraw.tubes(graph, ax=ax, log_time=args.log_time, title=args.title)
File "/home/grg/.local/lib/python3.9/site-packages/demesdraw/tubes.py", line 506, in tubes
ax.add_patch(arr)
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/axes/_base.py", line 2358, in add_patch
self._update_patch_limits(p)
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/axes/_base.py", line 2376, in _update_patch_limits
p = patch.get_path()
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/patches.py", line 4443, in get_path
return self.get_transform().inverted().transform_path(_path)
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/transforms.py", line 1608, in transform_path
return self.transform_path_affine(self.transform_path_non_affine(path))
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/transforms.py", line 2438, in transform_path_non_affine
return self._b.transform_path_non_affine(
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/transforms.py", line 1628, in transform_path_non_affine
x = self.transform_non_affine(path.vertices)
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/transforms.py", line 2245, in transform_non_affine
y_points = y.transform_non_affine(points[:, 1])
File "/home/grg/.local/lib/python3.9/site-packages/matplotlib/scale.py", line 254, in transform_non_affine
return ma.power(self.base, a)
File "/home/grg/.local/lib/python3.9/site-packages/numpy/ma/core.py", line 6849, in power
result = np.where(m, fa, umath.power(fa, fb)).view(basetype)
RuntimeWarning: overflow encountered in power
Exception ignored in: <_io.FileIO name='model_B.yaml' mode='rb' closefd=True>
ResourceWarning: unclosed file <_io.TextIOWrapper name='model_B.yaml' mode='r' encoding='UTF-8'>
Might be nice to be able to specify colors for demes in the same way we specify their positions, with a dictionary mapping deme to color. Gives the user more control of colors rather than a color map, maybe?
Not sure what the best practice is for testing programs with visual output. Manual inspection seems fairly tedious. But we could easily have tests that verify there are no unexpected failures in the various code paths.
At the moment demesdraw uses four packages that require compiling. This makes it hard to use in jupyterlite:
# within a jupyterlite session
import micropip
await micropip.install('demesdraw', keep_going=True)
ValueError Traceback (most recent call last)
Cell In[10], line 17
14 import tskit
15 #import tsinfer
16 #import tsdate
---> 17 await micropip.install('demesdraw', keep_going=True)
File /lib/python3.11/site-packages/micropip/_micropip.py:580, in install(requirements, keep_going, deps, credentials, pre)
578 if transaction.failed:
579 failed_requirements = ", ".join([f"'{req}'" for req in transaction.failed])
--> 580 raise ValueError(
581 f"Can't find a pure Python 3 wheel for: {failed_requirements}\n"
582 f"See: {FAQ_URLS['cant_find_wheel']}\n"
583 )
585 wheel_promises = []
586 # Install built-in packages
ValueError: Can't find a pure Python 3 wheel for: 'ecos>=2', 'fastcache', 'cvxcanon>=0.0.22', 'scs>=1.1.3'
See: https://pyodide.org/en/stable/usage/faq.html#micropip-can-t-find-a-pure-python-wheel
Is there any way of making these packages optional?
In a CI run of a jupyterbook using demesdraw, I just got this, whereas it was working fine with exactly the same code previously (which itself is weird, as a seed is provided in the code below - is the numpy being seeded correctly?)
17 ax = demesdraw.tubes(graph, ax=ax, positions=positions, seed=1)
18 plt.show(ax.figure)
/opt/hostedtoolcache/Python/3.8.10/x64/lib/python3.8/site-packages/demesdraw/tubes.py in tubes(graph, ax, colours, log_time, title, inf_ratio, positions, num_lines_per_migration, seed, optimisation_rounds, labels, fill)
559 if isinstance(migration, demes.AsymmetricMigration):
560 for _ in range(num_lines_per_migration):
561 t = random_migration_time(migration, log_time)
562 migration_line(migration.source, migration.dest, t, **migration_kwargs)
563 else:
/opt/hostedtoolcache/Python/3.8.10/x64/lib/python3.8/site-packages/demesdraw/tubes.py in random_migration_time(migration, log_scale)
551 )
552 else:
--> 553 t = rng.uniform(start_time, migration.end_time)
554 return t
555
_generator.pyx in numpy.random._generator.Generator.uniform()
_common.pyx in numpy.random._common.cont()
_common.pyx in numpy.random._common.check_constraint()
ValueError: high - low < 0
Looks like I forgot this, so mergify isn't doing its job.
log_w
from schematic()
. It's not at all useful.log_x
and log_y
to be log_time
and log_size
. These are more descriptive, and more future proof if we ever add a "rotate figure 90 degrees" option.In external scripts I regularly use utils.get_axes()
and copy/paste size_max
Lines 388 to 392 in 59f6afb
And any other cool things that moments
facilitates.
One Path for the line, and one for the discontinuities. This will leave fewer svg "strokes" in the output file, which will be (1) smaller, and (2) easier to edit in drawing programs like inkscape.
Hello!
I have recently begun using demesdraw
and have really enjoyed it! I was wondering if there is a way to make the output of .tube()
to appear more tree-like. For example here are the two demes
graphs for a three-taxon tree which is a subset of the larger four-taxon tree.
Three-taxon tree:
description: IUA
time_units: generations
demes:
- name: P123
epochs:
- {end_time: 2400000.0, start_size: 1000000.0}
- name: P12
ancestors: [P123]
epochs:
- {end_time: 1200000.0, start_size: 1000000.0}
- name: P3
ancestors: [P123]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
- name: P2
ancestors: [P12]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
- name: P1
ancestors: [P12]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
pulses:
- {source: P3, dest: P2, time: 200000.0, proportion: 0.1}
Four-taxon tree:
description: Introgression from P3 to P2 with an introgression proportion of 0.1 at
time 200000.0
time_units: generations
demes:
- name: P1234
epochs:
- {end_time: 16000000.0, start_size: 1000000.0}
- name: P123
ancestors: [P1234]
epochs:
- {end_time: 2400000.0, start_size: 1000000.0}
- name: P4
ancestors: [P1234]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
- name: P12
ancestors: [P123]
epochs:
- {end_time: 1200000.0, start_size: 1000000.0}
- name: P3
ancestors: [P123]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
- name: P2
ancestors: [P12]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
- name: P1
ancestors: [P12]
epochs:
- {end_time: 0.0, start_size: 1000000.0}
pulses:
- {source: P3, dest: P2, time: 200000.0, proportion: 0.1}
I then plot both graphs with the following code:
# Load graphs.
three_taxon = demes.load('./data/demes_yamls/three_taxon_tree_graph_f_01.yaml')
four_taxon = demes.load('./data/demes_yamls/four_taxon_tree_graph_f_01.yaml')
# Visualize the graphs.
fig, ax = plt.subplots(figsize=(12,9))
demesdraw.tubes(three_taxon, ax=ax, title='Three-Taxon Tree', seed=42)
plt.show()
fig, ax = plt.subplots(figsize=(12,9))
demesdraw.tubes(four_taxon, ax=ax, title='Four-Taxon Tree', seed=42)
plt.show()
I find the three-taxon tree visualization very appealing and would like to view the four-taxon tree in that "tree-like" manner as well. I know the documentation says that demesdraw
focus at the moment is to create example for the demes
documentation, but was wondering if there is functionality to make the .tube()
more tree-like?
Thanks!
-Dave
Hi @grahamgower,
I noticed that since new version of demesdraw v0.1.1
Python3.6 is no longer supported. Maybe it is possible to include it back? Some of my projects still use version 3.6 and I would like to use demesdraw
there.
I almost always set this manually, because the current defaults aren't very good.
Right now it's a bit silly, because every other commit probably needs to have a corresponding update to tutorial.ipynb
.
I don't really like this name, but I can't think of a better name either.
msprime
's demography debugger has some methods for computing within and between population coalescence rates as well as probability of finding (e.g. https://github.com/tskit-dev/msprime/blob/main/msprime/demography.py#L2816). Colors and shading of each deme could be set to match the probability that a lineage that starts in a given deme at a given sampling time is in each other deme. I think this would be a really awesome way to visualize the movement and mixing of lineages and ancestry through a deme graph.
If a single deme is specified, the alpha for the color for each deme over time is just the probability that the sampled lineage exists in that deme. If two (or more) are specified, you could imagine shading by the probability that two lineages find themselves in the same deme at a given time. Another possibility is to just overlaying colors so that they bleed into each other, and then at the top of the deme graph in the root population, it would end up being black or brown, or however it turns out by mixing the colors.
I'd be happy to try integrating this type of method here, though I don't know if we'd want to make msprime
a dependency of demesdraw
. This is a type of visualization that I've been dreaming about for a while now, and I think it would be super useful for people thinking about how lineages mix in complex multipopulation models.
At the moment the following is available:
python -m demesdraw.size_history ...
# and
python -m demesdraw.schematic ...
The CLI is currently half baked. I'm in two minds here... We could either (1) do something minimal for folks to quickly look at their demes graph, and customisation must be done through the python API; or alternately (2) do everything the API does, and possibly more (like multi panel figures).
And possibly the CLI should instead be driven like:
python -m demesdraw ...
Maybe with subcommands.
While attempting to upload a drawing script in the demes paper, I get this with demes 0.3.0:
python3 drawfig.py
Traceback (most recent call last):
File "drawfig.py", line 16, in <module>
ax = demesdraw.tubes(graph, ax=ax, positions=positions, seed=1, inf_ratio=0.4)
File "/home/kevin/venvs/demes_paper/lib/python3.8/site-packages/demesdraw/tubes.py", line 293, in tubes
ax.add_patch(path_patch)
AttributeError: 'tuple' object has no attribute 'add_patch'
The script is this one:
#!/usr/bin/env python3
import demes
import demesdraw
def size_max(graph):
return max(
max(epoch.start_size, epoch.end_size)
for deme in graph.demes
for epoch in deme.epochs
)
graph = demes.load("models/IM.yaml")
w = 0.8 * size_max(graph)
positions = dict(A=0, X=-w, Y=w)
ax = demesdraw.utils.get_fig_axes(aspect=1)
ax = demesdraw.tubes(graph, ax=ax, positions=positions, seed=1, inf_ratio=0.4) # The offending line.
ax.figure.savefig(
"fig/IM.pdf",
# Save with a transparent background.
transparent=True,
)
Tube plots lack information regarding absolute population sizes. A scale bar is probably the simplest solution.
Its not so much work to create conda-forge package.
Got some tests now, just need to run them when pull requests are opened. (And then open some pull requests).
Maybe use wavy lines.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.