phytreon 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {phytreon-0.2.0 → phytreon-0.2.2}/PKG-INFO +3 -2
- {phytreon-0.2.0 → phytreon-0.2.2}/README.md +2 -1
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/__init__.py +1 -1
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/plot/figure.py +25 -3
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon.egg-info/PKG-INFO +3 -2
- {phytreon-0.2.0 → phytreon-0.2.2}/tests/test_smoke.py +27 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/LICENSE +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/comparative/__init__.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/comparative/ace.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/comparative/stochastic_mapping.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/core/__init__.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/core/io.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/core/tree.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/datasets.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/__init__.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/_search.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/aa_models.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/align.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/bootstrap.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/distance.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/expression.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/lineage.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/matrix.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/ml.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/ml_native.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/parsimony.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/pipeline.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/infer/trim.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/layout/__init__.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/layout/base.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/layout/circular.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/layout/rectangular.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/layout/unrooted.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/plot/__init__.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/plot/backends.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/plot/elements.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/plot/palettes.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/scene.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon/treeops.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon.egg-info/SOURCES.txt +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon.egg-info/dependency_links.txt +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon.egg-info/requires.txt +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/phytreon.egg-info/top_level.txt +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/pyproject.toml +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/setup.cfg +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/tests/test_expression.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/tests/test_lineage.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/tests/test_pipeline.py +0 -0
- {phytreon-0.2.0 → phytreon-0.2.2}/tests/test_protein_ml.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phytreon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Phylogenetic trees and publication figures in Python
|
|
5
5
|
Author-email: Yiheng Du <yiheng.du@slu.edu>
|
|
6
6
|
License: MIT License
|
|
@@ -130,6 +130,7 @@ tr.join_data(meta, on="name") # attach metadata to tips
|
|
|
130
130
|
.tip_labels()
|
|
131
131
|
.support_labels() # node support values
|
|
132
132
|
).save("tree.pdf") # PDF/SVG/PNG -> matplotlib
|
|
133
|
+
# .save("tree.svg") # SVG: editable text -> drop into PowerPoint
|
|
133
134
|
# .save("tree.html") # HTML -> plotly (zoom/hover)
|
|
134
135
|
```
|
|
135
136
|
|
|
@@ -284,7 +285,7 @@ fluently — every method returns the figure, so calls chain.
|
|
|
284
285
|
|
|
285
286
|
| Method | Draws |
|
|
286
287
|
|---|---|
|
|
287
|
-
| `.branches(color=, size=)` | the tree skeleton (e.g. color by lineage) |
|
|
288
|
+
| `.branches(color=, size=)` | the tree skeleton (`size=` sets line width globally; e.g. color by lineage) |
|
|
288
289
|
| `.tip_labels()` / `.node_labels()` / `.support_labels()` | text labels |
|
|
289
290
|
| `.tip_points()` / `.node_points()` / `.points()` | markers (color / size / shape mapping) |
|
|
290
291
|
| `.highlight(node=)` / `.clade_label(...)` | shade / bracket a clade |
|
|
@@ -75,6 +75,7 @@ tr.join_data(meta, on="name") # attach metadata to tips
|
|
|
75
75
|
.tip_labels()
|
|
76
76
|
.support_labels() # node support values
|
|
77
77
|
).save("tree.pdf") # PDF/SVG/PNG -> matplotlib
|
|
78
|
+
# .save("tree.svg") # SVG: editable text -> drop into PowerPoint
|
|
78
79
|
# .save("tree.html") # HTML -> plotly (zoom/hover)
|
|
79
80
|
```
|
|
80
81
|
|
|
@@ -229,7 +230,7 @@ fluently — every method returns the figure, so calls chain.
|
|
|
229
230
|
|
|
230
231
|
| Method | Draws |
|
|
231
232
|
|---|---|
|
|
232
|
-
| `.branches(color=, size=)` | the tree skeleton (e.g. color by lineage) |
|
|
233
|
+
| `.branches(color=, size=)` | the tree skeleton (`size=` sets line width globally; e.g. color by lineage) |
|
|
233
234
|
| `.tip_labels()` / `.node_labels()` / `.support_labels()` | text labels |
|
|
234
235
|
| `.tip_points()` / `.node_points()` / `.points()` | markers (color / size / shape mapping) |
|
|
235
236
|
| `.highlight(node=)` / `.clade_label(...)` | shade / bracket a clade |
|
|
@@ -214,7 +214,15 @@ class TreeFigure:
|
|
|
214
214
|
|
|
215
215
|
# -- composition methods (each returns self so calls chain) ----------
|
|
216
216
|
def branches(self, color="black", size: float = 1.0) -> "TreeFigure":
|
|
217
|
+
"""Draw (or restyle) the tree skeleton. ``size`` is a single width
|
|
218
|
+
applied to every branch in the tree -- there is no per-branch/data
|
|
219
|
+
variation. A figure only ever has one skeleton layer: calling this
|
|
220
|
+
again (e.g. to override the ``skeleton=True`` default added by
|
|
221
|
+
``__init__``) replaces it in place rather than drawing a second,
|
|
222
|
+
overlapping set of lines.
|
|
223
|
+
"""
|
|
217
224
|
from .elements import _Branches
|
|
225
|
+
self._elements = [e for e in self._elements if not isinstance(e, _Branches)]
|
|
218
226
|
return self.add(_Branches(color=color, size=size))
|
|
219
227
|
|
|
220
228
|
def tip_labels(self, **kwargs) -> "TreeFigure":
|
|
@@ -313,16 +321,30 @@ class TreeFigure:
|
|
|
313
321
|
def save(self, path: str, dpi: int = 300, **kwargs) -> str:
|
|
314
322
|
"""Save to ``path``; backend chosen from the file extension.
|
|
315
323
|
|
|
316
|
-
``
|
|
317
|
-
|
|
324
|
+
Supports ``.png`` / ``.jpg`` (raster), ``.pdf`` / ``.svg`` (vector),
|
|
325
|
+
and ``.html`` (interactive plotly). ``dpi`` controls raster
|
|
326
|
+
resolution; remaining kwargs (e.g. ``figsize``) are forwarded to the
|
|
327
|
+
renderer.
|
|
328
|
+
|
|
329
|
+
SVG output keeps every label as a real ``<text>`` element (rather than
|
|
330
|
+
outlined vector paths), so the figure stays fully editable after
|
|
331
|
+
importing it into PowerPoint (Insert → Picture, then Graphics Format →
|
|
332
|
+
Convert to Shape), Illustrator, Inkscape, etc. -- you can recolour,
|
|
333
|
+
move, and re-type the text.
|
|
318
334
|
"""
|
|
319
335
|
ext = path.lower().rsplit(".", 1)[-1]
|
|
320
336
|
if ext == "html":
|
|
321
337
|
self.draw(backend="plotly", **kwargs).write_html(path)
|
|
322
338
|
else: # pdf/svg/png/jpg -> matplotlib
|
|
339
|
+
import matplotlib as mpl
|
|
323
340
|
fig = self.draw(backend="mpl", **kwargs)
|
|
324
341
|
extra = getattr(fig, "_phytreon_extra_artists", None)
|
|
325
|
-
|
|
342
|
+
# for SVG, emit editable <text> (not glyph outlines) so labels
|
|
343
|
+
# remain real, re-typeable text in PowerPoint / vector editors
|
|
344
|
+
rc = {"svg.fonttype": "none"} if ext == "svg" else {}
|
|
345
|
+
with mpl.rc_context(rc):
|
|
346
|
+
fig.savefig(path, bbox_inches="tight", dpi=dpi,
|
|
347
|
+
bbox_extra_artists=extra)
|
|
326
348
|
return path
|
|
327
349
|
|
|
328
350
|
def show(self, backend: str = "mpl", **kwargs):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: phytreon
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Phylogenetic trees and publication figures in Python
|
|
5
5
|
Author-email: Yiheng Du <yiheng.du@slu.edu>
|
|
6
6
|
License: MIT License
|
|
@@ -130,6 +130,7 @@ tr.join_data(meta, on="name") # attach metadata to tips
|
|
|
130
130
|
.tip_labels()
|
|
131
131
|
.support_labels() # node support values
|
|
132
132
|
).save("tree.pdf") # PDF/SVG/PNG -> matplotlib
|
|
133
|
+
# .save("tree.svg") # SVG: editable text -> drop into PowerPoint
|
|
133
134
|
# .save("tree.html") # HTML -> plotly (zoom/hover)
|
|
134
135
|
```
|
|
135
136
|
|
|
@@ -284,7 +285,7 @@ fluently — every method returns the figure, so calls chain.
|
|
|
284
285
|
|
|
285
286
|
| Method | Draws |
|
|
286
287
|
|---|---|
|
|
287
|
-
| `.branches(color=, size=)` | the tree skeleton (e.g. color by lineage) |
|
|
288
|
+
| `.branches(color=, size=)` | the tree skeleton (`size=` sets line width globally; e.g. color by lineage) |
|
|
288
289
|
| `.tip_labels()` / `.node_labels()` / `.support_labels()` | text labels |
|
|
289
290
|
| `.tip_points()` / `.node_points()` / `.points()` | markers (color / size / shape mapping) |
|
|
290
291
|
| `.highlight(node=)` / `.clade_label(...)` | shade / bracket a clade |
|
|
@@ -65,6 +65,33 @@ def test_layouts_render_both_backends(tmp_path):
|
|
|
65
65
|
assert (tmp_path / "circular.html").exists()
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
def test_svg_export_keeps_editable_text(tmp_path):
|
|
69
|
+
# SVG must emit real <text> elements (svg.fonttype="none"), not outlined
|
|
70
|
+
# glyph paths -- so labels stay editable after importing into PowerPoint
|
|
71
|
+
# or a vector editor.
|
|
72
|
+
import re
|
|
73
|
+
tr = pt.datasets.primates()
|
|
74
|
+
out = tmp_path / "tree.svg"
|
|
75
|
+
pt.TreeFigure(tr).tip_labels().save(str(out))
|
|
76
|
+
svg = out.read_text(encoding="utf-8")
|
|
77
|
+
texts = re.findall(r"<text[^>]*>(.*?)</text>", svg, re.S)
|
|
78
|
+
assert texts, "SVG has no <text> elements -- labels were outlined to paths"
|
|
79
|
+
assert "Human" in " ".join(texts) # a real tip label is editable text
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_branches_size_is_a_single_global_override():
|
|
83
|
+
# __init__(skeleton=True) already draws one branches layer; calling
|
|
84
|
+
# .branches(size=...) again must replace it, not stack a second layer
|
|
85
|
+
# on top (a second, differently-sized layer would leave a fringe of the
|
|
86
|
+
# first layer's colour/width showing through).
|
|
87
|
+
from phytreon.plot.elements import _Branches
|
|
88
|
+
tr = pt.datasets.primates()
|
|
89
|
+
p = pt.TreeFigure(tr).branches(size=0.3, color="red")
|
|
90
|
+
layers = [e for e in p._elements if isinstance(e, _Branches)]
|
|
91
|
+
assert len(layers) == 1
|
|
92
|
+
assert layers[0].size == 0.3 and layers[0].color == "red"
|
|
93
|
+
|
|
94
|
+
|
|
68
95
|
def test_neighbor_joining():
|
|
69
96
|
names = ["A", "B", "C", "D"]
|
|
70
97
|
d = [[0, 5, 9, 9], [5, 0, 10, 10], [9, 10, 0, 8], [9, 10, 8, 0]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|