LineageTree 1.6.1__py3-none-any.whl → 1.8.0__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/__init__.py +1 -1
- LineageTree/lineageTree.py +274 -273
- LineageTree/lineageTreeManager.py +50 -23
- LineageTree/loaders.py +284 -42
- LineageTree/tree_styles.py +99 -66
- LineageTree/utils.py +7 -11
- {LineageTree-1.6.1.dist-info → LineageTree-1.8.0.dist-info}/METADATA +11 -10
- LineageTree-1.8.0.dist-info/RECORD +11 -0
- {LineageTree-1.6.1.dist-info → LineageTree-1.8.0.dist-info}/WHEEL +1 -1
- LineageTree-1.6.1.dist-info/RECORD +0 -11
- {LineageTree-1.6.1.dist-info → LineageTree-1.8.0.dist-info}/LICENSE +0 -0
- {LineageTree-1.6.1.dist-info → LineageTree-1.8.0.dist-info}/top_level.txt +0 -0
LineageTree/tree_styles.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import warnings
|
1
2
|
from abc import ABC, abstractmethod
|
2
3
|
from enum import Enum
|
3
4
|
|
@@ -7,14 +8,32 @@ from LineageTree import lineageTree
|
|
7
8
|
|
8
9
|
|
9
10
|
class abstract_trees(ABC):
|
11
|
+
"""Template class to produce different techniques to comapare lineageTrees.
|
12
|
+
To add a new technique you need to iherit this class or one of its children
|
13
|
+
and add them to the tree_style enum.
|
14
|
+
For a class to be valid you need a
|
15
|
+
- tree constructor (get_tree) that produces one dictionary that contains
|
16
|
+
arbitary unique labels and one dictionary that contains the duration of each node.
|
17
|
+
- delta function: A function that handles the cost of comparing nodes to each other.
|
18
|
+
- normalization function, a function that returns the length of the tree or any interger.
|
19
|
+
"""
|
20
|
+
|
10
21
|
def __init__(
|
11
|
-
self,
|
22
|
+
self,
|
23
|
+
lT: lineageTree,
|
24
|
+
root: int,
|
25
|
+
downsample: int,
|
26
|
+
end_time: int = None,
|
27
|
+
time_scale: int = 1,
|
12
28
|
):
|
13
|
-
self.lT = lT
|
14
|
-
self.root = root
|
15
|
-
self.
|
16
|
-
self.end_time = end_time
|
17
|
-
self.
|
29
|
+
self.lT: lineageTree = lT
|
30
|
+
self.root: int = root
|
31
|
+
self.downsample: int = downsample
|
32
|
+
self.end_time: int = end_time if end_time else self.lT.t_e
|
33
|
+
self.time_scale: int = int(time_scale) if time_scale else 1
|
34
|
+
if time_scale <= 0:
|
35
|
+
raise Exception("Please used a valid time_scale (Larger than 0)")
|
36
|
+
self.tree: tuple = self.get_tree()
|
18
37
|
self.edist = self._edist_format(self.tree[0])
|
19
38
|
|
20
39
|
@abstractmethod
|
@@ -124,7 +143,7 @@ class mini_tree(abstract_trees):
|
|
124
143
|
cycle = cycle[cycle_times <= self.end_time]
|
125
144
|
if cycle.size:
|
126
145
|
_next = self.lT[cycle[-1]]
|
127
|
-
if len(_next)
|
146
|
+
if 1 < len(_next):
|
128
147
|
out_dict[current] = _next
|
129
148
|
to_do.extend(_next)
|
130
149
|
else:
|
@@ -177,86 +196,84 @@ class simple_tree(abstract_trees):
|
|
177
196
|
to_do.extend(_next)
|
178
197
|
else:
|
179
198
|
out_dict[current] = []
|
180
|
-
self.times[current] = len(
|
181
|
-
cycle
|
182
|
-
) # * time_resolution will be fixed when working on registered trees.
|
199
|
+
self.times[current] = len(cycle) * self.time_scale
|
183
200
|
return out_dict, self.times
|
184
201
|
|
185
202
|
def delta(self, x, y, corres1, corres2, times1, times2):
|
186
203
|
return super().delta(x, y, corres1, corres2, times1, times2)
|
187
204
|
|
188
205
|
def get_norm(self):
|
189
|
-
return
|
206
|
+
return (
|
207
|
+
len(self.lT.get_sub_tree(self.root, end_time=self.end_time))
|
208
|
+
* self.time_scale
|
209
|
+
)
|
190
210
|
|
191
211
|
|
192
|
-
class
|
193
|
-
"""
|
194
|
-
Instead of having branches with length == life cycle of cell,nodes of specific length are added on the
|
195
|
-
edges of the branch, providing both accuratr results and speed.
|
196
|
-
It's the recommended method for calculating edit distances on developing embryos.
|
197
|
-
"""
|
212
|
+
class downsample_tree(abstract_trees):
|
213
|
+
"""Downsamples a tree so every n nodes are being used as one."""
|
198
214
|
|
199
215
|
def __init__(self, **kwargs):
|
200
216
|
super().__init__(**kwargs)
|
217
|
+
if self.downsample == 0:
|
218
|
+
raise Exception("Please use a valid downsampling rate")
|
219
|
+
if self.downsample == 1:
|
220
|
+
warnings.warn(
|
221
|
+
"Downsampling rate of 1 is identical to the full tree.",
|
222
|
+
stacklevel=1,
|
223
|
+
)
|
201
224
|
|
202
225
|
def get_tree(self):
|
203
|
-
if self.end_time is None:
|
204
|
-
self.end_time = self.lT.t_e
|
205
226
|
self.out_dict = {}
|
206
227
|
self.times = {}
|
207
228
|
to_do = [self.root]
|
208
|
-
if not isinstance(self.node_length, list):
|
209
|
-
self.node_length = list(self.node_length)
|
210
229
|
while to_do:
|
211
230
|
current = to_do.pop()
|
212
|
-
|
213
|
-
|
231
|
+
_next = self.lT.get_cells_at_t_from_root(
|
232
|
+
current,
|
233
|
+
self.lT.time[current] + (self.downsample / self.time_scale),
|
214
234
|
)
|
215
|
-
if
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
)
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
].copy()
|
224
|
-
length_middle_node = (
|
225
|
-
len(cycle) - sum(current_node_lengths) * 2
|
226
|
-
)
|
227
|
-
times_tmp = (
|
228
|
-
current_node_lengths
|
229
|
-
+ [length_middle_node]
|
230
|
-
+ current_node_lengths[::-1]
|
231
|
-
)
|
232
|
-
pos_all_nodes = np.concatenate(
|
233
|
-
[[0], np.cumsum(times_tmp[:-1])]
|
234
|
-
)
|
235
|
-
track = cycle[pos_all_nodes]
|
236
|
-
self.out_dict.update(
|
237
|
-
{k: [v] for k, v in zip(track[:-1], track[1:])}
|
238
|
-
)
|
239
|
-
self.times.update(zip(track, times_tmp))
|
240
|
-
else:
|
241
|
-
for i, cell in enumerate(cycle[:-1]):
|
242
|
-
self.out_dict[cell] = [cycle[i + 1]]
|
243
|
-
self.times[cell] = 1
|
244
|
-
current = cycle[-1]
|
245
|
-
_next = self.lT[current]
|
246
|
-
self.times[current] = 1
|
247
|
-
if _next and self.lT.time[_next[0]] <= self.end_time:
|
248
|
-
to_do.extend(_next)
|
249
|
-
self.out_dict[current] = _next
|
250
|
-
else:
|
251
|
-
self.out_dict[current] = []
|
252
|
-
|
235
|
+
if _next == [current]:
|
236
|
+
_next = None
|
237
|
+
if _next and self.lT.time[_next[0]] <= self.end_time:
|
238
|
+
self.out_dict[current] = _next
|
239
|
+
to_do.extend(_next)
|
240
|
+
else:
|
241
|
+
self.out_dict[current] = []
|
242
|
+
self.times[current] = 1 # self.downsample
|
253
243
|
return self.out_dict, self.times
|
254
244
|
|
255
245
|
def get_norm(self):
|
256
|
-
return len(self.
|
246
|
+
return len(self.times.values()) * self.downsample / self.time_scale
|
257
247
|
|
258
248
|
def delta(self, x, y, corres1, corres2, times1, times2):
|
259
|
-
|
249
|
+
if x is None and y is None:
|
250
|
+
return 0
|
251
|
+
if x is None:
|
252
|
+
return self.downsample
|
253
|
+
if y is None:
|
254
|
+
return self.downsample
|
255
|
+
return 0
|
256
|
+
|
257
|
+
|
258
|
+
class normalized_simple_tree(simple_tree):
|
259
|
+
def __init__(self, **kwargs):
|
260
|
+
super().__init__(**kwargs)
|
261
|
+
|
262
|
+
def delta(self, x, y, corres1, corres2, times1, times2):
|
263
|
+
if x is None and y is None:
|
264
|
+
return 0
|
265
|
+
if x is None:
|
266
|
+
return 1
|
267
|
+
if y is None:
|
268
|
+
return 1
|
269
|
+
return abs(times1[corres1[x]] - times2[corres2[y]]) / (
|
270
|
+
times1[corres1[x]] + times2[corres2[y]]
|
271
|
+
)
|
272
|
+
|
273
|
+
def get_norm(self):
|
274
|
+
return len(
|
275
|
+
self.lT.get_all_branches_of_node(self.root, end_time=self.end_time)
|
276
|
+
)
|
260
277
|
|
261
278
|
|
262
279
|
class full_tree(abstract_trees):
|
@@ -276,24 +293,40 @@ class full_tree(abstract_trees):
|
|
276
293
|
current = to_do.pop()
|
277
294
|
_next = self.lT.successor.get(current, [])
|
278
295
|
if _next and self.lT.time[_next[0]] <= self.end_time:
|
296
|
+
if self.time_scale > 1:
|
297
|
+
for _ in range(self.time_scale - 1):
|
298
|
+
next_id = self.lT.get_next_id()
|
299
|
+
self.out_dict[current] = [next_id]
|
300
|
+
current = next_id
|
279
301
|
self.out_dict[current] = _next
|
280
302
|
to_do.extend(_next)
|
281
303
|
else:
|
304
|
+
for _ in range(self.time_scale - 1):
|
305
|
+
next_id = self.lT.get_next_id()
|
306
|
+
self.out_dict[current] = [next_id]
|
307
|
+
current = next_id
|
282
308
|
self.out_dict[current] = []
|
283
309
|
self.times[current] = 1
|
284
310
|
return self.out_dict, self.times
|
285
311
|
|
286
312
|
def get_norm(self):
|
287
|
-
return len(self.
|
313
|
+
return len(self.times) * self.time_scale
|
288
314
|
|
289
315
|
def delta(self, x, y, corres1, corres2, times1, times2):
|
290
|
-
|
316
|
+
if x is None and y is None:
|
317
|
+
return 0
|
318
|
+
if x is None:
|
319
|
+
return 1
|
320
|
+
if y is None:
|
321
|
+
return 1
|
322
|
+
return 0
|
291
323
|
|
292
324
|
|
293
325
|
class tree_style(Enum):
|
294
326
|
mini = mini_tree
|
295
327
|
simple = simple_tree
|
296
|
-
|
328
|
+
normalized_simple = normalized_simple_tree
|
329
|
+
downsampled = downsample_tree
|
297
330
|
full = full_tree
|
298
331
|
|
299
332
|
@classmethod
|
LineageTree/utils.py
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
import csv
|
2
|
-
import random
|
3
2
|
import warnings
|
4
3
|
|
5
|
-
|
6
4
|
from LineageTree import lineageTree
|
7
5
|
|
8
6
|
try:
|
9
7
|
import motile
|
10
8
|
except ImportError:
|
11
9
|
warnings.warn(
|
12
|
-
"No motile installed therefore you will not be able to produce links with motile."
|
10
|
+
"No motile installed therefore you will not be able to produce links with motile.",
|
11
|
+
stacklevel=2,
|
13
12
|
)
|
14
13
|
|
15
14
|
|
@@ -18,16 +17,13 @@ def to_motile(
|
|
18
17
|
):
|
19
18
|
try:
|
20
19
|
import networkx as nx
|
21
|
-
except:
|
22
|
-
raise Warning("Please install networkx")
|
20
|
+
except ImportError:
|
21
|
+
raise Warning("Please install networkx") # noqa: B904
|
23
22
|
|
24
23
|
fmt = nx.DiGraph()
|
25
24
|
if not crop:
|
26
25
|
crop = lT.t_e
|
27
|
-
# time_nodes = [
|
28
26
|
for time in range(crop):
|
29
|
-
# time_nodes += lT.time_nodes[time]
|
30
|
-
# print(time_nodes)
|
31
27
|
for time_node in lT.time_nodes[time]:
|
32
28
|
fmt.add_node(
|
33
29
|
time_node,
|
@@ -35,8 +31,6 @@ def to_motile(
|
|
35
31
|
pos=lT.pos[time_node],
|
36
32
|
score=1,
|
37
33
|
)
|
38
|
-
# for suc in lT.successor:
|
39
|
-
# fmt.add_edge(time_node, suc, **{"score":0})
|
40
34
|
|
41
35
|
motile.add_cand_edges(fmt, max_dist, max_skip_frames=max_skip_frames)
|
42
36
|
|
@@ -137,7 +131,9 @@ def hierarchical_pos(
|
|
137
131
|
elif len(succ) == 1:
|
138
132
|
pos_node[succ[0]] = [
|
139
133
|
pos_node[curr][0],
|
140
|
-
pos_node[curr][1]
|
134
|
+
pos_node[curr][1]
|
135
|
+
- lnks_tms["times"].get(curr, 0)
|
136
|
+
+ min(vert_gap, lnks_tms["times"].get(curr, 0)),
|
141
137
|
]
|
142
138
|
to_do.extend(succ)
|
143
139
|
prev_width[succ[0]] = prev_width[curr]
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: LineageTree
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.8.0
|
4
4
|
Summary: Lineage tree structure
|
5
5
|
Home-page: https://github.com/leoguignard/LineageTree
|
6
6
|
Author: Léo Guignard
|
@@ -28,14 +28,6 @@ Requires-Dist: scipy>=1.9
|
|
28
28
|
Requires-Dist: numpy>=1.23
|
29
29
|
Requires-Dist: mastodon-reader
|
30
30
|
Requires-Dist: matplotlib
|
31
|
-
Provides-Extra: all
|
32
|
-
Requires-Dist: svgwrite; extra == "all"
|
33
|
-
Requires-Dist: matplotlib; extra == "all"
|
34
|
-
Requires-Dist: tox; extra == "all"
|
35
|
-
Requires-Dist: pytest; extra == "all"
|
36
|
-
Requires-Dist: pytest-cov; extra == "all"
|
37
|
-
Requires-Dist: edist; extra == "all"
|
38
|
-
Requires-Dist: mastodon-reader; extra == "all"
|
39
31
|
Provides-Extra: svg
|
40
32
|
Requires-Dist: svgwrite; extra == "svg"
|
41
33
|
Requires-Dist: matplotlib; extra == "svg"
|
@@ -48,6 +40,14 @@ Requires-Dist: pytest-cov; extra == "test"
|
|
48
40
|
Requires-Dist: mastodon-reader; extra == "test"
|
49
41
|
Provides-Extra: treeedit
|
50
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"
|
51
51
|
|
52
52
|
# LineageTree
|
53
53
|
|
@@ -59,6 +59,7 @@ With LineageTree you can read from:
|
|
59
59
|
- MaMuT files described in [Wolff et al. 2018](https://doi.org/10.7554/eLife.34410)
|
60
60
|
- SVF algorithm outputs described in [McDole, Guignard et al. 2018](https://doi.org/10.1016/j.cell.2018.09.031)
|
61
61
|
- ASTEC algorithm outputs described in [Guignard, Fiuza et al. 2020](https://doi.org/10.1126/science.aar5663)
|
62
|
+
- 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
63
|
- and few others
|
63
64
|
|
64
65
|
## Basic usage
|
@@ -0,0 +1,11 @@
|
|
1
|
+
LineageTree/__init__.py,sha256=888vRBgs18oExOHp7Q87HSH1GL60m-YLLjkMWYnZcBI,155
|
2
|
+
LineageTree/lineageTree.py,sha256=SoxLcYXhr5wzuX9ZXMDsK5fePd_6_Boq-y4HVJ6mBBI,102326
|
3
|
+
LineageTree/lineageTreeManager.py,sha256=bBhJrCUmLa6D6cZ95jT4hTnNfG7q-l_FiqOf2oaBc2o,6458
|
4
|
+
LineageTree/loaders.py,sha256=eStgVrqYeUlYQ-r7EByCZo2t5cgn-mXARTWBzLSN12Q,32778
|
5
|
+
LineageTree/tree_styles.py,sha256=ZEj0HcoEaWk8zyObboXW-qoM3uzKAQgiNzX2-UUa0cg,11337
|
6
|
+
LineageTree/utils.py,sha256=sFIj1eDid1EKP09g_jrSgqk4NLdNVf7KjC5UwN0Fihc,4878
|
7
|
+
LineageTree-1.8.0.dist-info/LICENSE,sha256=IKncNCNpq93Kq7Ywg1V4I9Bu_99VMK-mX1EJyjTKLyc,1068
|
8
|
+
LineageTree-1.8.0.dist-info/METADATA,sha256=X9UUm6EP44N_O3Uv0JN4Bu9wXU9kXAB9Nh-lHkjHoEs,4469
|
9
|
+
LineageTree-1.8.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
10
|
+
LineageTree-1.8.0.dist-info/top_level.txt,sha256=CCqPCTX76zPTEUagUgTWbLaZun8752n59iOOwfUlvxs,12
|
11
|
+
LineageTree-1.8.0.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
LineageTree/__init__.py,sha256=fIOwpoeHI19rwQShQ50iRXyRg4OXzVo_A4q_BUDBmsk,155
|
2
|
-
LineageTree/lineageTree.py,sha256=lamlTaHtu3gPUCMKIvlCHQqGMbcQAHCvgS8MB6EHWTQ,102742
|
3
|
-
LineageTree/lineageTreeManager.py,sha256=tQMNmxAvUEd4fNLJViB-9DGItpgCRnSF-bS2_IdebMo,5566
|
4
|
-
LineageTree/loaders.py,sha256=hKd-69d_csjcsV0zabSTEOyy0NhprFqvnRAI18tYa9w,23718
|
5
|
-
LineageTree/tree_styles.py,sha256=IUEq8RHfwXTv3WXylpLVa5XoThaWt1i1cRsgZGXdd50,10634
|
6
|
-
LineageTree/utils.py,sha256=kC90fi8TFm8Pz7oom_bDsq7HPPKnFdJ6wI-x36fLvHE,4962
|
7
|
-
LineageTree-1.6.1.dist-info/LICENSE,sha256=IKncNCNpq93Kq7Ywg1V4I9Bu_99VMK-mX1EJyjTKLyc,1068
|
8
|
-
LineageTree-1.6.1.dist-info/METADATA,sha256=eDkg_GjSQtFDXXjmyagn-gXRosFsDq2n8706srcixA8,4195
|
9
|
-
LineageTree-1.6.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
10
|
-
LineageTree-1.6.1.dist-info/top_level.txt,sha256=CCqPCTX76zPTEUagUgTWbLaZun8752n59iOOwfUlvxs,12
|
11
|
-
LineageTree-1.6.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|