phylogenie 2.1.8__tar.gz → 2.1.10__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.
Potentially problematic release.
This version of phylogenie might be problematic. Click here for more details.
- {phylogenie-2.1.8 → phylogenie-2.1.10}/PKG-INFO +1 -1
- phylogenie-2.1.10/phylogenie/plot.py +78 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/tree.py +9 -6
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/utils.py +1 -1
- {phylogenie-2.1.8 → phylogenie-2.1.10}/pyproject.toml +1 -1
- phylogenie-2.1.8/phylogenie/plot.py +0 -39
- {phylogenie-2.1.8 → phylogenie-2.1.10}/LICENSE.txt +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/README.md +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/__init__.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/__init__.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/alisim.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/configs.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/dataset.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/factories.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/trees.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/generators/typeguards.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/io.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/main.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/models.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/msa.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/py.typed +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/skyline/__init__.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/skyline/matrix.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/skyline/parameter.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/skyline/vector.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/__init__.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/events/__init__.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/events/contact_tracing.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/events/core.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/events/mutations.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/features.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/gillespie.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/treesimulator/model.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/typeguards.py +0 -0
- {phylogenie-2.1.8 → phylogenie-2.1.10}/phylogenie/typings.py +0 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
import matplotlib.colors as mcolors
|
|
4
|
+
import matplotlib.patches as mpatches
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
from matplotlib.axes import Axes
|
|
7
|
+
|
|
8
|
+
from phylogenie import Tree
|
|
9
|
+
from phylogenie.tree import Tree
|
|
10
|
+
from phylogenie.utils import get_times
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Coloring(str, Enum):
|
|
14
|
+
DISCRETE = "discrete"
|
|
15
|
+
CONTINUOUS = "continuous"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def plot_tree(
|
|
19
|
+
tree: Tree,
|
|
20
|
+
ax: Axes | None = None,
|
|
21
|
+
color_by: str | None = None,
|
|
22
|
+
default_color: str = "black",
|
|
23
|
+
coloring: str | Coloring | None = None,
|
|
24
|
+
cmap: str | None = None,
|
|
25
|
+
show_legend: bool = True,
|
|
26
|
+
) -> Axes:
|
|
27
|
+
if ax is None:
|
|
28
|
+
ax = plt.gca()
|
|
29
|
+
|
|
30
|
+
xs = get_times(tree)
|
|
31
|
+
ys = {node: i for i, node in enumerate(tree.inorder_traversal())}
|
|
32
|
+
|
|
33
|
+
if color_by is not None:
|
|
34
|
+
features = set(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
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
colors = {node: default_color for node in tree}
|
|
67
|
+
|
|
68
|
+
for node in tree:
|
|
69
|
+
x1, y1 = xs[node.name], ys[node]
|
|
70
|
+
if node.parent is None:
|
|
71
|
+
ax.hlines(y=y1, xmin=0, xmax=x1, color=colors[node]) # pyright: ignore
|
|
72
|
+
continue
|
|
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
|
+
|
|
77
|
+
ax.set_yticks([]) # pyright: ignore
|
|
78
|
+
return ax
|
|
@@ -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.
|
|
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
|
|
@@ -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.
|
|
17
|
+
times[node.name] = node.time_to_parent + parent_time
|
|
18
18
|
return times
|
|
19
19
|
|
|
20
20
|
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import matplotlib.colors as mcolors
|
|
2
|
-
import matplotlib.pyplot as plt
|
|
3
|
-
|
|
4
|
-
from phylogenie import Tree
|
|
5
|
-
from phylogenie.tree import Tree
|
|
6
|
-
from phylogenie.utils import get_times
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def plot_tree(
|
|
10
|
-
tree: Tree,
|
|
11
|
-
ax: plt.Axes | None = None, # pyright: ignore
|
|
12
|
-
color_by: str | None = None,
|
|
13
|
-
default_color: str = "black",
|
|
14
|
-
cmap: str = "tab20",
|
|
15
|
-
) -> plt.Axes: # pyright: ignore
|
|
16
|
-
if ax is None:
|
|
17
|
-
ax = plt.gca()
|
|
18
|
-
|
|
19
|
-
xs = get_times(tree)
|
|
20
|
-
ys = {node.name: i for i, node in enumerate(tree.inorder_traversal())}
|
|
21
|
-
if color_by is not None:
|
|
22
|
-
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}
|
|
28
|
-
else:
|
|
29
|
-
colors = {node.name: default_color for node in tree}
|
|
30
|
-
|
|
31
|
-
for node in tree:
|
|
32
|
-
if node.parent is None:
|
|
33
|
-
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
|
|
38
|
-
ax.set_yticks([]) # pyright: ignore
|
|
39
|
-
return ax
|
|
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
|