phylogenie 2.1.8__py3-none-any.whl → 2.1.10__py3-none-any.whl

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.

Potentially problematic release.


This version of phylogenie might be problematic. Click here for more details.

phylogenie/plot.py CHANGED
@@ -1,39 +1,78 @@
1
+ from enum import Enum
2
+
1
3
  import matplotlib.colors as mcolors
4
+ import matplotlib.patches as mpatches
2
5
  import matplotlib.pyplot as plt
6
+ from matplotlib.axes import Axes
3
7
 
4
8
  from phylogenie import Tree
5
9
  from phylogenie.tree import Tree
6
10
  from phylogenie.utils import get_times
7
11
 
8
12
 
13
+ class Coloring(str, Enum):
14
+ DISCRETE = "discrete"
15
+ CONTINUOUS = "continuous"
16
+
17
+
9
18
  def plot_tree(
10
19
  tree: Tree,
11
- ax: plt.Axes | None = None, # pyright: ignore
20
+ ax: Axes | None = None,
12
21
  color_by: str | None = None,
13
22
  default_color: str = "black",
14
- cmap: str = "tab20",
15
- ) -> plt.Axes: # pyright: ignore
23
+ coloring: str | Coloring | None = None,
24
+ cmap: str | None = None,
25
+ show_legend: bool = True,
26
+ ) -> Axes:
16
27
  if ax is None:
17
28
  ax = plt.gca()
18
29
 
19
30
  xs = get_times(tree)
20
- ys = {node.name: i for i, node in enumerate(tree.inorder_traversal())}
31
+ ys = {node: i for i, node in enumerate(tree.inorder_traversal())}
32
+
21
33
  if color_by is not None:
22
34
  features = set(node.get(color_by) for node in tree)
23
- feature_colors = {
24
- f: mcolors.to_hex(plt.get_cmap(cmap, len(features))(i))
25
- for i, f in enumerate(features)
26
- }
27
- colors = {node.name: feature_colors[node.get(color_by)] for node in tree}
35
+ if coloring is None and any(isinstance(f, float) for f in features):
36
+ coloring = Coloring.CONTINUOUS
37
+ elif coloring is None:
38
+ coloring = Coloring.DISCRETE
39
+ if coloring == Coloring.DISCRETE:
40
+ if any(isinstance(f, float) for f in features):
41
+ raise ValueError(
42
+ "Discrete coloring selected but feature values are not all categorical."
43
+ )
44
+ cmap = "tab20" if cmap is None else cmap
45
+ colormap = plt.get_cmap(cmap, len(features))
46
+ feature_colors = {
47
+ f: mcolors.to_hex(colormap(i)) for i, f in enumerate(features)
48
+ }
49
+ colors = {node: feature_colors[node.get(color_by)] for node in tree}
50
+ legend_handles = [
51
+ mpatches.Patch(color=feature_colors[f], label=str(f)) for f in features
52
+ ]
53
+ if show_legend:
54
+ ax.legend(handles=legend_handles, title=color_by) # pyright: ignore
55
+ elif coloring in {Coloring.CONTINUOUS}:
56
+ cmap = "viridis" if cmap is None else cmap
57
+ values = list(map(float, features))
58
+ norm = mcolors.Normalize(vmin=min(values), vmax=max(values))
59
+ colormap = plt.get_cmap(cmap)
60
+ colors = {node: colormap(norm(float(node.get(color_by)))) for node in tree}
61
+ else:
62
+ raise ValueError(
63
+ f"Unknown coloring method: {coloring}. Choices are {list(Coloring)}."
64
+ )
28
65
  else:
29
- colors = {node.name: default_color for node in tree}
66
+ colors = {node: default_color for node in tree}
30
67
 
31
68
  for node in tree:
69
+ x1, y1 = xs[node.name], ys[node]
32
70
  if node.parent is None:
71
+ ax.hlines(y=y1, xmin=0, xmax=x1, color=colors[node]) # pyright: ignore
33
72
  continue
34
- x0, y0 = xs[node.parent.name], ys[node.parent.name]
35
- x1, y1 = xs[node.name], ys[node.name]
36
- ax.plot([x0, x0], [y0, y1], color=colors[node.name]) # pyright: ignore
37
- ax.plot([x0, x1], [y1, y1], color=colors[node.name]) # pyright: ignore
73
+ x0, y0 = xs[node.parent.name], ys[node.parent]
74
+ ax.vlines(x=x0, ymin=y0, ymax=y1, color=colors[node]) # pyright: ignore
75
+ ax.hlines(y=y1, xmin=x0, xmax=x1, color=colors[node]) # pyright: ignore
76
+
38
77
  ax.set_yticks([]) # pyright: ignore
39
78
  return ax
phylogenie/tree.py CHANGED
@@ -22,6 +22,14 @@ class Tree:
22
22
  def features(self) -> dict[str, Any]:
23
23
  return self._features.copy()
24
24
 
25
+ @property
26
+ def time_to_parent(self) -> float:
27
+ if self.parent is None and self.branch_length is None:
28
+ return 0.0
29
+ if self.branch_length is None:
30
+ raise ValueError(f"Branch length of node {self.name} is not set.")
31
+ return self.branch_length
32
+
25
33
  def add_child(self, child: "Tree") -> "Tree":
26
34
  child._parent = self
27
35
  self._children.append(child)
@@ -69,14 +77,9 @@ class Tree:
69
77
  def get_leaves(self) -> tuple["Tree", ...]:
70
78
  return tuple(node for node in self if not node.children)
71
79
 
72
- def parse_branch_length(self) -> float:
73
- if self.branch_length is None:
74
- raise ValueError(f"Branch length of node {self.name} is not set.")
75
- return self.branch_length
76
-
77
80
  def get_time(self) -> float:
78
81
  parent_time = 0 if self.parent is None else self.parent.get_time()
79
- return self.parse_branch_length() + parent_time
82
+ return self.time_to_parent + parent_time
80
83
 
81
84
  def set(self, key: str, value: Any) -> None:
82
85
  self._features[key] = value
phylogenie/utils.py CHANGED
@@ -14,7 +14,7 @@ def get_times(tree: Tree) -> dict[str, float]:
14
14
  times: dict[str, float] = {}
15
15
  for node in tree:
16
16
  parent_time = 0 if node.parent is None else times[node.parent.name]
17
- times[node.name] = node.parse_branch_length() + parent_time
17
+ times[node.name] = node.time_to_parent + parent_time
18
18
  return times
19
19
 
20
20
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: phylogenie
3
- Version: 2.1.8
3
+ Version: 2.1.10
4
4
  Summary: Generate phylogenetic datasets with minimal setup effort
5
5
  Author: Gabriele Marino
6
6
  Author-email: gabmarino.8601@gmail.com
@@ -10,13 +10,13 @@ phylogenie/io.py,sha256=nwy8DOknt0HqF9qMeFZHrCmSXpM5AGrU5oajwTtD6vY,3973
10
10
  phylogenie/main.py,sha256=vtvSpQxBNlYABoFQ25czl-l3fIr4QRo3svWVd-jcArw,1170
11
11
  phylogenie/models.py,sha256=pCg9ob0RpLUHwM49x4knKxL4FNPr3-EU_6zMXsvxtAg,370
12
12
  phylogenie/msa.py,sha256=JDGyZUsAq6-m-SQjoCDjAkAZIxfgyl_PDIhdYn5HOow,2064
13
- phylogenie/plot.py,sha256=yLJ331UNta0J3d8qdLBtLdIXsC3Qd0anA1qf_5AUwK8,1310
13
+ phylogenie/plot.py,sha256=oQYlyKnahGSnC6pyWm0wHpGDzGorLjOJMCIms-sUXd0,2856
14
14
  phylogenie/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  phylogenie/skyline/__init__.py,sha256=7pF4CUb4ZCLzNYJNhOjpuTOLTRhlK7L6ugfccNqjIGo,620
16
16
  phylogenie/skyline/matrix.py,sha256=Gl8OgKjtieG0NwPYiPimKI36gefV8fm_OeorjdXxPTs,9146
17
17
  phylogenie/skyline/parameter.py,sha256=EM9qlPt0JhMBy3TbztM0dj24BaGNEy8KWKdTObDKhbI,4644
18
18
  phylogenie/skyline/vector.py,sha256=bJP7_FNX_Klt6wXqsyfj0KX3VNj6-dIhzCKSJuQcOV0,7115
19
- phylogenie/tree.py,sha256=63A--s8C8K685KzZ_3hslkKM-lpqSM39-VFIuwBlIjk,3257
19
+ phylogenie/tree.py,sha256=bWQqbr8CbWZZHKQr-iav4gU2sLbnGRXuBk5_ZJZEZBU,3345
20
20
  phylogenie/treesimulator/__init__.py,sha256=yqS2vtYMhdWSXc9RAnX1dd4zAqSQweMLyVKTnJLfGTU,1106
21
21
  phylogenie/treesimulator/events/__init__.py,sha256=6zSgZ0MEUMvTK4yPlSolJnRWzCARLS-jYreTzh45mQo,1033
22
22
  phylogenie/treesimulator/events/contact_tracing.py,sha256=_nJ85yhgGkeruQgMHvGpDYoyhheBf8M4LgZWiWdi5dY,4801
@@ -27,9 +27,9 @@ phylogenie/treesimulator/gillespie.py,sha256=LZHB2Ko147E78LoUCtN_BN7NYO1xhMYRy5P
27
27
  phylogenie/treesimulator/model.py,sha256=Ct0lfn6maKtjuFxivWx1MbFHvH3Y-fiJ0XXMdkN3Cak,5775
28
28
  phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
29
29
  phylogenie/typings.py,sha256=GknvAFXyiaWeeYJ8Lk5d6E2VHT-xW6ONEojYbtJYiB8,476
30
- phylogenie/utils.py,sha256=JDe6_8-9AFYf7SuTrqUXTZ7n3CA-goQGg9vTi5UcHB8,878
31
- phylogenie-2.1.8.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
32
- phylogenie-2.1.8.dist-info/METADATA,sha256=-F1xhii9rWBPtXGyjIr0UTVWbsrD7wnswCbw--LoS8I,5476
33
- phylogenie-2.1.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
34
- phylogenie-2.1.8.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
35
- phylogenie-2.1.8.dist-info/RECORD,,
30
+ phylogenie/utils.py,sha256=tVorfhMuRkxogeNu45aBv4SDBflHg4faiIpnHNJPKPY,871
31
+ phylogenie-2.1.10.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
32
+ phylogenie-2.1.10.dist-info/METADATA,sha256=NRjmmmqUcJKklEr5G7mGPe_qrWv0Z7CQxvV1Mkz8sOc,5477
33
+ phylogenie-2.1.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
34
+ phylogenie-2.1.10.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
35
+ phylogenie-2.1.10.dist-info/RECORD,,