LineageTree 1.7.0__py3-none-any.whl → 2.0.1__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.
LineageTree/utils.py CHANGED
@@ -1,126 +1,88 @@
1
- import csv
2
- import warnings
3
-
4
- from LineageTree import lineageTree
5
-
6
- try:
7
- import motile
8
- except ImportError:
9
- warnings.warn(
10
- "No motile installed therefore you will not be able to produce links with motile."
11
- )
12
-
13
-
14
- def to_motile(
15
- lT: lineageTree, crop: int = None, max_dist=200, max_skip_frames=1
16
- ):
17
- try:
18
- import networkx as nx
19
- except:
20
- raise Warning("Please install networkx")
21
-
22
- fmt = nx.DiGraph()
23
- if not crop:
24
- crop = lT.t_e
25
- # time_nodes = [
26
- for time in range(crop):
27
- # time_nodes += lT.time_nodes[time]
28
- # print(time_nodes)
29
- for time_node in lT.time_nodes[time]:
30
- fmt.add_node(
31
- time_node,
32
- t=lT.time[time_node],
33
- pos=lT.pos[time_node],
34
- score=1,
35
- )
36
- # for suc in lT.successor:
37
- # fmt.add_edge(time_node, suc, **{"score":0})
38
-
39
- motile.add_cand_edges(fmt, max_dist, max_skip_frames=max_skip_frames)
40
-
41
- return fmt
42
-
43
-
44
- def write_csv_from_lT_to_lineaja(
45
- lT, path_to, start: int = 0, finish: int = 300
46
- ):
47
- csv_dict = {}
48
- for time in range(start, finish):
49
- for node in lT.time_nodes[time]:
50
- csv_dict[node] = {"pos": lT.pos[node], "t": time}
51
- with open(path_to, "w", newline="\n") as file:
52
- fieldnames = [
53
- "time",
54
- "positions_x",
55
- "positions_y",
56
- "positions_z",
57
- "id",
58
- ]
59
- writer = csv.DictWriter(file, fieldnames=fieldnames)
60
- writer.writeheader()
61
- for node in csv_dict:
62
- writer.writerow(
63
- {
64
- "time": csv_dict[node]["t"],
65
- "positions_z": csv_dict[node]["pos"][0],
66
- "positions_y": csv_dict[node]["pos"][1],
67
- "positions_x": csv_dict[node]["pos"][2],
68
- "id": node,
69
- }
70
- )
71
-
72
-
73
- def create_links_and_cycles(lT: lineageTree, roots=None):
74
- """Generates a dictionary containing the links and the lengths of each branch.
75
- Similar to simple tree, mainly used for tree manip app.
76
-
77
- Args:
78
- roots (Union[list,set,int]): The roots from which the tree will be generated.
79
-
80
- Returns:
81
- dict: A dictionary with keys "links" and "times" which contains the connections all cells and their branch
82
- length.
1
+ from collections.abc import Iterable
2
+
3
+ from LineageTree import lineageTree, tree_approximation
4
+
5
+
6
+ def create_links_and_chains(
7
+ lT: lineageTree,
8
+ roots: int | Iterable | None = None,
9
+ end_time: int | None = None,
10
+ ) -> dict[str, dict]:
11
+ """Generates a dictionary containing all the edges (from start of lifetime to end not the intermediate timepoints)
12
+ of a subtree spawned by node/s and their duration
13
+
14
+
15
+ Parameters
16
+ ----------
17
+ lT : lineageTree
18
+ The lineagetree that the user is working on
19
+ roots : int or Iterable, optional
20
+ The root/s from which the tree/s will be generated, if 'None' all the roots will be selected.
21
+ end_time : int, optional
22
+ The last timepoint to be considered, if 'None' the last timepoint of the dataset (t_e) is considered, by default None.
23
+
24
+ Returns
25
+ -------
26
+ dict mapping str to set or dict mapping int to list or int
27
+ A dictionary that contains:
28
+ - "links": The dictionary that contains the hierarchy of the nodes (only start and end of each chain)
29
+ - "times": The time distance between the start and the end of a chain
30
+ - "roots": The roots used
83
31
  """
84
32
  if roots is None:
85
33
  to_do = set(lT.roots)
86
- elif isinstance(roots, list):
34
+ elif isinstance(roots, Iterable):
87
35
  to_do = set(roots)
88
36
  else:
89
37
  to_do = {int(roots)}
38
+ if end_time is None:
39
+ end_time = lT.t_e
90
40
  times = {}
91
41
  links = {}
92
42
  while to_do:
93
43
  curr = to_do.pop()
94
- cyc = lT.get_successors(curr)
95
- last = cyc[-1]
96
- times[curr] = len(cyc)
97
- if last != curr:
98
- links[curr] = [cyc[-1]]
99
- else:
100
- links[curr] = []
101
- succ = lT.successor.get(last)
102
- if succ:
103
- times[cyc[-1]] = 0
104
- to_do.update(succ)
44
+ cyc = lT.get_successors(curr, end_time=end_time)
45
+ if cyc[-1] != curr or lT.time[cyc[-1]] <= end_time:
46
+ last = cyc[-1]
47
+ times[curr] = len(cyc)
48
+ if last != curr:
49
+ links[curr] = [last]
50
+ else:
51
+ links[curr] = []
52
+ succ = lT._successor.get(last)
53
+ if succ:
54
+ times[cyc[-1]] = 0
55
+ to_do.update(succ)
105
56
  links[last] = succ
106
57
  return {"links": links, "times": times, "root": roots}
107
58
 
108
59
 
109
60
  def hierarchical_pos(
110
61
  lnks_tms: dict, root, width=1000, vert_gap=2, xcenter=0, ycenter=0
111
- ):
112
- """Calculates the position of each node on te tree graph.
113
-
114
- Args:
115
- lnks_tms (dict): a dictionary created by create_links_and_cycles.
116
- root (_type_): The id of the node, usually it exists inside lnks_tms dictionary, however you may use your own root.
117
- width (int, optional): Max width, will not change the graph but the interacting with the graph takes this distance into account. Defaults to 1000.
118
- vert_gap (int, optional): How far downwards each timepoint will go. Defaults to 2.
119
- xcenter (int, optional): Where the root will be placed on the x axis. Defaults to 0.
120
- ycenter (int, optional): Where the root will be placed on the y axis. Defaults to 0.
121
-
122
- Returns:
123
- _type_: _description_
62
+ ) -> dict[int, list[float]] | None:
63
+ """Calculates the position of each node on the tree graph.
64
+
65
+ Parameters
66
+ ----------
67
+ lnks_tms : dict
68
+ a dictionary created by create_links_and_chains.
69
+ root : _type_
70
+ The id of the node, usually it exists inside lnks_tms dictionary, however you may use your own root.
71
+ width : int, optional
72
+ Max width, will not change the graph but interacting with the graph takes this distance into account, by default 1000
73
+ vert_gap : int, optional
74
+ How far downwards each timepoint will go, by default 2
75
+ xcenter : int, optional
76
+ Where the root will be placed on the x axis, by default 0
77
+ ycenter : int, optional
78
+ Where the root will be placed on the y axis, by default 0
79
+
80
+ Returns
81
+ -------
82
+ dict mapping int to list of float
83
+ Provides a dictionary that contains the id of each node as keys and its 2-d position on the
84
+ tree graph as values.
85
+ If the root requested does not exists, None is then returned
124
86
  """
125
87
  to_do = [root]
126
88
  if root not in lnks_tms["times"]:
@@ -135,7 +97,9 @@ def hierarchical_pos(
135
97
  elif len(succ) == 1:
136
98
  pos_node[succ[0]] = [
137
99
  pos_node[curr][0],
138
- pos_node[curr][1] - lnks_tms["times"].get(curr, 0),
100
+ pos_node[curr][1]
101
+ - lnks_tms["times"].get(curr, 0)
102
+ + min(vert_gap, lnks_tms["times"].get(curr, 0)),
139
103
  ]
140
104
  to_do.extend(succ)
141
105
  prev_width[succ[0]] = prev_width[curr]
@@ -154,3 +118,37 @@ def hierarchical_pos(
154
118
  prev_width[curr] / 2,
155
119
  )
156
120
  return pos_node
121
+
122
+
123
+ def convert_style_to_number(
124
+ style: str | tree_approximation.TreeApproximationTemplate,
125
+ downsample: int | None,
126
+ ) -> int:
127
+ """Converts tree_style and downsampling to a single number.
128
+
129
+ Parameters
130
+ ----------
131
+ style : str
132
+ the tree style
133
+ downsample : int
134
+ the downsampling factor
135
+
136
+ Returns
137
+ -------
138
+ int
139
+ A number which serves as ID if the tree style and downsampling used.
140
+ """
141
+ style_dict = {
142
+ "full": 0,
143
+ "simple": -1,
144
+ "normalized_simple": -2,
145
+ "mini": -1000,
146
+ }
147
+ if style == "downsampled" and downsample is not None:
148
+ return downsample
149
+ elif not isinstance(style, str) and issubclass(
150
+ style, tree_approximation.TreeApproximationTemplate
151
+ ):
152
+ return hash(style.__name__)
153
+ else:
154
+ return style_dict[style]
@@ -1,53 +1,49 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: LineageTree
3
- Version: 1.7.0
4
- Summary: Lineage tree structure
5
- Home-page: https://github.com/leoguignard/LineageTree
6
- Author: Léo Guignard
7
- Author-email: leo.guignard@univ-amu.fr
8
- License: MIT
9
- Project-URL: Bug Tracker, https://github.com/leoguignard/LineageTree/issues
10
- Project-URL: Documentation, https://github.com/leoguignard/LineageTree#README.md
11
- Project-URL: Source Code, https://github.com/leoguignard/LineageTree
12
- Project-URL: User Support, https://github.com/leoguignard/LineageTree/issues
13
- Classifier: Development Status :: 2 - Pre-Alpha
3
+ Version: 2.0.1
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
14
  Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Operating System :: OS Independent
17
16
  Classifier: Programming Language :: Python
18
17
  Classifier: Programming Language :: Python :: 3
19
18
  Classifier: Programming Language :: Python :: 3 :: Only
20
- Classifier: Programming Language :: Python :: 3.8
21
- Classifier: Programming Language :: Python :: 3.9
22
19
  Classifier: Programming Language :: Python :: 3.10
23
- Classifier: Topic :: Scientific/Engineering :: Image Processing
24
- Requires-Python: >=3.8
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
25
24
  Description-Content-Type: text/markdown
26
25
  License-File: LICENSE
27
26
  Requires-Dist: scipy>=1.9
28
27
  Requires-Dist: numpy>=1.23
29
28
  Requires-Dist: mastodon-reader
30
29
  Requires-Dist: matplotlib
31
- Provides-Extra: svg
32
- Requires-Dist: svgwrite; extra == "svg"
33
- Requires-Dist: matplotlib; extra == "svg"
30
+ Requires-Dist: edist
31
+ Requires-Dist: svgwrite
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest; extra == "dev"
34
+ Requires-Dist: pytest-cov; extra == "dev"
35
+ Requires-Dist: tox; extra == "dev"
36
+ Requires-Dist: black; extra == "dev"
37
+ Requires-Dist: ruff; extra == "dev"
38
+ Requires-Dist: bumpver; extra == "dev"
39
+ Provides-Extra: doc
40
+ Requires-Dist: sphinx-rtd-theme; extra == "doc"
41
+ Requires-Dist: myst-parser; extra == "doc"
42
+ Requires-Dist: sphinx_book_theme; extra == "doc"
34
43
  Provides-Extra: test
35
- Requires-Dist: svgwrite; extra == "test"
36
- Requires-Dist: matplotlib; extra == "test"
37
- Requires-Dist: tox; extra == "test"
38
44
  Requires-Dist: pytest; extra == "test"
39
45
  Requires-Dist: pytest-cov; extra == "test"
40
- Requires-Dist: mastodon-reader; extra == "test"
41
- Provides-Extra: treeedit
42
- Requires-Dist: edist; extra == "treeedit"
43
- Provides-Extra: all
44
- Requires-Dist: svgwrite; extra == "all"
45
- Requires-Dist: matplotlib; extra == "all"
46
- Requires-Dist: tox; extra == "all"
47
- Requires-Dist: pytest; extra == "all"
48
- Requires-Dist: pytest-cov; extra == "all"
49
- Requires-Dist: edist; extra == "all"
50
- Requires-Dist: mastodon-reader; extra == "all"
46
+ Dynamic: license-file
51
47
 
52
48
  # LineageTree
53
49
 
@@ -59,6 +55,7 @@ With LineageTree you can read from:
59
55
  - MaMuT files described in [Wolff et al. 2018](https://doi.org/10.7554/eLife.34410)
60
56
  - SVF algorithm outputs described in [McDole, Guignard et al. 2018](https://doi.org/10.1016/j.cell.2018.09.031)
61
57
  - ASTEC algorithm outputs described in [Guignard, Fiuza et al. 2020](https://doi.org/10.1126/science.aar5663)
58
+ - 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)
62
59
  - and few others
63
60
 
64
61
  ## Basic usage
@@ -0,0 +1,16 @@
1
+ LineageTree/__init__.py,sha256=Mkxh44uNuGueMMHEnOWpEchFefwMS6rht_EqHShIYew,749
2
+ LineageTree/lineageTree.py,sha256=dWpGuQ_h4JMvTzOBaMg_dC4mkPwIR2Rb3EvDKowcVxM,125146
3
+ LineageTree/lineageTreeManager.py,sha256=25QWNbj3Ep7itQKd6LrddICG457BEp1DlfKWgVHlx5I,31753
4
+ LineageTree/loaders.py,sha256=rjLHSaukBI23oxndkqrkK_6UBjmzU4LUkr_lgOaZqvM,30189
5
+ LineageTree/tree_approximation.py,sha256=61Omn0gUaRiNTDcIucDM4DUPBW5voXRxCZQ0uYGRDSg,16170
6
+ LineageTree/utils.py,sha256=EOH2MGlBH3cFs8DVOX-3fJRwoj0ZmiE_nPkAjzWXK48,5124
7
+ LineageTree/legacy/export_csv.py,sha256=cJ1R3GCGEy9Oey73jbFhoKRGTonNbqg229fVnGm_FuU,2009
8
+ LineageTree/legacy/to_lineajea.py,sha256=iZyZTsrvWYHkGJ8npf91lQPrXPgKouiy7Zrywx5Lkkw,926
9
+ LineageTree/legacy/to_motile.py,sha256=4AC9BmGa-hpqt8_x6HTR_XqCrTERlKo9WwYHUFtheM0,860
10
+ LineageTree/test/test_lineageTree.py,sha256=d5uucokzE7OWQirsW7XxaGEgeRszQ5KICbAYhriXEQo,16492
11
+ LineageTree/test/test_uted.py,sha256=Vl7uNGZp57XUD3v3SHRJtXhb0Lte8mDjpOtEKp_dxt0,6496
12
+ lineagetree-2.0.1.dist-info/licenses/LICENSE,sha256=IKncNCNpq93Kq7Ywg1V4I9Bu_99VMK-mX1EJyjTKLyc,1068
13
+ lineagetree-2.0.1.dist-info/METADATA,sha256=UwtX1LWAG-s-7aaGkss_UTpourt3InBk5ab5x-4W4i0,4300
14
+ lineagetree-2.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ lineagetree-2.0.1.dist-info/top_level.txt,sha256=CCqPCTX76zPTEUagUgTWbLaZun8752n59iOOwfUlvxs,12
16
+ lineagetree-2.0.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,322 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from enum import Enum
3
-
4
- import numpy as np
5
-
6
- from LineageTree import lineageTree
7
-
8
-
9
- class abstract_trees(ABC):
10
- """Template class to produce different techniques to comapare lineageTrees.
11
- To add a new technique you need to iherit this class or one of its children
12
- and add them to the tree_style enum.
13
- For a class to be valid you need a
14
- - tree constructor (get_tree) that produces one dictionary that contains
15
- arbitary unique labels and one dictionary that contains the duration of each node.
16
- - delta function: A function that handles the cost of comparing nodes to each other.
17
- - normalization function, a function that returns the length of the tree or any interger.
18
- """
19
-
20
- def __init__(
21
- self,
22
- lT: lineageTree,
23
- root: int,
24
- downsample: int,
25
- end_time: int = None,
26
- time_scale: int = None,
27
- ):
28
- self.lT: lineageTree = lT
29
- self.root: int = root
30
- self.downsample: int = downsample
31
- self.end_time: int = end_time if end_time else self.lT.t_e
32
- self.time_scale: int = time_scale if time_scale is not None else 1
33
- self.tree: tuple = self.get_tree()
34
- self.edist = self._edist_format(self.tree[0])
35
-
36
- @abstractmethod
37
- def get_tree(self):
38
- """
39
- Get a tree version of the tree spawned by the node `r`
40
-
41
- Args:
42
- r (int): root of the tree to spawn
43
- end_time (int): the last time point to consider
44
- time_resolution (float): the time between two consecutive time points
45
-
46
- Returns:
47
- (dict) {m (int): [d1 (int), d2 (int)]}: an adjacency dictionnary
48
- where the ids are the ids of the cells in the original tree
49
- at their first time point (except for the cell `r` if it was
50
- not the first time point).
51
- (dict) {m (int): duration (float)}: life time duration of the cell `m`
52
- """
53
-
54
- @abstractmethod
55
- def delta(self, x, y, corres1, corres2, times1, times2):
56
- """The distance of two nodes inside a tree. Behaves like a staticmethod.
57
- The corres1/2 and time1/2 should always be provided and will be handled accordingly by the specific
58
- delta of each tree style.
59
-
60
- Args:
61
- x (int): The first node to compare, takes the names provided by the edist.
62
- y (int): The second node to compare, takes the names provided by the edist
63
- corres1 (dict): Correspondance between node1 and its name in the real tree.
64
- corres2 (dict): Correspondance between node2 and its name in the real tree.
65
- times1 (dict): The dictionary of the branch lengths of the tree that n1 is spawned from.
66
- times2 (dict): The dictionary of the branch lengths of the tree that n2 is spawned from.
67
-
68
- Returns:
69
- (int|float): The diatance between these 2 nodes.
70
- """
71
- if x is None and y is None:
72
- return 0
73
- if x is None:
74
- return times2[corres2[y]]
75
- if y is None:
76
- return times1[corres1[x]]
77
- len_x = times1[corres1[x]]
78
- len_y = times2[corres2[y]]
79
- return np.abs(len_x - len_y)
80
-
81
- @abstractmethod
82
- def get_norm(self):
83
- """
84
- Returns the valid value for normalizing the edit distance.
85
- Returns:
86
- (int|float): The number of nodes of each tree according to each style.
87
- """
88
-
89
- def _edist_format(self, adj_dict: dict):
90
- """Formating the custom tree style to the format needed by edist.
91
- SHOULD NOT BE CHANGED.
92
-
93
- Args:
94
- adj_dict (dict): _description_
95
-
96
- Returns:
97
- _type_: _description_
98
- """
99
- inv_adj = {vi: k for k, v in adj_dict.items() for vi in v}
100
- roots = set(adj_dict).difference(inv_adj)
101
- nid2list = {}
102
- list2nid = {}
103
- nodes = []
104
- adj_list = []
105
- curr_id = 0
106
- for r in roots:
107
- to_do = [r]
108
- while to_do:
109
- curr = to_do.pop(0)
110
- nid2list[curr] = curr_id
111
- list2nid[curr_id] = curr
112
- nodes.append(curr_id)
113
- to_do = adj_dict.get(curr, []) + to_do
114
- curr_id += 1
115
- adj_list = [
116
- [nid2list[d] for d in adj_dict.get(list2nid[_id], [])]
117
- for _id in nodes
118
- ]
119
- return nodes, adj_list, list2nid
120
-
121
-
122
- class mini_tree(abstract_trees):
123
- """Each branch is converted to a node of length 1, it is useful for comparing synchronous developing cells, extremely fast.
124
- Mainly used for testing.
125
- """
126
-
127
- def __init__(self, **kwargs):
128
- super().__init__(**kwargs)
129
-
130
- def get_tree(self):
131
- if self.end_time is None:
132
- self.end_time = self.lT.t_e
133
- out_dict = {}
134
- self.times = {}
135
- to_do = [self.root]
136
- while to_do:
137
- current = to_do.pop()
138
- cycle = np.array(self.lT.get_successors(current))
139
- cycle_times = np.array([self.lT.time[c] for c in cycle])
140
- cycle = cycle[cycle_times <= self.end_time]
141
- if cycle.size:
142
- _next = self.lT[cycle[-1]]
143
- if 1 < len(_next):
144
- out_dict[current] = _next
145
- to_do.extend(_next)
146
- else:
147
- out_dict[current] = []
148
- self.length = len(out_dict)
149
- return out_dict, None
150
-
151
- def get_norm(self):
152
- return len(
153
- self.lT.get_all_branches_of_node(self.root, end_time=self.end_time)
154
- )
155
-
156
- def _edist_format(self, adj_dict: dict):
157
- return super()._edist_format(adj_dict)
158
-
159
- def delta(self, x, y, corres1, corres2, times1, times2):
160
- if x is None and y is None:
161
- return 0
162
- if x is None:
163
- return 1
164
- if y is None:
165
- return 1
166
- return 0
167
-
168
-
169
- class simple_tree(abstract_trees):
170
- """Each branch is converted to one node with length the same as the life cycle of the cell.
171
- This method is fast, but imprecise, especialy for small trees (recommended height of the trees should be 100 at least).
172
- Use with CAUTION.
173
- """
174
-
175
- def __init__(self, **kwargs):
176
- super().__init__(**kwargs)
177
-
178
- def get_tree(self):
179
- if self.end_time is None:
180
- self.end_time = self.lT.t_e
181
- out_dict = {}
182
- self.times = {}
183
- to_do = [self.root]
184
- while to_do:
185
- current = to_do.pop()
186
- cycle = np.array(self.lT.get_successors(current))
187
- cycle_times = np.array([self.lT.time[c] for c in cycle])
188
- cycle = cycle[cycle_times <= self.end_time]
189
- if cycle.size:
190
- _next = self.lT[cycle[-1]]
191
- if len(_next) > 1 and self.lT.time[cycle[-1]] < self.end_time:
192
- out_dict[current] = _next
193
- to_do.extend(_next)
194
- else:
195
- out_dict[current] = []
196
- self.times[current] = len(cycle) * self.time_scale
197
- return out_dict, self.times
198
-
199
- def delta(self, x, y, corres1, corres2, times1, times2):
200
- return super().delta(x, y, corres1, corres2, times1, times2)
201
-
202
- def get_norm(self):
203
- return (
204
- len(self.lT.get_sub_tree(self.root, end_time=self.end_time))
205
- * self.time_scale
206
- )
207
-
208
-
209
- class downsample_tree(abstract_trees):
210
- """Downsamples a tree so every n nodes are being used as one."""
211
-
212
- def __init__(self, **kwargs):
213
- super().__init__(**kwargs)
214
-
215
- def get_tree(self):
216
- self.out_dict = {}
217
- self.times = {}
218
- to_do = [self.root]
219
- while to_do:
220
- current = to_do.pop()
221
- _next = self.lT.get_cells_at_t_from_root(
222
- current, self.lT.time[current] + self.downsample
223
- )
224
- if _next == [current]:
225
- _next = None
226
- if _next and self.lT.time[_next[0]] <= self.end_time:
227
- self.out_dict[current] = _next
228
- to_do.extend(_next)
229
- else:
230
- self.out_dict[current] = []
231
- self.times[current] = self.downsample
232
- return self.out_dict, self.times
233
-
234
- def get_norm(self):
235
- return sum(self.times.values())
236
-
237
- def delta(self, x, y, corres1, corres2, times1, times2):
238
- if x is None and y is None:
239
- return 0
240
- if x is None:
241
- return self.downsample
242
- if y is None:
243
- return self.downsample
244
- return 0
245
-
246
-
247
- class normalized_simple_tree(simple_tree):
248
- def __init__(self, **kwargs):
249
- super().__init__(**kwargs)
250
-
251
- def delta(self, x, y, corres1, corres2, times1, times2):
252
- if x is None and y is None:
253
- return 0
254
- if x is None:
255
- return 1
256
- if y is None:
257
- return 1
258
- return abs(times1[corres1[x]] - times2[corres2[y]]) / (
259
- times1[corres1[x]] + times2[corres2[y]]
260
- )
261
-
262
- def get_norm(self):
263
- return len(
264
- self.lT.get_all_branches_of_node(self.root, end_time=self.end_time)
265
- )
266
-
267
-
268
- class full_tree(abstract_trees):
269
- """No approximations the whole tree is used here. Perfect accuracy, but heavy on ram and speed.
270
- Not recommended to use on napari.
271
-
272
- """
273
-
274
- def __init__(self, **kwargs):
275
- super().__init__(**kwargs)
276
-
277
- def get_tree(self) -> dict:
278
- self.out_dict = {}
279
- self.times = {}
280
- to_do = [self.root]
281
- while to_do:
282
- current = to_do.pop()
283
- _next = self.lT.successor.get(current, [])
284
- if _next and self.lT.time[_next[0]] <= self.end_time:
285
- if self.time_scale > 1:
286
- for _ in range(self.time_scale):
287
- next_id = self.lT.get_next_id()
288
- self.out_dict[current] = next_id
289
- current = next_id
290
- self.out_dict[current] = _next
291
- to_do.extend(_next)
292
- else:
293
- self.out_dict[current] = []
294
- self.times[current] = 1
295
- return self.out_dict, self.times
296
-
297
- def get_norm(self):
298
- return (
299
- len(self.lT.get_sub_tree(self.root, end_time=self.end_time))
300
- * self.time_scale
301
- )
302
-
303
- def delta(self, x, y, corres1, corres2, times1, times2):
304
- if x is None and y is None:
305
- return 0
306
- if x is None:
307
- return 1
308
- if y is None:
309
- return 1
310
- return 0
311
-
312
-
313
- class tree_style(Enum):
314
- mini = mini_tree
315
- simple = simple_tree
316
- normalized_simple = normalized_simple_tree
317
- downsampled = downsample_tree
318
- full = full_tree
319
-
320
- @classmethod
321
- def list_names(self):
322
- return [style.name for style in self]