lineagetree 3.0.0__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.
Files changed (38) hide show
  1. lineagetree-3.0.0/LICENSE +21 -0
  2. lineagetree-3.0.0/PKG-INFO +129 -0
  3. lineagetree-3.0.0/README.md +83 -0
  4. lineagetree-3.0.0/pyproject.toml +136 -0
  5. lineagetree-3.0.0/setup.cfg +4 -0
  6. lineagetree-3.0.0/src/LineageTree/__init__.py +30 -0
  7. lineagetree-3.0.0/src/LineageTree/_core/_modifier.py +207 -0
  8. lineagetree-3.0.0/src/LineageTree/_core/_navigation.py +498 -0
  9. lineagetree-3.0.0/src/LineageTree/_core/_properties.py +227 -0
  10. lineagetree-3.0.0/src/LineageTree/_core/utils.py +170 -0
  11. lineagetree-3.0.0/src/LineageTree/_core/validation.py +37 -0
  12. lineagetree-3.0.0/src/LineageTree/_io/_loaders.py +980 -0
  13. lineagetree-3.0.0/src/LineageTree/_io/_writers.py +484 -0
  14. lineagetree-3.0.0/src/LineageTree/_legacy/export_csv.py +70 -0
  15. lineagetree-3.0.0/src/LineageTree/_legacy/to_lineajea.py +30 -0
  16. lineagetree-3.0.0/src/LineageTree/_legacy/to_motile.py +36 -0
  17. lineagetree-3.0.0/src/LineageTree/_mixins/_methodize.py +190 -0
  18. lineagetree-3.0.0/src/LineageTree/_mixins/analysis_mixin.py +28 -0
  19. lineagetree-3.0.0/src/LineageTree/_mixins/io_mixin.py +17 -0
  20. lineagetree-3.0.0/src/LineageTree/_mixins/modifier_mixin.py +21 -0
  21. lineagetree-3.0.0/src/LineageTree/_mixins/navigation_mixin.py +33 -0
  22. lineagetree-3.0.0/src/LineageTree/_mixins/plot_mixin.py +21 -0
  23. lineagetree-3.0.0/src/LineageTree/_mixins/properties_mixin.py +41 -0
  24. lineagetree-3.0.0/src/LineageTree/_mixins/spatial_mixin.py +19 -0
  25. lineagetree-3.0.0/src/LineageTree/lineage_tree.py +314 -0
  26. lineagetree-3.0.0/src/LineageTree/lineage_tree_manager.py +885 -0
  27. lineagetree-3.0.0/src/LineageTree/measure/dynamic_time_warping.py +342 -0
  28. lineagetree-3.0.0/src/LineageTree/measure/spatial.py +224 -0
  29. lineagetree-3.0.0/src/LineageTree/measure/uted.py +655 -0
  30. lineagetree-3.0.0/src/LineageTree/plot.py +737 -0
  31. lineagetree-3.0.0/src/LineageTree/tree_approximation.py +492 -0
  32. lineagetree-3.0.0/src/LineageTree.egg-info/PKG-INFO +129 -0
  33. lineagetree-3.0.0/src/LineageTree.egg-info/SOURCES.txt +43 -0
  34. lineagetree-3.0.0/src/LineageTree.egg-info/dependency_links.txt +1 -0
  35. lineagetree-3.0.0/src/LineageTree.egg-info/requires.txt +22 -0
  36. lineagetree-3.0.0/src/LineageTree.egg-info/top_level.txt +1 -0
  37. lineagetree-3.0.0/tests/test_lineageTree.py +682 -0
  38. lineagetree-3.0.0/tests/test_uted.py +233 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 leoguignard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.4
2
+ Name: lineagetree
3
+ Version: 3.0.0
4
+ Summary: Structure for Lineage Trees
5
+ Author: Giannis Liaskas, Christopher Mazzerbo
6
+ Author-email: Léo Guignard <leo.guignard@univ-amu.fr>
7
+ Maintainer-email: Léo Guignard <leo.guignard@univ-amu.fr>
8
+ License-Expression: MIT
9
+ Project-URL: Bug Tracker, https://github.com/GuignardLab/LineageTree/issues
10
+ Project-URL: Documentation, https://github.com/GuignardLab/LineageTree#README.md
11
+ Project-URL: Source Code, https://github.com/GuignardLab/LineageTree
12
+ Project-URL: User Support, https://github.com/GuignardLab/LineageTree/issues
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: scipy>=1.9
27
+ Requires-Dist: numpy>=1.23
28
+ Requires-Dist: matplotlib
29
+ Requires-Dist: edist
30
+ Requires-Dist: svgwrite
31
+ Requires-Dist: Micro-Mastodon-Reader
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: pytest-cov; extra == "dev"
35
+ Requires-Dist: black; extra == "dev"
36
+ Requires-Dist: ruff; extra == "dev"
37
+ Requires-Dist: bumpver; extra == "dev"
38
+ Provides-Extra: doc
39
+ Requires-Dist: sphinx-rtd-theme; extra == "doc"
40
+ Requires-Dist: myst-parser; extra == "doc"
41
+ Requires-Dist: sphinx_book_theme; extra == "doc"
42
+ Provides-Extra: test
43
+ Requires-Dist: pytest; extra == "test"
44
+ Requires-Dist: pytest-cov; extra == "test"
45
+ Dynamic: license-file
46
+
47
+ # LineageTree
48
+
49
+ This library allows to import and work with cell (but not limited to cells) lineage trees.
50
+ With LineageTree you can read from:
51
+
52
+ - TGMM algorithm outputs described in [Amat et al. 2014](https://www.nature.com/articles/nmeth.3036)
53
+ - TrackMate files described in [Tinevez et al. 2017](https://doi.org/10.1016/j.ymeth.2016.09.016)
54
+ - MaMuT files described in [Wolff et al. 2018](https://doi.org/10.7554/eLife.34410)
55
+ - SVF algorithm outputs described in [McDole, Guignard et al. 2018](https://doi.org/10.1016/j.cell.2018.09.031)
56
+ - ASTEC algorithm outputs described in [Guignard, Fiuza et al. 2020](https://doi.org/10.1126/science.aar5663)
57
+ - Data from the [Digital development Database](http://digital-development.org/index.html) described in [Du et al. 2014](https://www.cell.com/fulltext/S0092-8674(13)01542-0) and [Du et al. 2015](https://www.sciencedirect.com/science/article/pii/S1534580715004876?via%3Dihub)
58
+ - and few others
59
+
60
+ ## Basic usage
61
+
62
+ Once installed the library can be called the following way (as an example):
63
+
64
+ ```python
65
+ from lineagetree import LineageTree
66
+ ```
67
+
68
+ and one can then load lineage trees the following way:
69
+
70
+ For `.lT` files:
71
+
72
+ ```python
73
+ lT = LineageTree.load('path/to/file.lT')
74
+ ```
75
+
76
+ For ASTEC data:
77
+
78
+ ```python
79
+ from lineagetree import read_from_ASTEC
80
+ lT = read_from_ASTEC('path/to/ASTEC.pkl')
81
+ ```
82
+
83
+ For MaMuT or TrackMate:
84
+
85
+ ```python
86
+ from lineagetree import read_from_mamut_xml
87
+ lT = read_from_mamut_xml('path/to/MaMuT.xml')
88
+ ```
89
+
90
+ For TGMM:
91
+
92
+ ```python
93
+ from lineagetree import read_from_tgmm_xml
94
+ lT = read_from_tgmm_xml('path/to/single_time_file{t:04d}.xml', tb=0, te=500)
95
+ ```
96
+
97
+ For Mastodon:
98
+
99
+ ```python
100
+ from lineagetree import read_from_mastodon
101
+ lT = read_from_mastodon('path/to/Mastodon.mastodon')
102
+ ```
103
+
104
+ or, for Mastodon csv file:
105
+
106
+ ```python
107
+ from lineagetree import read_from_mastodon_csv
108
+ lT = read_from_mastodon_csv(['path/to/nodes.csv', 'path/to/links.csv'])
109
+ ```
110
+
111
+ ## Quick install
112
+
113
+ To quickly install the library together with its dependencies one can run:
114
+
115
+ ```shell
116
+ pip install LineageTree
117
+ ```
118
+
119
+ or, for the latest version if you have cloned the directory:
120
+
121
+ ```shell
122
+ pip install .
123
+ ```
124
+
125
+ or for the latest version wihtout cloning the directory
126
+
127
+ ```shell
128
+ pip install git+https://github.com/leoguignard/LineageTree
129
+ ```
@@ -0,0 +1,83 @@
1
+ # LineageTree
2
+
3
+ This library allows to import and work with cell (but not limited to cells) lineage trees.
4
+ With LineageTree you can read from:
5
+
6
+ - TGMM algorithm outputs described in [Amat et al. 2014](https://www.nature.com/articles/nmeth.3036)
7
+ - TrackMate files described in [Tinevez et al. 2017](https://doi.org/10.1016/j.ymeth.2016.09.016)
8
+ - MaMuT files described in [Wolff et al. 2018](https://doi.org/10.7554/eLife.34410)
9
+ - SVF algorithm outputs described in [McDole, Guignard et al. 2018](https://doi.org/10.1016/j.cell.2018.09.031)
10
+ - ASTEC algorithm outputs described in [Guignard, Fiuza et al. 2020](https://doi.org/10.1126/science.aar5663)
11
+ - Data from the [Digital development Database](http://digital-development.org/index.html) described in [Du et al. 2014](https://www.cell.com/fulltext/S0092-8674(13)01542-0) and [Du et al. 2015](https://www.sciencedirect.com/science/article/pii/S1534580715004876?via%3Dihub)
12
+ - and few others
13
+
14
+ ## Basic usage
15
+
16
+ Once installed the library can be called the following way (as an example):
17
+
18
+ ```python
19
+ from lineagetree import LineageTree
20
+ ```
21
+
22
+ and one can then load lineage trees the following way:
23
+
24
+ For `.lT` files:
25
+
26
+ ```python
27
+ lT = LineageTree.load('path/to/file.lT')
28
+ ```
29
+
30
+ For ASTEC data:
31
+
32
+ ```python
33
+ from lineagetree import read_from_ASTEC
34
+ lT = read_from_ASTEC('path/to/ASTEC.pkl')
35
+ ```
36
+
37
+ For MaMuT or TrackMate:
38
+
39
+ ```python
40
+ from lineagetree import read_from_mamut_xml
41
+ lT = read_from_mamut_xml('path/to/MaMuT.xml')
42
+ ```
43
+
44
+ For TGMM:
45
+
46
+ ```python
47
+ from lineagetree import read_from_tgmm_xml
48
+ lT = read_from_tgmm_xml('path/to/single_time_file{t:04d}.xml', tb=0, te=500)
49
+ ```
50
+
51
+ For Mastodon:
52
+
53
+ ```python
54
+ from lineagetree import read_from_mastodon
55
+ lT = read_from_mastodon('path/to/Mastodon.mastodon')
56
+ ```
57
+
58
+ or, for Mastodon csv file:
59
+
60
+ ```python
61
+ from lineagetree import read_from_mastodon_csv
62
+ lT = read_from_mastodon_csv(['path/to/nodes.csv', 'path/to/links.csv'])
63
+ ```
64
+
65
+ ## Quick install
66
+
67
+ To quickly install the library together with its dependencies one can run:
68
+
69
+ ```shell
70
+ pip install LineageTree
71
+ ```
72
+
73
+ or, for the latest version if you have cloned the directory:
74
+
75
+ ```shell
76
+ pip install .
77
+ ```
78
+
79
+ or for the latest version wihtout cloning the directory
80
+
81
+ ```shell
82
+ pip install git+https://github.com/leoguignard/LineageTree
83
+ ```
@@ -0,0 +1,136 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools.packages.find]
6
+ where = ["src"]
7
+
8
+ [project]
9
+ authors = [
10
+ { name = "Léo Guignard", email = "leo.guignard@univ-amu.fr"},
11
+ { name = "Giannis Liaskas"},
12
+ { name = "Christopher Mazzerbo"},
13
+ ]
14
+ maintainers = [
15
+ {name = "Léo Guignard", email = "leo.guignard@univ-amu.fr"}
16
+ ]
17
+ name = "lineagetree"
18
+ description = "Structure for Lineage Trees"
19
+ version = "3.0.0"
20
+ license = "MIT"
21
+ license-files = [ "LICENSE" ]
22
+ readme = {file = "README.md", content-type = "text/markdown"}
23
+ requires-python = ">= 3.10"
24
+ classifiers = [
25
+ "Development Status :: 4 - Beta",
26
+ "Intended Audience :: Developers",
27
+ "Operating System :: OS Independent",
28
+ "Programming Language :: Python",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3 :: Only",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Programming Language :: Python :: 3.13",
35
+ ]
36
+
37
+ dependencies = [
38
+ "scipy>=1.9",
39
+ "numpy>=1.23",
40
+ "matplotlib",
41
+ "edist",
42
+ "svgwrite",
43
+ "Micro-Mastodon-Reader",
44
+ ]
45
+
46
+ [project.optional-dependencies]
47
+ dev = [
48
+ "pytest",
49
+ "pytest-cov",
50
+ "black",
51
+ "ruff",
52
+ "bumpver",
53
+ ]
54
+ doc = [
55
+ "sphinx-rtd-theme",
56
+ "myst-parser",
57
+ "sphinx_book_theme",
58
+ ]
59
+ test = [
60
+ "pytest",
61
+ "pytest-cov",
62
+ ]
63
+
64
+ [project.urls]
65
+ "Bug Tracker" = "https://github.com/GuignardLab/LineageTree/issues"
66
+ "Documentation" = "https://github.com/GuignardLab/LineageTree#README.md"
67
+ "Source Code" = "https://github.com/GuignardLab/LineageTree"
68
+ "User Support" = "https://github.com/GuignardLab/LineageTree/issues"
69
+
70
+ [tool.black]
71
+ line-length = 79
72
+
73
+ [tool.isort]
74
+ profile = "black"
75
+ line_length = 79
76
+
77
+ [tool.bumpver]
78
+ current_version = "3.0.0"
79
+ version_pattern = "MAJOR.MINOR.PATCH[-TAG]"
80
+ commit_message = "bump version {old_version} -> {new_version}"
81
+ commit = true
82
+ tag = true
83
+ push = false
84
+
85
+ [tool.bumpver.file_patterns]
86
+ "pyproject.toml" = [
87
+ 'current_version = "{version}"',
88
+ 'version = "{version}"',
89
+ ]
90
+ "src/lineagetree/__init__.py" = [
91
+ '__version__ = "{version}"',
92
+ ]
93
+
94
+ [tool.ruff]
95
+ line-length = 79
96
+ exclude = [
97
+ ".bzr",
98
+ ".direnv",
99
+ ".eggs",
100
+ ".git",
101
+ ".mypy_cache",
102
+ ".pants.d",
103
+ ".ruff_cache",
104
+ ".svn",
105
+ ".tox",
106
+ ".venv",
107
+ "__pypackages__",
108
+ "_build",
109
+ "buck-out",
110
+ "build",
111
+ "dist",
112
+ "node_modules",
113
+ "venv",
114
+ "*vendored*",
115
+ "*_vendor*",
116
+ ]
117
+
118
+ target-version = "py313"
119
+
120
+ [tool.ty.environment]
121
+ root = ["./src", ]
122
+
123
+ [tool.ty.rules]
124
+ unresolved-import = "ignore"
125
+
126
+ [tool.coverage.run]
127
+ source = ["lineagetree"]
128
+
129
+ [tool.coverage.report]
130
+ omit = [
131
+ "*/test/*",
132
+ "*/.vscode/*",
133
+ "*/site-packages/*",
134
+ "*/_io/*",
135
+ "*/_methodize.py",
136
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,30 @@
1
+ __version__ = "3.0.0"
2
+ from .lineage_tree import LineageTree
3
+ from ._io._loaders import (
4
+ read_from_ASTEC,
5
+ read_from_binary,
6
+ read_from_csv,
7
+ read_from_mamut_xml,
8
+ read_from_mastodon,
9
+ read_from_mastodon_csv,
10
+ read_from_tgmm_xml,
11
+ read_from_txt_for_celegans,
12
+ read_from_txt_for_celegans_BAO,
13
+ read_from_txt_for_celegans_CAO,
14
+ )
15
+ from .lineage_tree_manager import LineageTreeManager
16
+
17
+ __all__ = (
18
+ "LineageTree",
19
+ "LineageTreeManager",
20
+ "read_from_tgmm_xml",
21
+ "read_from_txt_for_celegans_BAO",
22
+ "read_from_ASTEC",
23
+ "read_from_binary",
24
+ "read_from_csv",
25
+ "read_from_mamut_xml",
26
+ "read_from_mastodon_csv",
27
+ "read_from_mastodon",
28
+ "read_from_txt_for_celegans",
29
+ "read_from_txt_for_celegans_CAO",
30
+ )
@@ -0,0 +1,207 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable, Iterable
4
+ from functools import wraps
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from ..lineage_tree import LineageTree
9
+
10
+
11
+ def modifier(wrapped_func):
12
+ @wraps(wrapped_func)
13
+ def raising_flag(self, *args, **kwargs):
14
+ should_reset = (
15
+ not hasattr(self, "_has_been_reset") or not self._has_been_reset
16
+ )
17
+ out_func = wrapped_func(self, *args, **kwargs)
18
+ if should_reset:
19
+ for prop in self._protected_dynamic_properties:
20
+ self.__dict__[prop] = None
21
+ self._has_been_reset = True
22
+ return out_func
23
+
24
+ return raising_flag
25
+
26
+
27
+ ###TODO pos can be callable and stay motionless (copy the position of the succ node, use something like optical flow)
28
+ @modifier
29
+ def add_chain(
30
+ lT: LineageTree,
31
+ node: int,
32
+ length: int,
33
+ downstream: bool,
34
+ pos: Callable | None = None,
35
+ ) -> int:
36
+ """Adds a chain of specific length to a node either as a successor or as a predecessor.
37
+ If it is placed on top of a tree all the nodes will move timepoints #length down.
38
+
39
+ Parameters
40
+ ----------
41
+ node : int
42
+ Id of the successor (predecessor if `downstream==False`)
43
+ length : int
44
+ The length of the new chain.
45
+ downstream : bool, default=True
46
+ If `True` will create a chain that goes forwards in time otherwise backwards.
47
+ pos : np.ndarray, optional
48
+ The new position of the chain. Defaults to None.
49
+
50
+ Returns
51
+ -------
52
+ int
53
+ Id of the first node of the sublineage.
54
+ """
55
+ if length == 0:
56
+ return node
57
+ if length < 1:
58
+ raise ValueError("Length cannot be <1")
59
+ if downstream:
60
+ for _ in range(int(length)):
61
+ old_node = node
62
+ node = lT._add_node(pred=[old_node])
63
+ lT._time[node] = lT._time[old_node] + 1
64
+ else:
65
+ if lT._predecessor[node]:
66
+ raise Warning("The node already has a predecessor.")
67
+ if lT._time[node] - length < lT.t_b:
68
+ raise Warning(
69
+ "A node cannot created outside the lower bound of the dataset. (It is possible to change it by lT.t_b = int(...))"
70
+ )
71
+ for _ in range(int(length)):
72
+ old_node = node
73
+ node = lT._add_node(succ=[old_node])
74
+ lT._time[node] = lT._time[old_node] - 1
75
+ return node
76
+
77
+
78
+ @modifier
79
+ def add_root(lT: LineageTree, t: int, pos: list | None = None) -> int:
80
+ """Adds a root to a specific timepoint.
81
+
82
+ Parameters
83
+ ----------
84
+ lT : LineageTree
85
+ The LineageTree instance.
86
+ t :int
87
+ The timepoint the node is going to be added.
88
+ pos : list
89
+ The position of the new node.
90
+ Returns
91
+ -------
92
+ int
93
+ The id of the new root.
94
+ """
95
+ C_next = lT.get_next_id()
96
+ lT._successor[C_next] = ()
97
+ lT._predecessor[C_next] = ()
98
+ lT._time[C_next] = t
99
+ lT.pos[C_next] = pos if isinstance(pos, list) else []
100
+ lT._changed_roots = True
101
+ return C_next
102
+
103
+
104
+ def get_next_id(lT) -> int:
105
+ """Computes the next authorized id and assign it.
106
+
107
+ Returns
108
+ -------
109
+ int
110
+ next authorized id
111
+ """
112
+ if not hasattr(lT, "max_id") or (lT.max_id == -1 and lT.nodes):
113
+ lT.max_id = max(lT.nodes) if len(lT.nodes) else 0
114
+ if not hasattr(lT, "next_id") or lT.next_id == []:
115
+ lT.max_id += 1
116
+ return lT.max_id
117
+ else:
118
+ return lT.next_id.pop()
119
+
120
+
121
+ @modifier
122
+ def _add_node(
123
+ lT: LineageTree,
124
+ succ: list | None = None,
125
+ pred: list | None = None,
126
+ pos: Iterable | None = None,
127
+ nid: int | None = None,
128
+ ) -> int:
129
+ """Adds a node to the LineageTree object that is either a successor or a predecessor of another node.
130
+ Does not handle time! You cannot enter both a successor and a predecessor.
131
+
132
+ Parameters
133
+ ----------
134
+ lT : LineageTree
135
+ The LineageTree instance.
136
+ succ : list
137
+ list of ids of the nodes the new node is a successor to
138
+ pred : list
139
+ list of ids of the nodes the new node is a predecessor to
140
+ pos : np.ndarray, optional
141
+ position of the new node
142
+ nid : int, optional
143
+ id value of the new node, to be used carefully,
144
+ if None is provided the new id is automatically computed.
145
+
146
+ Returns
147
+ -------
148
+ int
149
+ id of the new node.
150
+ """
151
+ if not succ and not pred:
152
+ raise Warning(
153
+ "Please enter a successor or a predecessor, otherwise use the add_roots() function."
154
+ )
155
+ C_next = lT.get_next_id() if nid is None else nid
156
+ if succ:
157
+ lT._successor[C_next] = succ
158
+ for suc in succ:
159
+ lT._predecessor[suc] = (C_next,)
160
+ else:
161
+ lT._successor[C_next] = ()
162
+ if pred:
163
+ lT._predecessor[C_next] = pred
164
+ lT._successor[pred[0]] = lT._successor.setdefault(pred[0], ()) + (
165
+ C_next,
166
+ )
167
+ else:
168
+ lT._predecessor[C_next] = ()
169
+ if isinstance(pos, list):
170
+ lT.pos[C_next] = pos
171
+ return C_next
172
+
173
+
174
+ @modifier
175
+ def remove_nodes(lT: LineageTree, group: int | set | list) -> None:
176
+ """Removes a group of nodes from the LineageTree
177
+
178
+ Parameters
179
+ ----------
180
+ lT : LineageTree
181
+ The LineageTree instance.
182
+ group : set of int or list of int or int
183
+ One or more nodes that are to be removed.
184
+ """
185
+ if isinstance(group, int | float):
186
+ group = {group}
187
+ if isinstance(group, list):
188
+ group = set(group)
189
+ group = lT.nodes.intersection(group)
190
+ for node in group:
191
+ for attr in lT.__dict__:
192
+ attr_value = lT.__getattribute__(attr)
193
+ if isinstance(attr_value, dict) and attr not in [
194
+ "successor",
195
+ "predecessor",
196
+ "_successor",
197
+ "_predecessor",
198
+ ]:
199
+ attr_value.pop(node, ())
200
+ if lT._predecessor.get(node):
201
+ lT._successor[lT._predecessor[node][0]] = tuple(
202
+ set(lT._successor[lT._predecessor[node][0]]).difference(group)
203
+ )
204
+ for p_node in lT._successor.get(node, []):
205
+ lT._predecessor[p_node] = ()
206
+ lT._predecessor.pop(node, ())
207
+ lT._successor.pop(node, ())