phylogenie 2.1.4__py3-none-any.whl → 3.1.7__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.
- phylogenie/__init__.py +60 -14
- phylogenie/draw.py +690 -0
- phylogenie/generators/alisim.py +12 -12
- phylogenie/generators/configs.py +26 -4
- phylogenie/generators/dataset.py +3 -3
- phylogenie/generators/factories.py +38 -12
- phylogenie/generators/trees.py +48 -47
- phylogenie/io/__init__.py +3 -0
- phylogenie/io/fasta.py +34 -0
- phylogenie/main.py +27 -10
- phylogenie/mixins.py +33 -0
- phylogenie/skyline/matrix.py +11 -7
- phylogenie/skyline/parameter.py +12 -4
- phylogenie/skyline/vector.py +12 -6
- phylogenie/treesimulator/__init__.py +36 -3
- phylogenie/treesimulator/events/__init__.py +5 -5
- phylogenie/treesimulator/events/base.py +39 -0
- phylogenie/treesimulator/events/contact_tracing.py +38 -23
- phylogenie/treesimulator/events/core.py +21 -12
- phylogenie/treesimulator/events/mutations.py +46 -46
- phylogenie/treesimulator/features.py +49 -0
- phylogenie/treesimulator/gillespie.py +59 -55
- phylogenie/treesimulator/io/__init__.py +4 -0
- phylogenie/treesimulator/io/newick.py +104 -0
- phylogenie/treesimulator/io/nexus.py +50 -0
- phylogenie/treesimulator/model.py +25 -49
- phylogenie/treesimulator/tree.py +196 -0
- phylogenie/treesimulator/utils.py +108 -0
- phylogenie/typings.py +3 -3
- {phylogenie-2.1.4.dist-info → phylogenie-3.1.7.dist-info}/METADATA +13 -15
- phylogenie-3.1.7.dist-info/RECORD +41 -0
- {phylogenie-2.1.4.dist-info → phylogenie-3.1.7.dist-info}/WHEEL +2 -1
- phylogenie-3.1.7.dist-info/entry_points.txt +2 -0
- phylogenie-3.1.7.dist-info/top_level.txt +1 -0
- phylogenie/io.py +0 -107
- phylogenie/tree.py +0 -92
- phylogenie/utils.py +0 -17
- phylogenie-2.1.4.dist-info/RECORD +0 -32
- phylogenie-2.1.4.dist-info/entry_points.txt +0 -3
- {phylogenie-2.1.4.dist-info → phylogenie-3.1.7.dist-info/licenses}/LICENSE.txt +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from math import comb
|
|
3
|
+
|
|
4
|
+
from phylogenie.treesimulator.tree import Tree
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_node_leaf_counts(tree: Tree) -> dict[Tree, int]:
|
|
8
|
+
n_leaves: dict[Tree, int] = {}
|
|
9
|
+
for node in tree.postorder_traversal():
|
|
10
|
+
n_leaves[node] = sum(n_leaves[child] for child in node.children) or 1
|
|
11
|
+
return n_leaves
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_node_depth_levels(tree: Tree) -> dict[Tree, int]:
|
|
15
|
+
depth_levels: dict[Tree, int] = {tree: tree.depth_level}
|
|
16
|
+
for node in tree.iter_descendants():
|
|
17
|
+
depth_levels[node] = depth_levels[node.parent] + 1 # pyright: ignore
|
|
18
|
+
return depth_levels
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_node_depths(tree: Tree) -> dict[Tree, float]:
|
|
22
|
+
depths: dict[Tree, float] = {tree: tree.depth}
|
|
23
|
+
for node in tree.iter_descendants():
|
|
24
|
+
parent_depth = depths[node.parent] # pyright: ignore
|
|
25
|
+
depths[node] = node.branch_length_or_raise() + parent_depth
|
|
26
|
+
return depths
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_node_height_levels(tree: Tree) -> dict[Tree, int]:
|
|
30
|
+
height_levels: dict[Tree, int] = {}
|
|
31
|
+
for node in tree.postorder_traversal():
|
|
32
|
+
height_levels[node] = (
|
|
33
|
+
0
|
|
34
|
+
if node.is_leaf()
|
|
35
|
+
else max(1 + height_levels[child] for child in node.children)
|
|
36
|
+
)
|
|
37
|
+
return height_levels
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_node_heights(tree: Tree) -> dict[Tree, float]:
|
|
41
|
+
heights: dict[Tree, float] = {}
|
|
42
|
+
for node in tree.postorder_traversal():
|
|
43
|
+
heights[node] = (
|
|
44
|
+
0
|
|
45
|
+
if node.is_leaf()
|
|
46
|
+
else max(
|
|
47
|
+
child.branch_length_or_raise() + heights[child]
|
|
48
|
+
for child in node.children
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
return heights
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_mrca(node1: Tree, node2: Tree) -> Tree:
|
|
55
|
+
node1_ancestors = set(node1.iter_upward())
|
|
56
|
+
for node2_ancestor in node2.iter_upward():
|
|
57
|
+
if node2_ancestor in node1_ancestors:
|
|
58
|
+
return node2_ancestor
|
|
59
|
+
raise ValueError(f"No common ancestor found between node {node1} and node {node2}.")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_path(node1: Tree, node2: Tree) -> list[Tree]:
|
|
63
|
+
mrca = get_mrca(node1, node2)
|
|
64
|
+
return [
|
|
65
|
+
*node1.iter_upward(stop=mrca.parent),
|
|
66
|
+
*reversed(list(node2.iter_upward(stop=mrca))),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def count_hops(node1: Tree, node2: Tree) -> int:
|
|
71
|
+
return len(get_path(node1, node2)) - 1
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_distance(node1: Tree, node2: Tree) -> float:
|
|
75
|
+
mrca = get_mrca(node1, node2)
|
|
76
|
+
path = get_path(node1, node2)
|
|
77
|
+
path.remove(mrca)
|
|
78
|
+
return sum(node.branch_length_or_raise() for node in path)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def compute_sackin_index(tree: Tree, normalize: bool = False) -> float:
|
|
82
|
+
depth_levels = get_node_depth_levels(tree)
|
|
83
|
+
sackin_index = sum(dl for node, dl in depth_levels.items() if node.is_leaf())
|
|
84
|
+
if normalize:
|
|
85
|
+
if not tree.is_binary():
|
|
86
|
+
raise ValueError(
|
|
87
|
+
"Normalized Sackin index is only defined for binary trees."
|
|
88
|
+
)
|
|
89
|
+
n = tree.n_leaves
|
|
90
|
+
h = math.floor(math.log2(n))
|
|
91
|
+
min_sackin_index = n * (h + 2) - 2 ** (h + 1)
|
|
92
|
+
max_sackin_index = n * (n - 1) / 2
|
|
93
|
+
return (sackin_index - min_sackin_index) / (max_sackin_index - min_sackin_index)
|
|
94
|
+
return sackin_index
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def compute_mean_leaf_pairwise_distance(tree: Tree) -> float:
|
|
98
|
+
leaves = tree.get_leaves()
|
|
99
|
+
n_leaves = len(leaves)
|
|
100
|
+
if n_leaves < 2:
|
|
101
|
+
return 0.0
|
|
102
|
+
|
|
103
|
+
total_distance = sum(
|
|
104
|
+
get_distance(leaves[i], leaves[j])
|
|
105
|
+
for i in range(n_leaves)
|
|
106
|
+
for j in range(i + 1, n_leaves)
|
|
107
|
+
)
|
|
108
|
+
return total_distance / comb(n_leaves, 2)
|
phylogenie/typings.py
CHANGED
|
@@ -15,6 +15,6 @@ OneOrMany2DScalars = OneOrMany2D[Scalar]
|
|
|
15
15
|
Many2DScalars = Many2D[Scalar]
|
|
16
16
|
Many3DScalars = Many3D[Scalar]
|
|
17
17
|
|
|
18
|
-
Vector1D =
|
|
19
|
-
Vector2D =
|
|
20
|
-
Vector3D =
|
|
18
|
+
Vector1D = tuple[Scalar, ...]
|
|
19
|
+
Vector2D = tuple[Vector1D, ...]
|
|
20
|
+
Vector3D = tuple[Vector2D, ...]
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: phylogenie
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.1.7
|
|
4
4
|
Summary: Generate phylogenetic datasets with minimal setup effort
|
|
5
|
-
|
|
6
|
-
Author-email: gabmarino.8601@gmail.com
|
|
7
|
-
Requires-Python: >=3.10,<4.0
|
|
8
|
-
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
-
Requires-Dist: joblib (>=1.4.2,<2.0.0)
|
|
13
|
-
Requires-Dist: pandas (>=2.2.2,<3.0.0)
|
|
14
|
-
Requires-Dist: pydantic (>=2.11.5,<3.0.0)
|
|
15
|
-
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
16
|
-
Requires-Dist: tqdm (>=4.66.4,<5.0.0)
|
|
5
|
+
Requires-Python: >=3.10
|
|
17
6
|
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE.txt
|
|
8
|
+
Requires-Dist: joblib>=1.5.2
|
|
9
|
+
Requires-Dist: matplotlib>=3.10.7
|
|
10
|
+
Requires-Dist: pandas>=2.3.3
|
|
11
|
+
Requires-Dist: pydantic>=2.12.3
|
|
12
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
13
|
+
Requires-Dist: tqdm>=4.67.1
|
|
14
|
+
Dynamic: license-file
|
|
18
15
|
|
|
19
16
|
<p align="center">
|
|
20
17
|
<img src="https://raw.githubusercontent.com/gabriele-marino/phylogenie/main/logo.png" style="width:100%; height:auto;"/>
|
|
@@ -24,6 +21,8 @@ Description-Content-Type: text/markdown
|
|
|
24
21
|
|
|
25
22
|
[](https://iqtree.github.io/doc/AliSim)
|
|
26
23
|
[](https://pypi.org/project/phylogenie/)
|
|
24
|
+

|
|
25
|
+
|
|
27
26
|
|
|
28
27
|
Phylogenie is a [Python](https://www.python.org/) package designed to easily simulate phylogenetic datasets—such as trees and multiple sequence alignments (MSAs)—with minimal setup effort. Simply specify the distributions from which your parameters should be sampled, and Phylogenie will handle the rest!
|
|
29
28
|
|
|
@@ -100,4 +99,3 @@ This project is licensed under [MIT License](https://raw.githubusercontent.com/g
|
|
|
100
99
|
For questions, bug reports, or feature requests, please, consider opening an [issue on GitHub](https://github.com/gabriele-marino/phylogenie/issues), or [contact me directly](mailto:gabmarino.8601@email.com).
|
|
101
100
|
|
|
102
101
|
If you need help with the configuration files, feel free to reach out — I am always very available and happy to assist!
|
|
103
|
-
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
phylogenie/__init__.py,sha256=redo-SdvWwQPlyqOfdSW0TkkZf-aP98kLRCUIg4pB8c,3304
|
|
2
|
+
phylogenie/draw.py,sha256=djG0cG9mmNFZ7vf3jo8Ei3bNBJVqujp6gtZtwPxyfSY,23341
|
|
3
|
+
phylogenie/main.py,sha256=ry3B3HiwibZG3_qB58T5UhWy5dp6neYUtSqzL9LrSkA,1698
|
|
4
|
+
phylogenie/mixins.py,sha256=wMwqP6zkqME9eMyzx5FS6-p9X8yW09jIC8jge8pHlkk,907
|
|
5
|
+
phylogenie/msa.py,sha256=JDGyZUsAq6-m-SQjoCDjAkAZIxfgyl_PDIhdYn5HOow,2064
|
|
6
|
+
phylogenie/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
|
|
8
|
+
phylogenie/typings.py,sha256=p694PBe_tk25A6N8vGGWxuqoDtt3nHFUsIYJrwR_76Y,494
|
|
9
|
+
phylogenie/generators/__init__.py,sha256=zsOxy28-9j9alOQLIgrOAFfmM58NNHO_NEtW-KXQXAY,888
|
|
10
|
+
phylogenie/generators/alisim.py,sha256=oizyUz2xNKUdO4HrmZFqtVUfzPBN4BRgxgYczofgeYs,2809
|
|
11
|
+
phylogenie/generators/configs.py,sha256=xDIyDgGxuaqdET9wVkpdrXk02jIbibSp8mgxuIcM5qQ,1489
|
|
12
|
+
phylogenie/generators/dataset.py,sha256=Z3a4Mhh6zMTYmhd-aIT3aCaJMTVg4I_QvWWIrCw2l4E,2121
|
|
13
|
+
phylogenie/generators/factories.py,sha256=2mTFdFbbLyV3v79JaOEVtqLOmxQHaOUv1S-Y3vVo7U0,8480
|
|
14
|
+
phylogenie/generators/trees.py,sha256=8dO1CkU34E6mmMAHrYqiLV_VA8r54cSEOo-UzoHiN20,10467
|
|
15
|
+
phylogenie/generators/typeguards.py,sha256=yj4VkhOaUXJ2OrY-6zhOeY9C4yKIQxjZtk2d-vIxttQ,828
|
|
16
|
+
phylogenie/io/__init__.py,sha256=3v_bxv9RVeQ3KZzxNFeV9KLVxaC_Whf7rgWLnBKoEr0,95
|
|
17
|
+
phylogenie/io/fasta.py,sha256=kx9uVATLpzpXdhhNNMvMpB5Vdwh9CTWepTGachvEwV4,1154
|
|
18
|
+
phylogenie/skyline/__init__.py,sha256=7pF4CUb4ZCLzNYJNhOjpuTOLTRhlK7L6ugfccNqjIGo,620
|
|
19
|
+
phylogenie/skyline/matrix.py,sha256=v4SitY7VbXprqlqQckjWTzW5hwRmCyIF595R6IJMxWw,9268
|
|
20
|
+
phylogenie/skyline/parameter.py,sha256=TVqkqirGXNN-VP8hnIJACPkOxUan6LkGa5o_JcPfwbY,4834
|
|
21
|
+
phylogenie/skyline/vector.py,sha256=60jtp7PieiEaEH0Tp6zNjNKjyzpN_nT5uwBUXbgeATk,7261
|
|
22
|
+
phylogenie/treesimulator/__init__.py,sha256=Qz__cFookXuEjq8AUp1A6XRON0qQ_xX-q2Q5ixP4XUg,1791
|
|
23
|
+
phylogenie/treesimulator/features.py,sha256=XbuwGw8xjGs2lNhJvvUUvXVtheSTBaSN6qj39tWYEro,1391
|
|
24
|
+
phylogenie/treesimulator/gillespie.py,sha256=ey2hdpJOSpNW88duwK7wTAdYSTnSuTSZ_yhZv9MlNHo,5323
|
|
25
|
+
phylogenie/treesimulator/model.py,sha256=L0RsL6H1ynFDPecULniSs4Cs8dvz87ovviQOXFy5Qt0,4580
|
|
26
|
+
phylogenie/treesimulator/tree.py,sha256=DEdzCh4vABq2f095beh3tD3_aee7EyXPDSjcyHKgKLg,6064
|
|
27
|
+
phylogenie/treesimulator/utils.py,sha256=OxZwVHxN004Jf-kYZ_GfJgIY0beo-0tYq80CuFGQt-M,3416
|
|
28
|
+
phylogenie/treesimulator/events/__init__.py,sha256=w2tJ0D2WB5AiCbr3CsKN6vdADueiAEMzd_ve0rpa4zg,939
|
|
29
|
+
phylogenie/treesimulator/events/base.py,sha256=JQKYUZmhB2Q-WQOy2ULGKQiabsMz-JvwMVfDoa3ZKyo,1170
|
|
30
|
+
phylogenie/treesimulator/events/contact_tracing.py,sha256=t64omKEO2d-DfN_dhDJlXp_Kg9egy2ZE346yWjV3ZrA,5148
|
|
31
|
+
phylogenie/treesimulator/events/core.py,sha256=bhgQgi5L-oaHsoWJmUOsTTzWxi0POYxVLoF-KrC8AGQ,8179
|
|
32
|
+
phylogenie/treesimulator/events/mutations.py,sha256=8Nqa2fg7fwaVNe5XSkGDSwp9pIKQ7XaBQCCj-LYlfzA,3666
|
|
33
|
+
phylogenie/treesimulator/io/__init__.py,sha256=rfP-zp8SP8baq5_4dPAr10WH0W6KfoMCxdTZDCSXtzE,185
|
|
34
|
+
phylogenie/treesimulator/io/newick.py,sha256=8Pr_jixByPOaVch18w-rFt62HYy0U97YMu0H-QSwIy0,3449
|
|
35
|
+
phylogenie/treesimulator/io/nexus.py,sha256=zqT9dzj413z_s0hqp3Cdq5NMO6lv-zuuaJlaqzaqaB8,1847
|
|
36
|
+
phylogenie-3.1.7.dist-info/licenses/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
|
|
37
|
+
phylogenie-3.1.7.dist-info/METADATA,sha256=qnvdSWDQUd2fJa7RHSByFdCKvDgYqaPP_2QeCD2F-eg,5194
|
|
38
|
+
phylogenie-3.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
39
|
+
phylogenie-3.1.7.dist-info/entry_points.txt,sha256=BBH8LoReHnNFnvq4sROEsVFegfkKJ6c_oHZ7bgK7Jl4,52
|
|
40
|
+
phylogenie-3.1.7.dist-info/top_level.txt,sha256=1YGZJhKA9tN9qI0Hcj6Cn_sOoDpba0HQlNcgQTjMD-8,11
|
|
41
|
+
phylogenie-3.1.7.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
phylogenie
|
phylogenie/io.py
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
from typing import Callable
|
|
2
|
-
|
|
3
|
-
from phylogenie.msa import MSA, Sequence
|
|
4
|
-
from phylogenie.tree import Tree
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def _parse_newick(newick: str) -> Tree:
|
|
8
|
-
newick = newick.strip()
|
|
9
|
-
stack: list[list[Tree]] = []
|
|
10
|
-
current_children: list[Tree] = []
|
|
11
|
-
current_nodes: list[Tree] = []
|
|
12
|
-
i = 0
|
|
13
|
-
while i < len(newick):
|
|
14
|
-
|
|
15
|
-
def _parse_chars(stoppers: list[str]) -> str:
|
|
16
|
-
nonlocal i
|
|
17
|
-
chars = ""
|
|
18
|
-
while newick[i] not in stoppers:
|
|
19
|
-
chars += newick[i]
|
|
20
|
-
i += 1
|
|
21
|
-
return chars
|
|
22
|
-
|
|
23
|
-
if newick[i] == "(":
|
|
24
|
-
stack.append(current_nodes)
|
|
25
|
-
current_nodes = []
|
|
26
|
-
else:
|
|
27
|
-
id = _parse_chars([":", ",", ")", ";", "["])
|
|
28
|
-
branch_length = None
|
|
29
|
-
if newick[i] == ":":
|
|
30
|
-
i += 1
|
|
31
|
-
branch_length = float(_parse_chars([",", ")", ";", "["]))
|
|
32
|
-
|
|
33
|
-
current_node = Tree(id, branch_length)
|
|
34
|
-
for node in current_children:
|
|
35
|
-
current_node.add_child(node)
|
|
36
|
-
current_children = []
|
|
37
|
-
current_nodes.append(current_node)
|
|
38
|
-
|
|
39
|
-
if newick[i] == "[":
|
|
40
|
-
i += 1
|
|
41
|
-
features = _parse_chars(["]"]).split(":")
|
|
42
|
-
i += 1
|
|
43
|
-
if features[0] != "&&NHX":
|
|
44
|
-
raise ValueError(f"Expected '&&NHX' for node features.")
|
|
45
|
-
for feature in features[1:]:
|
|
46
|
-
key, value = feature.split("=", 1)
|
|
47
|
-
current_node.set(key, eval(value))
|
|
48
|
-
|
|
49
|
-
if newick[i] == ")":
|
|
50
|
-
current_children = current_nodes
|
|
51
|
-
current_nodes = stack.pop()
|
|
52
|
-
elif newick[i] == ";":
|
|
53
|
-
return current_node
|
|
54
|
-
|
|
55
|
-
i += 1
|
|
56
|
-
|
|
57
|
-
raise ValueError("Newick string is invalid.")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def load_newick(filepath: str) -> Tree | list[Tree]:
|
|
61
|
-
with open(filepath, "r") as file:
|
|
62
|
-
trees = [_parse_newick(newick) for newick in file]
|
|
63
|
-
return trees[0] if len(trees) == 1 else trees
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def _to_newick(tree: Tree) -> str:
|
|
67
|
-
children_newick = ",".join([_to_newick(child) for child in tree.children])
|
|
68
|
-
newick = tree.id
|
|
69
|
-
if children_newick:
|
|
70
|
-
newick = f"({children_newick}){newick}"
|
|
71
|
-
if tree.branch_length is not None:
|
|
72
|
-
newick += f":{tree.branch_length}"
|
|
73
|
-
if tree.features:
|
|
74
|
-
reprs = {k: repr(v).replace("'", '"') for k, v in tree.features.items()}
|
|
75
|
-
features = [f"{k}={repr}" for k, repr in reprs.items()]
|
|
76
|
-
newick += f"[&&NHX:{':'.join(features)}]"
|
|
77
|
-
return newick
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def dump_newick(trees: Tree | list[Tree], filepath: str) -> None:
|
|
81
|
-
if isinstance(trees, Tree):
|
|
82
|
-
trees = [trees]
|
|
83
|
-
with open(filepath, "w") as file:
|
|
84
|
-
for t in trees:
|
|
85
|
-
file.write(_to_newick(t) + ";\n")
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def load_fasta(
|
|
89
|
-
fasta_file: str, extract_time_from_id: Callable[[str], float] | None = None
|
|
90
|
-
) -> MSA:
|
|
91
|
-
sequences: list[Sequence] = []
|
|
92
|
-
with open(fasta_file, "r") as f:
|
|
93
|
-
for line in f:
|
|
94
|
-
if not line.startswith(">"):
|
|
95
|
-
raise ValueError(f"Invalid FASTA format: expected '>', got '{line[0]}'")
|
|
96
|
-
id = line[1:].strip()
|
|
97
|
-
time = None
|
|
98
|
-
if extract_time_from_id is not None:
|
|
99
|
-
time = extract_time_from_id(id)
|
|
100
|
-
elif "|" in id:
|
|
101
|
-
try:
|
|
102
|
-
time = float(id.split("|")[-1])
|
|
103
|
-
except ValueError:
|
|
104
|
-
pass
|
|
105
|
-
chars = next(f).strip()
|
|
106
|
-
sequences.append(Sequence(id, chars, time))
|
|
107
|
-
return MSA(sequences)
|
phylogenie/tree.py
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
from collections.abc import Iterator
|
|
2
|
-
from typing import Any
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class Tree:
|
|
6
|
-
def __init__(self, id: str = "", branch_length: float | None = None):
|
|
7
|
-
self.id = id
|
|
8
|
-
self.branch_length = branch_length
|
|
9
|
-
self._parent: Tree | None = None
|
|
10
|
-
self._children: list[Tree] = []
|
|
11
|
-
self._features: dict[str, Any] = {}
|
|
12
|
-
|
|
13
|
-
@property
|
|
14
|
-
def children(self) -> tuple["Tree", ...]:
|
|
15
|
-
return tuple(self._children)
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def parent(self) -> "Tree | None":
|
|
19
|
-
return self._parent
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
def features(self) -> dict[str, Any]:
|
|
23
|
-
return self._features
|
|
24
|
-
|
|
25
|
-
def add_child(self, child: "Tree") -> "Tree":
|
|
26
|
-
child._parent = self
|
|
27
|
-
self._children.append(child)
|
|
28
|
-
return self
|
|
29
|
-
|
|
30
|
-
def remove_child(self, child: "Tree") -> None:
|
|
31
|
-
self._children.remove(child)
|
|
32
|
-
child._parent = None
|
|
33
|
-
|
|
34
|
-
def set_parent(self, node: "Tree | None"):
|
|
35
|
-
self._parent = node
|
|
36
|
-
if node is not None:
|
|
37
|
-
node._children.append(self)
|
|
38
|
-
|
|
39
|
-
def preorder_traversal(self) -> Iterator["Tree"]:
|
|
40
|
-
yield self
|
|
41
|
-
for child in self.children:
|
|
42
|
-
yield from child.preorder_traversal()
|
|
43
|
-
|
|
44
|
-
def postorder_traversal(self) -> Iterator["Tree"]:
|
|
45
|
-
for child in self.children:
|
|
46
|
-
yield from child.postorder_traversal()
|
|
47
|
-
yield self
|
|
48
|
-
|
|
49
|
-
def get_node(self, id: str) -> "Tree":
|
|
50
|
-
for node in self:
|
|
51
|
-
if node.id == id:
|
|
52
|
-
return node
|
|
53
|
-
raise ValueError(f"Node with id {id} not found.")
|
|
54
|
-
|
|
55
|
-
def is_leaf(self) -> bool:
|
|
56
|
-
return not self.children
|
|
57
|
-
|
|
58
|
-
def get_leaves(self) -> list["Tree"]:
|
|
59
|
-
return [node for node in self if not node.children]
|
|
60
|
-
|
|
61
|
-
def get_time(self) -> float:
|
|
62
|
-
parent_time = 0 if self.parent is None else self.parent.get_time()
|
|
63
|
-
if self.branch_length is None:
|
|
64
|
-
if self.parent is not None:
|
|
65
|
-
raise ValueError(
|
|
66
|
-
f"Branch length of non-root node {self.id} is not set."
|
|
67
|
-
)
|
|
68
|
-
return 0.0
|
|
69
|
-
return self.branch_length + parent_time
|
|
70
|
-
|
|
71
|
-
def set(self, key: str, value: Any) -> None:
|
|
72
|
-
self._features[key] = value
|
|
73
|
-
|
|
74
|
-
def get(self, key: str) -> Any:
|
|
75
|
-
return self._features.get(key)
|
|
76
|
-
|
|
77
|
-
def delete(self, key: str) -> None:
|
|
78
|
-
del self._features[key]
|
|
79
|
-
|
|
80
|
-
def copy(self):
|
|
81
|
-
new_tree = Tree(self.id, self.branch_length)
|
|
82
|
-
for key, value in self._features.items():
|
|
83
|
-
new_tree.set(key, value)
|
|
84
|
-
for child in self.children:
|
|
85
|
-
new_tree.add_child(child.copy())
|
|
86
|
-
return new_tree
|
|
87
|
-
|
|
88
|
-
def __iter__(self) -> Iterator["Tree"]:
|
|
89
|
-
return self.preorder_traversal()
|
|
90
|
-
|
|
91
|
-
def __repr__(self) -> str:
|
|
92
|
-
return f"TreeNode(id='{self.id}', branch_length={self.branch_length}, features={self.features})"
|
phylogenie/utils.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from pydantic import BaseModel, ConfigDict
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class StrictBaseModel(BaseModel):
|
|
7
|
-
model_config = ConfigDict(extra="forbid")
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Distribution(BaseModel):
|
|
11
|
-
type: str
|
|
12
|
-
model_config = ConfigDict(extra="allow")
|
|
13
|
-
|
|
14
|
-
@property
|
|
15
|
-
def args(self) -> dict[str, Any]:
|
|
16
|
-
assert self.model_extra is not None
|
|
17
|
-
return self.model_extra
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
phylogenie/__init__.py,sha256=T2mRLsYtoLlWt8GlxrrUnfXJ9XVioq7hTvVq3uJpwQI,2215
|
|
2
|
-
phylogenie/generators/__init__.py,sha256=zsOxy28-9j9alOQLIgrOAFfmM58NNHO_NEtW-KXQXAY,888
|
|
3
|
-
phylogenie/generators/alisim.py,sha256=3mANgyQrlozhslV3_ryt-m4ItkRcKKRLufWf6SNBTnQ,2781
|
|
4
|
-
phylogenie/generators/configs.py,sha256=AiiFS6rpH9BPwDKCkT4SVrRzfLFFrwRCJM4CRj0Srdk,1072
|
|
5
|
-
phylogenie/generators/dataset.py,sha256=loVKC_1G7gzkPDN9W3GF-Rj9od8AeOJgIC0aJJa-4KA,2110
|
|
6
|
-
phylogenie/generators/factories.py,sha256=jLwDuq0mrmDz2U5rZM19KJ2hSpamG3r6zb83YCc6snA,7619
|
|
7
|
-
phylogenie/generators/trees.py,sha256=q03WPG82M4ucp-jyjoKEBy7TKMBzD3RkKn8hS0G0-i0,10463
|
|
8
|
-
phylogenie/generators/typeguards.py,sha256=yj4VkhOaUXJ2OrY-6zhOeY9C4yKIQxjZtk2d-vIxttQ,828
|
|
9
|
-
phylogenie/io.py,sha256=y7nQIvLgCvqELsXFKfm1GgKJO_saoQ-7zQpE3Kvajzc,3509
|
|
10
|
-
phylogenie/main.py,sha256=vtvSpQxBNlYABoFQ25czl-l3fIr4QRo3svWVd-jcArw,1170
|
|
11
|
-
phylogenie/msa.py,sha256=JDGyZUsAq6-m-SQjoCDjAkAZIxfgyl_PDIhdYn5HOow,2064
|
|
12
|
-
phylogenie/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
phylogenie/skyline/__init__.py,sha256=7pF4CUb4ZCLzNYJNhOjpuTOLTRhlK7L6ugfccNqjIGo,620
|
|
14
|
-
phylogenie/skyline/matrix.py,sha256=Gl8OgKjtieG0NwPYiPimKI36gefV8fm_OeorjdXxPTs,9146
|
|
15
|
-
phylogenie/skyline/parameter.py,sha256=EM9qlPt0JhMBy3TbztM0dj24BaGNEy8KWKdTObDKhbI,4644
|
|
16
|
-
phylogenie/skyline/vector.py,sha256=bJP7_FNX_Klt6wXqsyfj0KX3VNj6-dIhzCKSJuQcOV0,7115
|
|
17
|
-
phylogenie/tree.py,sha256=KTx3m_tJPdeBqA5i0SA3dwCIobxsFcJwZFunMfDmVBY,2791
|
|
18
|
-
phylogenie/treesimulator/__init__.py,sha256=XG_xwETKWgDmCihqNUFCcMHtFg4WvZu5qbqWn9Dndt8,879
|
|
19
|
-
phylogenie/treesimulator/events/__init__.py,sha256=UGfvXOVJ_ZAkk_8sBPihjmxciiaEnXZEPFIY53sttWI,940
|
|
20
|
-
phylogenie/treesimulator/events/contact_tracing.py,sha256=_nJ85yhgGkeruQgMHvGpDYoyhheBf8M4LgZWiWdi5dY,4801
|
|
21
|
-
phylogenie/treesimulator/events/core.py,sha256=JokGmieAv2xEX7KsjBWZr05jHN1jB-XZbpxe9gwdbDA,7953
|
|
22
|
-
phylogenie/treesimulator/events/mutations.py,sha256=xkXUIppbLIWZqKwVf-hi7d-_pS42TG2EPVfJA_grxBg,3443
|
|
23
|
-
phylogenie/treesimulator/gillespie.py,sha256=naoxPyZixWVkd5f7B3KhEtOFiQI4NDIp_589NCLTHKM,4831
|
|
24
|
-
phylogenie/treesimulator/model.py,sha256=0Im6cFTlpMlJrSP4pTTKtvLT9qrQWV8MSTesAsBxT8g,5422
|
|
25
|
-
phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
|
|
26
|
-
phylogenie/typings.py,sha256=GknvAFXyiaWeeYJ8Lk5d6E2VHT-xW6ONEojYbtJYiB8,476
|
|
27
|
-
phylogenie/utils.py,sha256=pCg9ob0RpLUHwM49x4knKxL4FNPr3-EU_6zMXsvxtAg,370
|
|
28
|
-
phylogenie-2.1.4.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
|
|
29
|
-
phylogenie-2.1.4.dist-info/METADATA,sha256=xp3v_zG0Cua0UQN6TlF7yS9NuchhdlKnDf8-O3e-l_8,5375
|
|
30
|
-
phylogenie-2.1.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
31
|
-
phylogenie-2.1.4.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
|
|
32
|
-
phylogenie-2.1.4.dist-info/RECORD,,
|
|
File without changes
|