phylogenie 3.1.2__py3-none-any.whl → 3.1.4__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/draw.py CHANGED
@@ -5,6 +5,7 @@ import matplotlib.colors as mcolors
5
5
  import matplotlib.patches as mpatches
6
6
  import matplotlib.pyplot as plt
7
7
  from matplotlib.axes import Axes
8
+ from matplotlib.colors import Colormap
8
9
  from mpl_toolkits.axes_grid1.inset_locator import inset_axes # pyright: ignore
9
10
 
10
11
  from phylogenie.treesimulator import Tree, get_node_depth_levels, get_node_depths
@@ -48,10 +49,10 @@ def _draw_colored_tree(tree: Tree, ax: Axes, colors: Color | dict[Tree, Color])
48
49
  def draw_tree(
49
50
  tree: Tree,
50
51
  ax: Axes | None = None,
51
- color_by: str | dict[Tree, Any] | None = None,
52
+ color_by: str | dict[str, Any] | None = None,
52
53
  coloring: str | Coloring | None = None,
53
54
  default_color: Color = "black",
54
- cmap: str | None = None,
55
+ colormap: str | Colormap | None = None,
55
56
  vmin: float | None = None,
56
57
  vmax: float | None = None,
57
58
  show_legend: bool = True,
@@ -67,17 +68,22 @@ def draw_tree(
67
68
  if color_by is None:
68
69
  return _draw_colored_tree(tree, ax, colors=default_color)
69
70
 
70
- if isinstance(color_by, dict):
71
- features = {node: color_by[node] for node in tree if node in color_by}
72
- else:
71
+ if isinstance(color_by, str):
73
72
  features = {node: node[color_by] for node in tree if color_by in node.metadata}
73
+ else:
74
+ features = {node: color_by[node.name] for node in tree if node.name in color_by}
75
+ values = list(features.values())
74
76
 
75
77
  if coloring is None:
76
78
  coloring = (
77
79
  Coloring.CONTINUOUS
78
- if any(isinstance(f, float) for f in features.values())
80
+ if any(isinstance(f, float) for f in values)
79
81
  else Coloring.DISCRETE
80
82
  )
83
+ if colormap is None:
84
+ colormap = "tab20" if coloring == Coloring.DISCRETE else "viridis"
85
+ if isinstance(colormap, str):
86
+ colormap = plt.get_cmap(colormap)
81
87
 
82
88
  def _get_colors(feature_map: Callable[[Any], Color]) -> dict[Tree, Color]:
83
89
  return {
@@ -86,14 +92,12 @@ def draw_tree(
86
92
  }
87
93
 
88
94
  if coloring == Coloring.DISCRETE:
89
- if any(isinstance(f, float) for f in features.values()):
95
+ if any(isinstance(f, float) for f in values):
90
96
  raise ValueError(
91
97
  "Discrete coloring selected but feature values are not all categorical."
92
98
  )
93
-
94
- colormap = plt.get_cmap("tab20" if cmap is None else cmap)
95
99
  feature_colors = {
96
- f: mcolors.to_hex(colormap(i)) for i, f in enumerate(set(features.values()))
100
+ f: mcolors.to_hex(colormap(i)) for i, f in enumerate(set(values))
97
101
  }
98
102
  colors = _get_colors(lambda f: feature_colors[f])
99
103
 
@@ -114,10 +118,9 @@ def draw_tree(
114
118
  return _draw_colored_tree(tree, ax, colors)
115
119
 
116
120
  if coloring == Coloring.CONTINUOUS:
117
- vmin = min(features.values()) if vmin is None else vmin
118
- vmax = max(features.values()) if vmax is None else vmax
121
+ vmin = min(values) if vmin is None else vmin
122
+ vmax = max(values) if vmax is None else vmax
119
123
  norm = mcolors.Normalize(vmin=vmin, vmax=vmax)
120
- colormap = plt.get_cmap("viridis" if cmap is None else cmap)
121
124
  colors = _get_colors(lambda f: colormap(norm(float(f))))
122
125
 
123
126
  if show_hist:
@@ -127,7 +130,7 @@ def draw_tree(
127
130
  hist_ax = inset_axes(ax, **default_hist_axes_kwargs) # pyright: ignore
128
131
 
129
132
  hist_kwargs = {} if hist_kwargs is None else hist_kwargs
130
- _, bins, patches = hist_ax.hist(features, **hist_kwargs) # pyright: ignore
133
+ _, bins, patches = hist_ax.hist(values, **hist_kwargs) # pyright: ignore
131
134
 
132
135
  for patch, b0, b1 in zip( # pyright: ignore
133
136
  patches, bins[:-1], bins[1:] # pyright: ignore
phylogenie/main.py CHANGED
@@ -15,14 +15,10 @@ def run(config_path: str) -> None:
15
15
  if os.path.isdir(config_path):
16
16
  for config_file in glob(os.path.join(config_path, "**/*.yaml"), recursive=True):
17
17
  with open(config_file, "r") as f:
18
- config = safe_load(f)
19
- generator = adapter.validate_python(config)
20
- generator.generate()
18
+ adapter.validate_python(safe_load(f)).generate()
21
19
  else:
22
20
  with open(config_path, "r") as f:
23
- config = safe_load(f)
24
- generator = adapter.validate_python(config)
25
- generator.generate()
21
+ adapter.validate_python(safe_load(f)).generate()
26
22
 
27
23
 
28
24
  def main() -> None:
@@ -15,9 +15,9 @@ def _parse_translate_block(lines: Iterator[str]) -> dict[str, str]:
15
15
  if ";" in line:
16
16
  return translations
17
17
  else:
18
- raise ValueError(f"Invalid translate line: {line}")
18
+ raise ValueError(f"Invalid translate line. Expected '<num> <name>'.")
19
19
  translations[match.group(1)] = match.group(2)
20
- raise ValueError("Translate block not terminated with ';'")
20
+ raise ValueError("Translate block not terminated with ';'.")
21
21
 
22
22
 
23
23
  def _parse_trees_block(lines: Iterator[str]) -> dict[str, Tree]:
@@ -33,9 +33,12 @@ def _parse_trees_block(lines: Iterator[str]) -> dict[str, Tree]:
33
33
  match = re.match(r"^TREE\s*\*?\s+(\S+)\s*=\s*(.+)$", line, re.IGNORECASE)
34
34
  if match is None:
35
35
  raise ValueError(
36
- f"Invalid tree line. Expected 'TREE <name> = <newick>'"
36
+ f"Invalid tree line. Expected 'TREE <name> = <newick>'."
37
37
  )
38
- trees[match.group(1)] = parse_newick(match.group(2), translations)
38
+ name = match.group(1)
39
+ if name in trees:
40
+ raise ValueError(f"Duplicate tree name found: {name}.")
41
+ trees[name] = parse_newick(match.group(2), translations)
39
42
  raise ValueError("Unterminated TREES block.")
40
43
 
41
44
 
@@ -1,21 +1,17 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: phylogenie
3
- Version: 3.1.2
4
- Summary: Generate phylogenetic datasets with minimal setup effort
5
- Author: Gabriele Marino
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: matplotlib (>=3.10.6,<4.0.0)
14
- Requires-Dist: pandas (>=2.2.2,<3.0.0)
15
- Requires-Dist: pydantic (>=2.11.5,<3.0.0)
16
- Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
17
- Requires-Dist: tqdm (>=4.66.4,<5.0.0)
3
+ Version: 3.1.4
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.10
18
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
19
15
 
20
16
  <p align="center">
21
17
  <img src="https://raw.githubusercontent.com/gabriele-marino/phylogenie/main/logo.png" style="width:100%; height:auto;"/>
@@ -103,4 +99,3 @@ This project is licensed under [MIT License](https://raw.githubusercontent.com/g
103
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).
104
100
 
105
101
  If you need help with the configuration files, feel free to reach out — I am always very available and happy to assist!
106
-
@@ -1,5 +1,11 @@
1
1
  phylogenie/__init__.py,sha256=-4GKO2EKgc2GiOhzVRI0SQk6wQZ4AlP7Ron_Lp-s_r0,2913
2
- phylogenie/draw.py,sha256=6WHLxXXyTKq6pxuPBfDgZfEZti3UkJVhDory2pJzdIc,5499
2
+ phylogenie/draw.py,sha256=nT3FOHSm85G23qz2xxYf2MesEDonWRSxfKGJkVboTKY,5580
3
+ phylogenie/main.py,sha256=DstKWUySyDXdcPN_P8O3Mmoau4IjUxCQ4iLWDVuAcws,1054
4
+ phylogenie/mixins.py,sha256=kwxcHGYSS-Frk-r0nwuyILYS1g6NjSOhbUKEYt2g1B0,1319
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
3
9
  phylogenie/generators/__init__.py,sha256=zsOxy28-9j9alOQLIgrOAFfmM58NNHO_NEtW-KXQXAY,888
4
10
  phylogenie/generators/alisim.py,sha256=oizyUz2xNKUdO4HrmZFqtVUfzPBN4BRgxgYczofgeYs,2809
5
11
  phylogenie/generators/configs.py,sha256=xDIyDgGxuaqdET9wVkpdrXk02jIbibSp8mgxuIcM5qQ,1489
@@ -9,32 +15,27 @@ phylogenie/generators/trees.py,sha256=8dO1CkU34E6mmMAHrYqiLV_VA8r54cSEOo-UzoHiN2
9
15
  phylogenie/generators/typeguards.py,sha256=yj4VkhOaUXJ2OrY-6zhOeY9C4yKIQxjZtk2d-vIxttQ,828
10
16
  phylogenie/io/__init__.py,sha256=eWDU6YDqAdx6TZlUMmMbsfO8gP3i5HP_cfUe_-0x2FA,69
11
17
  phylogenie/io/fasta.py,sha256=CUFO06m7wClprarsMheZojM4Os2NQf3ALYUXSWzfNL0,869
12
- phylogenie/main.py,sha256=vtvSpQxBNlYABoFQ25czl-l3fIr4QRo3svWVd-jcArw,1170
13
- phylogenie/mixins.py,sha256=kwxcHGYSS-Frk-r0nwuyILYS1g6NjSOhbUKEYt2g1B0,1319
14
- phylogenie/msa.py,sha256=JDGyZUsAq6-m-SQjoCDjAkAZIxfgyl_PDIhdYn5HOow,2064
15
- phylogenie/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
18
  phylogenie/skyline/__init__.py,sha256=7pF4CUb4ZCLzNYJNhOjpuTOLTRhlK7L6ugfccNqjIGo,620
17
19
  phylogenie/skyline/matrix.py,sha256=v4SitY7VbXprqlqQckjWTzW5hwRmCyIF595R6IJMxWw,9268
18
20
  phylogenie/skyline/parameter.py,sha256=TVqkqirGXNN-VP8hnIJACPkOxUan6LkGa5o_JcPfwbY,4834
19
21
  phylogenie/skyline/vector.py,sha256=60jtp7PieiEaEH0Tp6zNjNKjyzpN_nT5uwBUXbgeATk,7261
20
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=-yMW14018x9dw45TonS6nRlzWcwXcaHv3Jn5HYriLQQ,6009
27
+ phylogenie/treesimulator/utils.py,sha256=OxZwVHxN004Jf-kYZ_GfJgIY0beo-0tYq80CuFGQt-M,3416
21
28
  phylogenie/treesimulator/events/__init__.py,sha256=w2tJ0D2WB5AiCbr3CsKN6vdADueiAEMzd_ve0rpa4zg,939
22
29
  phylogenie/treesimulator/events/base.py,sha256=JQKYUZmhB2Q-WQOy2ULGKQiabsMz-JvwMVfDoa3ZKyo,1170
23
30
  phylogenie/treesimulator/events/contact_tracing.py,sha256=t64omKEO2d-DfN_dhDJlXp_Kg9egy2ZE346yWjV3ZrA,5148
24
31
  phylogenie/treesimulator/events/core.py,sha256=bhgQgi5L-oaHsoWJmUOsTTzWxi0POYxVLoF-KrC8AGQ,8179
25
32
  phylogenie/treesimulator/events/mutations.py,sha256=8Nqa2fg7fwaVNe5XSkGDSwp9pIKQ7XaBQCCj-LYlfzA,3666
26
- phylogenie/treesimulator/features.py,sha256=XbuwGw8xjGs2lNhJvvUUvXVtheSTBaSN6qj39tWYEro,1391
27
- phylogenie/treesimulator/gillespie.py,sha256=ey2hdpJOSpNW88duwK7wTAdYSTnSuTSZ_yhZv9MlNHo,5323
28
33
  phylogenie/treesimulator/io/__init__.py,sha256=rfP-zp8SP8baq5_4dPAr10WH0W6KfoMCxdTZDCSXtzE,185
29
34
  phylogenie/treesimulator/io/newick.py,sha256=8Pr_jixByPOaVch18w-rFt62HYy0U97YMu0H-QSwIy0,3449
30
- phylogenie/treesimulator/io/nexus.py,sha256=wmOueclNRp85Zcs72dpaoKFiDY13TtmEL0mabKYni0w,1703
31
- phylogenie/treesimulator/model.py,sha256=L0RsL6H1ynFDPecULniSs4Cs8dvz87ovviQOXFy5Qt0,4580
32
- phylogenie/treesimulator/tree.py,sha256=-yMW14018x9dw45TonS6nRlzWcwXcaHv3Jn5HYriLQQ,6009
33
- phylogenie/treesimulator/utils.py,sha256=OxZwVHxN004Jf-kYZ_GfJgIY0beo-0tYq80CuFGQt-M,3416
34
- phylogenie/typeguards.py,sha256=JtqmbEWJZBRHbWgCvcl6nrWm3VcBfzRbklbTBYHItn0,1325
35
- phylogenie/typings.py,sha256=p694PBe_tk25A6N8vGGWxuqoDtt3nHFUsIYJrwR_76Y,494
36
- phylogenie-3.1.2.dist-info/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
37
- phylogenie-3.1.2.dist-info/METADATA,sha256=8bNrxXaHWW1N1nWmjDwf8CV9306fDaxguE2xoWmPufU,5476
38
- phylogenie-3.1.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
39
- phylogenie-3.1.2.dist-info/entry_points.txt,sha256=Rt6_usN0FkBX1ZfiqCirjMN9FKOgFLG8rydcQ8kugeE,51
40
- phylogenie-3.1.2.dist-info/RECORD,,
35
+ phylogenie/treesimulator/io/nexus.py,sha256=tPZRPejnG0OEE7YTorwvyZe2vnZHv4KiC9f-N7Kx6qg,1849
36
+ phylogenie-3.1.4.dist-info/licenses/LICENSE.txt,sha256=NUrDqElK-eD3I0WqC004CJsy6cs0JgsAoebDv_42-pw,1071
37
+ phylogenie-3.1.4.dist-info/METADATA,sha256=en3CvCPhiUbQPZTAUYWpx8nQze6oqS-KQJJa3xGdvbI,5163
38
+ phylogenie-3.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
+ phylogenie-3.1.4.dist-info/entry_points.txt,sha256=BBH8LoReHnNFnvq4sROEsVFegfkKJ6c_oHZ7bgK7Jl4,52
40
+ phylogenie-3.1.4.dist-info/top_level.txt,sha256=1YGZJhKA9tN9qI0Hcj6Cn_sOoDpba0HQlNcgQTjMD-8,11
41
+ phylogenie-3.1.4.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ phylogenie = phylogenie.main:main
@@ -0,0 +1 @@
1
+ phylogenie
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- phylogenie=phylogenie.main:main
3
-