LineageTree 1.7.0__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 +82 -207
- LineageTree/lineageTreeManager.py +37 -12
- LineageTree/loaders.py +216 -42
- LineageTree/tree_styles.py +23 -11
- LineageTree/utils.py +7 -9
- {LineageTree-1.7.0.dist-info → LineageTree-1.8.0.dist-info}/METADATA +2 -1
- LineageTree-1.8.0.dist-info/RECORD +11 -0
- LineageTree-1.7.0.dist-info/RECORD +0 -11
- {LineageTree-1.7.0.dist-info → LineageTree-1.8.0.dist-info}/LICENSE +0 -0
- {LineageTree-1.7.0.dist-info → LineageTree-1.8.0.dist-info}/WHEEL +0 -0
- {LineageTree-1.7.0.dist-info → LineageTree-1.8.0.dist-info}/top_level.txt +0 -0
LineageTree/__init__.py
CHANGED
LineageTree/lineageTree.py
CHANGED
@@ -70,7 +70,12 @@ class lineageTree(lineageTreeLoaders):
|
|
70
70
|
sub = set(self.get_sub_tree(node))
|
71
71
|
specific_leaves = sub.intersection(self.leaves)
|
72
72
|
for leaf in specific_leaves:
|
73
|
-
self.add_branch(
|
73
|
+
self.add_branch(
|
74
|
+
leaf,
|
75
|
+
(self.t_e - self.time[leaf]),
|
76
|
+
reverse=True,
|
77
|
+
move_timepoints=True,
|
78
|
+
)
|
74
79
|
|
75
80
|
###TODO pos can be callable and stay motionless (copy the position of the succ node, use something like optical flow)
|
76
81
|
def add_branch(
|
@@ -88,7 +93,7 @@ class lineageTree(lineageTreeLoaders):
|
|
88
93
|
pred (int): Id of the successor (predecessor if reverse is False)
|
89
94
|
length (int): The length of the new branch.
|
90
95
|
pos (np.ndarray, optional): The new position of the branch. Defaults to None.
|
91
|
-
move_timepoints (bool): Moves the
|
96
|
+
move_timepoints (bool): Moves the time, important only if reverse= True
|
92
97
|
reverese (bool): If True will create a branch that goes forwards in time otherwise backwards.
|
93
98
|
Returns:
|
94
99
|
(int): Id of the first node of the sublineage.
|
@@ -138,14 +143,12 @@ class lineageTree(lineageTreeLoaders):
|
|
138
143
|
reverse=False,
|
139
144
|
)
|
140
145
|
pred = _next
|
146
|
+
self.successor[self.get_cycle(pred)[-1]] = []
|
141
147
|
self.labels[pred] = "New branch"
|
142
148
|
if self.time[pred] == self.t_b:
|
143
|
-
self.roots.add(pred)
|
144
149
|
self.labels[pred] = "New branch"
|
145
150
|
if original in self.roots and reverse is True:
|
146
|
-
self.roots.add(pred)
|
147
151
|
self.labels[pred] = "New branch"
|
148
|
-
self.roots.remove(original)
|
149
152
|
self.labels.pop(original, -1)
|
150
153
|
self.t_e = max(self.time_nodes)
|
151
154
|
return pred
|
@@ -167,9 +170,7 @@ class lineageTree(lineageTreeLoaders):
|
|
167
170
|
self.predecessor.pop(new_lT)
|
168
171
|
label_of_root = self.labels.get(cycle[0], cycle[0])
|
169
172
|
self.labels[cycle[0]] = f"L-Split {label_of_root}"
|
170
|
-
new_tr = self.add_branch(
|
171
|
-
new_lT, len(cycle) + 1, move_timepoints=False
|
172
|
-
)
|
173
|
+
new_tr = self.add_branch(new_lT, len(cycle), move_timepoints=False)
|
173
174
|
self.roots.add(new_tr)
|
174
175
|
self.labels[new_tr] = f"R-Split {label_of_root}"
|
175
176
|
return new_tr
|
@@ -209,7 +210,10 @@ class lineageTree(lineageTreeLoaders):
|
|
209
210
|
self.remove_nodes(new_root1)
|
210
211
|
self.successor[new_root2].append(next_root1)
|
211
212
|
self.predecessor[next_root1] = [new_root2]
|
212
|
-
new_branch = self.add_branch(
|
213
|
+
new_branch = self.add_branch(
|
214
|
+
new_root2,
|
215
|
+
length - 1,
|
216
|
+
)
|
213
217
|
self.labels[new_branch] = f"Fusion of {new_root1} and {new_root2}"
|
214
218
|
return new_branch
|
215
219
|
|
@@ -321,7 +325,7 @@ class lineageTree(lineageTreeLoaders):
|
|
321
325
|
new_length (int): The new length of the tree.
|
322
326
|
"""
|
323
327
|
if new_length <= 1:
|
324
|
-
warnings.warn("New length should be more than 1")
|
328
|
+
warnings.warn("New length should be more than 1", stacklevel=2)
|
325
329
|
return None
|
326
330
|
cycle = self.get_cycle(node)
|
327
331
|
length = len(cycle)
|
@@ -383,14 +387,29 @@ class lineageTree(lineageTreeLoaders):
|
|
383
387
|
if time_resolution is not None:
|
384
388
|
self._time_resolution = int(time_resolution * 10)
|
385
389
|
else:
|
386
|
-
warnings.warn("Time resolution set to default
|
390
|
+
warnings.warn("Time resolution set to default 0", stacklevel=2)
|
387
391
|
self._time_resolution = 10
|
388
392
|
|
393
|
+
@property
|
394
|
+
def depth(self):
|
395
|
+
if not hasattr(self, "_depth"):
|
396
|
+
self._depth = {}
|
397
|
+
for leaf in self.leaves:
|
398
|
+
self._depth[leaf] = 1
|
399
|
+
while leaf in self.predecessor:
|
400
|
+
parent = self.predecessor[leaf][0]
|
401
|
+
current_depth = self._depth.get(parent, 0)
|
402
|
+
self._depth[parent] = max(
|
403
|
+
self._depth[leaf] + 1, current_depth
|
404
|
+
)
|
405
|
+
leaf = parent
|
406
|
+
for root in self.roots - set(self._depth):
|
407
|
+
self._depth[root] = 1
|
408
|
+
return self._depth
|
409
|
+
|
389
410
|
@property
|
390
411
|
def roots(self):
|
391
|
-
|
392
|
-
self._roots = set(self.nodes).difference(self.predecessor)
|
393
|
-
return self._roots
|
412
|
+
return set(self.nodes).difference(self.predecessor)
|
394
413
|
|
395
414
|
@property
|
396
415
|
def edges(self):
|
@@ -398,7 +417,7 @@ class lineageTree(lineageTreeLoaders):
|
|
398
417
|
|
399
418
|
@property
|
400
419
|
def leaves(self):
|
401
|
-
return
|
420
|
+
return {p for p, s in self.successor.items() if s == []}
|
402
421
|
|
403
422
|
@property
|
404
423
|
def labels(self):
|
@@ -409,10 +428,10 @@ class lineageTree(lineageTreeLoaders):
|
|
409
428
|
}
|
410
429
|
else:
|
411
430
|
self._labels = {
|
412
|
-
|
413
|
-
for
|
414
|
-
for
|
415
|
-
if abs(self.time[
|
431
|
+
root: "Unlabeled"
|
432
|
+
for root in self.roots
|
433
|
+
for leaf in self.find_leaves(root)
|
434
|
+
if abs(self.time[leaf] - self.time[root])
|
416
435
|
>= abs(self.t_e - self.t_b) / 4
|
417
436
|
}
|
418
437
|
return self._labels
|
@@ -504,7 +523,7 @@ class lineageTree(lineageTreeLoaders):
|
|
504
523
|
|
505
524
|
f.write("@5\n")
|
506
525
|
for C in self.to_take_time[t]:
|
507
|
-
f.write(
|
526
|
+
f.write(f"{manual_labels.get(C, default_label):f}\n")
|
508
527
|
f.write(f"{0:f}\n")
|
509
528
|
|
510
529
|
f.write("@6\n")
|
@@ -523,8 +542,7 @@ class lineageTree(lineageTreeLoaders):
|
|
523
542
|
f.write("@7\n")
|
524
543
|
for C in self.to_take_time[t]:
|
525
544
|
f.write(
|
526
|
-
"
|
527
|
-
% (np.linalg.norm(points_v[C][0] - points_v[C][-1]))
|
545
|
+
f"{np.linalg.norm(points_v[C][0] - points_v[C][-1]):f}\n"
|
528
546
|
)
|
529
547
|
|
530
548
|
f.write("@8\n")
|
@@ -953,10 +971,10 @@ class lineageTree(lineageTreeLoaders):
|
|
953
971
|
if node_properties:
|
954
972
|
for p_name, (p_dict, default) in node_properties.items():
|
955
973
|
if isinstance(list(p_dict.values())[0], str):
|
956
|
-
f.write('(property 0 string "
|
974
|
+
f.write(f'(property 0 string "{p_name}"\n')
|
957
975
|
f.write(f"\t(default {default} {default})\n")
|
958
976
|
elif isinstance(list(p_dict.values())[0], Number):
|
959
|
-
f.write('(property 0 double "
|
977
|
+
f.write(f'(property 0 double "{p_name}"\n')
|
960
978
|
f.write('\t(default "0" "0")\n')
|
961
979
|
for n in nodes_to_use:
|
962
980
|
f.write(
|
@@ -1030,148 +1048,6 @@ class lineageTree(lineageTreeLoaders):
|
|
1030
1048
|
|
1031
1049
|
f.close()
|
1032
1050
|
|
1033
|
-
def read_from_binary(self, fname: str):
|
1034
|
-
"""
|
1035
|
-
Reads a binary lineageTree file name.
|
1036
|
-
Format description: see self.to_binary
|
1037
|
-
|
1038
|
-
Args:
|
1039
|
-
fname: string, path to the binary file
|
1040
|
-
reverse_time: bool, not used
|
1041
|
-
"""
|
1042
|
-
q_size = struct.calcsize("q")
|
1043
|
-
H_size = struct.calcsize("H")
|
1044
|
-
d_size = struct.calcsize("d")
|
1045
|
-
|
1046
|
-
with open(fname, "rb") as f:
|
1047
|
-
len_tree = struct.unpack("q", f.read(q_size))[0]
|
1048
|
-
len_time = struct.unpack("q", f.read(q_size))[0]
|
1049
|
-
len_pos = struct.unpack("q", f.read(q_size))[0]
|
1050
|
-
number_sequence = list(
|
1051
|
-
struct.unpack("q" * len_tree, f.read(q_size * len_tree))
|
1052
|
-
)
|
1053
|
-
time_sequence = list(
|
1054
|
-
struct.unpack("H" * len_time, f.read(H_size * len_time))
|
1055
|
-
)
|
1056
|
-
pos_sequence = np.array(
|
1057
|
-
struct.unpack("d" * len_pos, f.read(d_size * len_pos))
|
1058
|
-
)
|
1059
|
-
|
1060
|
-
f.close()
|
1061
|
-
|
1062
|
-
successor = {}
|
1063
|
-
predecessor = {}
|
1064
|
-
time = {}
|
1065
|
-
time_nodes = {}
|
1066
|
-
time_edges = {}
|
1067
|
-
pos = {}
|
1068
|
-
is_root = {}
|
1069
|
-
nodes = []
|
1070
|
-
edges = []
|
1071
|
-
waiting_list = []
|
1072
|
-
print(number_sequence[0])
|
1073
|
-
i = 0
|
1074
|
-
done = False
|
1075
|
-
if max(number_sequence[::2]) == -1:
|
1076
|
-
tmp = number_sequence[1::2]
|
1077
|
-
if len(tmp) * 3 == len(pos_sequence) == len(time_sequence) * 3:
|
1078
|
-
time = dict(list(zip(tmp, time_sequence)))
|
1079
|
-
for c, t in time.items():
|
1080
|
-
time_nodes.setdefault(t, set()).add(c)
|
1081
|
-
pos = dict(
|
1082
|
-
list(zip(tmp, np.reshape(pos_sequence, (len_time, 3))))
|
1083
|
-
)
|
1084
|
-
is_root = {c: True for c in tmp}
|
1085
|
-
nodes = tmp
|
1086
|
-
done = True
|
1087
|
-
while (
|
1088
|
-
i < len(number_sequence) and not done
|
1089
|
-
): # , c in enumerate(number_sequence[:-1]):
|
1090
|
-
c = number_sequence[i]
|
1091
|
-
if c == -1:
|
1092
|
-
if waiting_list != []:
|
1093
|
-
prev_mother = waiting_list.pop()
|
1094
|
-
successor[prev_mother].insert(0, number_sequence[i + 1])
|
1095
|
-
edges.append((prev_mother, number_sequence[i + 1]))
|
1096
|
-
time_edges.setdefault(t, set()).add(
|
1097
|
-
(prev_mother, number_sequence[i + 1])
|
1098
|
-
)
|
1099
|
-
is_root[number_sequence[i + 1]] = False
|
1100
|
-
t = time[prev_mother] + 1
|
1101
|
-
else:
|
1102
|
-
t = time_sequence.pop(0)
|
1103
|
-
is_root[number_sequence[i + 1]] = True
|
1104
|
-
|
1105
|
-
elif c == -2:
|
1106
|
-
successor[waiting_list[-1]] = [number_sequence[i + 1]]
|
1107
|
-
edges.append((waiting_list[-1], number_sequence[i + 1]))
|
1108
|
-
time_edges.setdefault(t, set()).add(
|
1109
|
-
(waiting_list[-1], number_sequence[i + 1])
|
1110
|
-
)
|
1111
|
-
is_root[number_sequence[i + 1]] = False
|
1112
|
-
pos[waiting_list[-1]] = pos_sequence[:3]
|
1113
|
-
pos_sequence = pos_sequence[3:]
|
1114
|
-
nodes.append(waiting_list[-1])
|
1115
|
-
time[waiting_list[-1]] = t
|
1116
|
-
time_nodes.setdefault(t, set()).add(waiting_list[-1])
|
1117
|
-
t += 1
|
1118
|
-
|
1119
|
-
elif number_sequence[i + 1] >= 0:
|
1120
|
-
successor[c] = [number_sequence[i + 1]]
|
1121
|
-
edges.append((c, number_sequence[i + 1]))
|
1122
|
-
time_edges.setdefault(t, set()).add(
|
1123
|
-
(c, number_sequence[i + 1])
|
1124
|
-
)
|
1125
|
-
is_root[number_sequence[i + 1]] = False
|
1126
|
-
pos[c] = pos_sequence[:3]
|
1127
|
-
pos_sequence = pos_sequence[3:]
|
1128
|
-
nodes.append(c)
|
1129
|
-
time[c] = t
|
1130
|
-
time_nodes.setdefault(t, set()).add(c)
|
1131
|
-
t += 1
|
1132
|
-
|
1133
|
-
elif number_sequence[i + 1] == -2:
|
1134
|
-
waiting_list += [c]
|
1135
|
-
|
1136
|
-
elif number_sequence[i + 1] == -1:
|
1137
|
-
pos[c] = pos_sequence[:3]
|
1138
|
-
pos_sequence = pos_sequence[3:]
|
1139
|
-
nodes.append(c)
|
1140
|
-
time[c] = t
|
1141
|
-
time_nodes.setdefault(t, set()).add(c)
|
1142
|
-
t += 1
|
1143
|
-
i += 1
|
1144
|
-
if waiting_list != []:
|
1145
|
-
prev_mother = waiting_list.pop()
|
1146
|
-
successor[prev_mother].insert(0, number_sequence[i + 1])
|
1147
|
-
edges.append((prev_mother, number_sequence[i + 1]))
|
1148
|
-
time_edges.setdefault(t, set()).add(
|
1149
|
-
(prev_mother, number_sequence[i + 1])
|
1150
|
-
)
|
1151
|
-
if i + 1 < len(number_sequence):
|
1152
|
-
is_root[number_sequence[i + 1]] = False
|
1153
|
-
t = time[prev_mother] + 1
|
1154
|
-
else:
|
1155
|
-
if len(time_sequence) > 0:
|
1156
|
-
t = time_sequence.pop(0)
|
1157
|
-
if i + 1 < len(number_sequence):
|
1158
|
-
is_root[number_sequence[i + 1]] = True
|
1159
|
-
i += 1
|
1160
|
-
|
1161
|
-
predecessor = {vi: [k] for k, v in successor.items() for vi in v}
|
1162
|
-
|
1163
|
-
self.successor = successor
|
1164
|
-
self.predecessor = predecessor
|
1165
|
-
self.time = time
|
1166
|
-
self.time_nodes = time_nodes
|
1167
|
-
self.time_edges = time_edges
|
1168
|
-
self.pos = pos
|
1169
|
-
self.nodes = set(nodes)
|
1170
|
-
self.t_b = min(time_nodes.keys())
|
1171
|
-
self.t_e = max(time_nodes.keys())
|
1172
|
-
self.is_root = is_root
|
1173
|
-
self.max_id = max(self.nodes)
|
1174
|
-
|
1175
1051
|
def write(self, fname: str):
|
1176
1052
|
"""
|
1177
1053
|
Write a lineage tree on disk as an .lT file.
|
@@ -1186,7 +1062,7 @@ class lineageTree(lineageTreeLoaders):
|
|
1186
1062
|
f.close()
|
1187
1063
|
|
1188
1064
|
@classmethod
|
1189
|
-
def load(clf, fname: str, rm_empty_lists=
|
1065
|
+
def load(clf, fname: str, rm_empty_lists=False):
|
1190
1066
|
"""
|
1191
1067
|
Loading a lineage tree from a ".lT" file.
|
1192
1068
|
|
@@ -1201,18 +1077,6 @@ class lineageTree(lineageTreeLoaders):
|
|
1201
1077
|
f.close()
|
1202
1078
|
if not hasattr(lT, "time_resolution"):
|
1203
1079
|
lT.time_resolution = None
|
1204
|
-
if rm_empty_lists:
|
1205
|
-
if [] in lT.successor.values():
|
1206
|
-
for node, succ in lT.successor.items():
|
1207
|
-
if succ == []:
|
1208
|
-
lT.successor.pop(node)
|
1209
|
-
if [] in lT.predecessor.values():
|
1210
|
-
for node, succ in lT.predecessor.items():
|
1211
|
-
if succ == []:
|
1212
|
-
lT.predecessor.pop(node)
|
1213
|
-
lT.t_e = max(lT.time_nodes)
|
1214
|
-
lT.t_b = min(lT.time_nodes)
|
1215
|
-
warnings.warn("Empty lists have been removed")
|
1216
1080
|
return lT
|
1217
1081
|
|
1218
1082
|
def get_idx3d(self, t: int) -> tuple:
|
@@ -1474,11 +1338,10 @@ class lineageTree(lineageTreeLoaders):
|
|
1474
1338
|
leaves = set()
|
1475
1339
|
while to_do:
|
1476
1340
|
curr = to_do.pop()
|
1477
|
-
succ = self.successor.get(curr)
|
1478
|
-
if
|
1341
|
+
succ = self.successor.get(curr, [])
|
1342
|
+
if not succ:
|
1479
1343
|
leaves.add(curr)
|
1480
|
-
|
1481
|
-
to_do += succ
|
1344
|
+
to_do += succ
|
1482
1345
|
return leaves
|
1483
1346
|
|
1484
1347
|
def get_sub_tree(
|
@@ -1883,7 +1746,6 @@ class lineageTree(lineageTreeLoaders):
|
|
1883
1746
|
color_of_edges=None,
|
1884
1747
|
size=10,
|
1885
1748
|
ax=None,
|
1886
|
-
figure=None,
|
1887
1749
|
default_color="black",
|
1888
1750
|
**kwargs,
|
1889
1751
|
):
|
@@ -1940,7 +1802,7 @@ class lineageTree(lineageTreeLoaders):
|
|
1940
1802
|
)
|
1941
1803
|
ax.get_yaxis().set_visible(False)
|
1942
1804
|
ax.get_xaxis().set_visible(False)
|
1943
|
-
return
|
1805
|
+
return ax.get_figure(), ax
|
1944
1806
|
|
1945
1807
|
def to_simple_graph(self, node=None, start_time: int = None):
|
1946
1808
|
"""Generates a dictionary of graphs where the keys are the index of the graph and
|
@@ -1975,8 +1837,8 @@ class lineageTree(lineageTreeLoaders):
|
|
1975
1837
|
figsize=(10, 15),
|
1976
1838
|
dpi=100,
|
1977
1839
|
fontsize=15,
|
1978
|
-
figure=None,
|
1979
1840
|
axes=None,
|
1841
|
+
vert_gap=1,
|
1980
1842
|
**kwargs,
|
1981
1843
|
):
|
1982
1844
|
"""Plots all lineages.
|
@@ -2006,14 +1868,24 @@ class lineageTree(lineageTreeLoaders):
|
|
2006
1868
|
)
|
2007
1869
|
pos = {
|
2008
1870
|
i: hierarchical_pos(
|
2009
|
-
g,
|
1871
|
+
g,
|
1872
|
+
g["root"],
|
1873
|
+
ycenter=-int(self.time[g["root"]]),
|
1874
|
+
vert_gap=vert_gap,
|
2010
1875
|
)
|
2011
1876
|
for i, g in graphs.items()
|
2012
1877
|
}
|
2013
|
-
|
2014
|
-
|
2015
|
-
|
2016
|
-
|
1878
|
+
if axes is None:
|
1879
|
+
ncols = int(len(graphs) // nrows) + (+np.sign(len(graphs) % nrows))
|
1880
|
+
figure, axes = plt.subplots(
|
1881
|
+
figsize=figsize, nrows=nrows, ncols=ncols, dpi=dpi, sharey=True
|
1882
|
+
)
|
1883
|
+
else:
|
1884
|
+
figure, axes = axes.flatten()[0].get_figure(), axes
|
1885
|
+
if len(axes.flatten()) < len(graphs):
|
1886
|
+
raise Exception(
|
1887
|
+
f"Not enough axes, they should be at least {len(graphs)}."
|
1888
|
+
)
|
2017
1889
|
flat_axes = axes.flatten()
|
2018
1890
|
ax2root = {}
|
2019
1891
|
min_width, min_height = float("inf"), float("inf")
|
@@ -2051,9 +1923,17 @@ class lineageTree(lineageTreeLoaders):
|
|
2051
1923
|
},
|
2052
1924
|
)
|
2053
1925
|
[figure.delaxes(ax) for ax in axes.flatten() if not ax.has_data()]
|
2054
|
-
return
|
1926
|
+
return axes.flatten()[0].get_figure(), axes, ax2root
|
2055
1927
|
|
2056
|
-
def plot_node(
|
1928
|
+
def plot_node(
|
1929
|
+
self,
|
1930
|
+
node,
|
1931
|
+
figsize=(4, 7),
|
1932
|
+
dpi=150,
|
1933
|
+
vert_gap=2,
|
1934
|
+
ax=None,
|
1935
|
+
**kwargs,
|
1936
|
+
):
|
2057
1937
|
"""Plots the subtree spawn by a node.
|
2058
1938
|
|
2059
1939
|
Args:
|
@@ -2064,7 +1944,10 @@ class lineageTree(lineageTreeLoaders):
|
|
2064
1944
|
if len(graph) > 1:
|
2065
1945
|
raise Warning("Please enter only one node")
|
2066
1946
|
graph = graph[0]
|
2067
|
-
|
1947
|
+
if not ax:
|
1948
|
+
figure, ax = plt.subplots(
|
1949
|
+
nrows=1, ncols=1, figsize=figsize, dpi=dpi
|
1950
|
+
)
|
2068
1951
|
self.draw_tree_graph(
|
2069
1952
|
hier=hierarchical_pos(
|
2070
1953
|
graph,
|
@@ -2074,9 +1957,8 @@ class lineageTree(lineageTreeLoaders):
|
|
2074
1957
|
),
|
2075
1958
|
lnks_tms=graph,
|
2076
1959
|
ax=ax,
|
2077
|
-
**kwargs,
|
2078
1960
|
)
|
2079
|
-
return
|
1961
|
+
return ax.get_figure(), ax
|
2080
1962
|
|
2081
1963
|
# def DTW(self, t1, t2, max_w=None, start_delay=None, end_delay=None,
|
2082
1964
|
# metric='euclidian', **kwargs):
|
@@ -2729,6 +2611,7 @@ class lineageTree(lineageTreeLoaders):
|
|
2729
2611
|
warnings.warn(
|
2730
2612
|
"Error: not possible to show alignment in PCA projection !",
|
2731
2613
|
UserWarning,
|
2614
|
+
stacklevel=2,
|
2732
2615
|
)
|
2733
2616
|
|
2734
2617
|
return distance, fig
|
@@ -2824,6 +2707,8 @@ class lineageTree(lineageTreeLoaders):
|
|
2824
2707
|
self.read_from_ASTEC(file_format, eigen)
|
2825
2708
|
elif file_type == "csv":
|
2826
2709
|
self.read_from_csv(file_format, z_mult, link=1, delim=delim)
|
2710
|
+
elif file_type == "bao":
|
2711
|
+
self.read_C_elegans_bao(file_format)
|
2827
2712
|
elif file_format and file_format.endswith(".lT"):
|
2828
2713
|
with open(file_format, "br") as f:
|
2829
2714
|
tmp = pkl.load(f)
|
@@ -2834,15 +2719,5 @@ class lineageTree(lineageTreeLoaders):
|
|
2834
2719
|
if self.name is None:
|
2835
2720
|
try:
|
2836
2721
|
self.name = Path(file_format).stem
|
2837
|
-
except:
|
2722
|
+
except TypeError:
|
2838
2723
|
self.name = Path(file_format[0]).stem
|
2839
|
-
if [] in self.successor.values():
|
2840
|
-
successors = list(self.successor.keys())
|
2841
|
-
for succ in successors:
|
2842
|
-
if self[succ] == []:
|
2843
|
-
self.successor.pop(succ)
|
2844
|
-
if [] in self.predecessor.values():
|
2845
|
-
predecessors = list(self.predecessor.keys())
|
2846
|
-
for succ in predecessors:
|
2847
|
-
if self[succ] == []:
|
2848
|
-
self.predecessor.pop(succ)
|
@@ -2,6 +2,7 @@ import os
|
|
2
2
|
import pickle as pkl
|
3
3
|
import warnings
|
4
4
|
from functools import partial
|
5
|
+
|
5
6
|
import numpy as np
|
6
7
|
|
7
8
|
try:
|
@@ -21,21 +22,19 @@ class lineageTreeManager:
|
|
21
22
|
self.lineagetrees = {}
|
22
23
|
self.lineageTree_counter = 0
|
23
24
|
self.registered = {}
|
24
|
-
self.greatest_common_divisors = {}
|
25
25
|
|
26
26
|
def __next__(self):
|
27
27
|
self.lineageTree_counter += 1
|
28
28
|
return self.lineageTree_counter - 1
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
)
|
30
|
+
@property
|
31
|
+
def gcd(self):
|
32
|
+
if len(self.lineagetrees) >= 1:
|
33
|
+
all_time_res = [
|
34
|
+
embryo._time_resolution
|
35
|
+
for embryo in self.lineagetrees.values()
|
36
|
+
]
|
37
|
+
return np.gcd.reduce(all_time_res)
|
39
38
|
|
40
39
|
def add(
|
41
40
|
self, other_tree: lineageTree, name: str = "", classification: str = ""
|
@@ -64,7 +63,6 @@ class lineageTreeManager:
|
|
64
63
|
name = f"Lineagetree {next(self)}"
|
65
64
|
self.lineagetrees[name] = other_tree
|
66
65
|
self.lineagetrees[name].name = name
|
67
|
-
# self.greatest_common_divisors[name] = gcd
|
68
66
|
else:
|
69
67
|
raise Exception(
|
70
68
|
"Please add a LineageTree object or add time resolution to the LineageTree added."
|
@@ -120,7 +118,7 @@ class lineageTreeManager:
|
|
120
118
|
n2: int,
|
121
119
|
embryo_2,
|
122
120
|
end_time2: int,
|
123
|
-
style="
|
121
|
+
style="simple",
|
124
122
|
downsample: int = 2,
|
125
123
|
registration=None, # will be added as a later feature
|
126
124
|
):
|
@@ -141,21 +139,48 @@ class lineageTreeManager:
|
|
141
139
|
"""
|
142
140
|
|
143
141
|
tree = tree_style[style].value
|
142
|
+
lcm = (
|
143
|
+
self.lineagetrees[embryo_1]._time_resolution
|
144
|
+
* self.lineagetrees[embryo_2]._time_resolution
|
145
|
+
) / self.gcd
|
146
|
+
if style == "downsampled":
|
147
|
+
if downsample % (lcm / 10) != 0:
|
148
|
+
raise Exception(
|
149
|
+
f"Use a valid downsampling rate (multiple of {lcm/10})"
|
150
|
+
)
|
151
|
+
time_res = [
|
152
|
+
downsample / self.lineagetrees[embryo_2].time_resolution,
|
153
|
+
downsample / self.lineagetrees[embryo_1].time_resolution,
|
154
|
+
]
|
155
|
+
elif style == "full":
|
156
|
+
time_res = [
|
157
|
+
lcm / 10 / self.lineagetrees[embryo_2].time_resolution,
|
158
|
+
lcm / 10 / self.lineagetrees[embryo_1].time_resolution,
|
159
|
+
]
|
160
|
+
else:
|
161
|
+
time_res = [
|
162
|
+
self.lineagetrees[embryo_1]._time_resolution,
|
163
|
+
self.lineagetrees[embryo_2]._time_resolution,
|
164
|
+
]
|
165
|
+
time_res = [i / self.gcd for i in time_res]
|
144
166
|
tree1 = tree(
|
145
167
|
lT=self.lineagetrees[embryo_1],
|
146
168
|
downsample=downsample,
|
147
169
|
end_time=end_time1,
|
148
170
|
root=n1,
|
171
|
+
time_scale=time_res[0],
|
149
172
|
)
|
150
173
|
tree2 = tree(
|
151
174
|
lT=self.lineagetrees[embryo_2],
|
152
175
|
downsample=downsample,
|
153
176
|
end_time=end_time2,
|
154
177
|
root=n2,
|
178
|
+
time_scale=time_res[1],
|
155
179
|
)
|
156
180
|
delta = tree1.delta
|
157
181
|
_, times1 = tree1.tree
|
158
182
|
_, times2 = tree2.tree
|
183
|
+
|
159
184
|
nodes1, adj1, corres1 = tree1.edist
|
160
185
|
nodes2, adj2, corres2 = tree2.edist
|
161
186
|
if len(nodes1) == len(nodes2) == 0:
|
LineageTree/loaders.py
CHANGED
@@ -1,12 +1,28 @@
|
|
1
1
|
import csv
|
2
2
|
import os
|
3
3
|
import pickle as pkl
|
4
|
+
import struct
|
4
5
|
import xml.etree.ElementTree as ET
|
6
|
+
from warnings import warn
|
5
7
|
|
6
8
|
import numpy as np
|
7
9
|
|
8
10
|
|
9
11
|
class lineageTreeLoaders:
|
12
|
+
implicit_l_t = {
|
13
|
+
"AB": "P0",
|
14
|
+
"P1": "P0",
|
15
|
+
"EMS": "P1",
|
16
|
+
"P2": "P1",
|
17
|
+
"MS": "EMS",
|
18
|
+
"E": "EMS",
|
19
|
+
"C": "P2",
|
20
|
+
"P3": "P2",
|
21
|
+
"D": "P3",
|
22
|
+
"P4": "P3",
|
23
|
+
"Z2": "P4",
|
24
|
+
"Z3": "P4",
|
25
|
+
}
|
10
26
|
|
11
27
|
def read_from_csv(
|
12
28
|
self, file_path: str, z_mult: float, link: int = 1, delim: str = ","
|
@@ -65,7 +81,6 @@ class lineageTreeLoaders:
|
|
65
81
|
self.pos[C] = pos
|
66
82
|
self.nodes.add(C)
|
67
83
|
self.time_nodes.setdefault(t, set()).add(C)
|
68
|
-
# self.time_id[(t, cell_id)] = C
|
69
84
|
self.time[C] = t
|
70
85
|
if not link:
|
71
86
|
self.displacement[C] = np.array([dx, dy, dz * z_mult])
|
@@ -314,6 +329,147 @@ class lineageTreeLoaders:
|
|
314
329
|
new_dict[k] = v
|
315
330
|
return new_dict
|
316
331
|
|
332
|
+
def read_from_binary(self, fname: str):
|
333
|
+
"""
|
334
|
+
Reads a binary lineageTree file name.
|
335
|
+
Format description: see self.to_binary
|
336
|
+
|
337
|
+
Args:
|
338
|
+
fname: string, path to the binary file
|
339
|
+
reverse_time: bool, not used
|
340
|
+
"""
|
341
|
+
q_size = struct.calcsize("q")
|
342
|
+
H_size = struct.calcsize("H")
|
343
|
+
d_size = struct.calcsize("d")
|
344
|
+
|
345
|
+
with open(fname, "rb") as f:
|
346
|
+
len_tree = struct.unpack("q", f.read(q_size))[0]
|
347
|
+
len_time = struct.unpack("q", f.read(q_size))[0]
|
348
|
+
len_pos = struct.unpack("q", f.read(q_size))[0]
|
349
|
+
number_sequence = list(
|
350
|
+
struct.unpack("q" * len_tree, f.read(q_size * len_tree))
|
351
|
+
)
|
352
|
+
time_sequence = list(
|
353
|
+
struct.unpack("H" * len_time, f.read(H_size * len_time))
|
354
|
+
)
|
355
|
+
pos_sequence = np.array(
|
356
|
+
struct.unpack("d" * len_pos, f.read(d_size * len_pos))
|
357
|
+
)
|
358
|
+
|
359
|
+
f.close()
|
360
|
+
|
361
|
+
successor = {}
|
362
|
+
predecessor = {}
|
363
|
+
time = {}
|
364
|
+
time_nodes = {}
|
365
|
+
time_edges = {}
|
366
|
+
pos = {}
|
367
|
+
is_root = {}
|
368
|
+
nodes = []
|
369
|
+
edges = []
|
370
|
+
waiting_list = []
|
371
|
+
i = 0
|
372
|
+
done = False
|
373
|
+
if max(number_sequence[::2]) == -1:
|
374
|
+
tmp = number_sequence[1::2]
|
375
|
+
if len(tmp) * 3 == len(pos_sequence) == len(time_sequence) * 3:
|
376
|
+
time = dict(list(zip(tmp, time_sequence)))
|
377
|
+
for c, t in time.items():
|
378
|
+
time_nodes.setdefault(t, set()).add(c)
|
379
|
+
pos = dict(
|
380
|
+
list(zip(tmp, np.reshape(pos_sequence, (len_time, 3))))
|
381
|
+
)
|
382
|
+
is_root = {c: True for c in tmp}
|
383
|
+
nodes = tmp
|
384
|
+
done = True
|
385
|
+
while (
|
386
|
+
i < len(number_sequence) and not done
|
387
|
+
): # , c in enumerate(number_sequence[:-1]):
|
388
|
+
c = number_sequence[i]
|
389
|
+
if c == -1:
|
390
|
+
if waiting_list != []:
|
391
|
+
prev_mother = waiting_list.pop()
|
392
|
+
successor[prev_mother].insert(0, number_sequence[i + 1])
|
393
|
+
edges.append((prev_mother, number_sequence[i + 1]))
|
394
|
+
time_edges.setdefault(t, set()).add(
|
395
|
+
(prev_mother, number_sequence[i + 1])
|
396
|
+
)
|
397
|
+
is_root[number_sequence[i + 1]] = False
|
398
|
+
t = time[prev_mother] + 1
|
399
|
+
else:
|
400
|
+
t = time_sequence.pop(0)
|
401
|
+
is_root[number_sequence[i + 1]] = True
|
402
|
+
|
403
|
+
elif c == -2:
|
404
|
+
successor[waiting_list[-1]] = [number_sequence[i + 1]]
|
405
|
+
edges.append((waiting_list[-1], number_sequence[i + 1]))
|
406
|
+
time_edges.setdefault(t, set()).add(
|
407
|
+
(waiting_list[-1], number_sequence[i + 1])
|
408
|
+
)
|
409
|
+
is_root[number_sequence[i + 1]] = False
|
410
|
+
pos[waiting_list[-1]] = pos_sequence[:3]
|
411
|
+
pos_sequence = pos_sequence[3:]
|
412
|
+
nodes.append(waiting_list[-1])
|
413
|
+
time[waiting_list[-1]] = t
|
414
|
+
time_nodes.setdefault(t, set()).add(waiting_list[-1])
|
415
|
+
t += 1
|
416
|
+
|
417
|
+
elif number_sequence[i + 1] >= 0:
|
418
|
+
successor[c] = [number_sequence[i + 1]]
|
419
|
+
edges.append((c, number_sequence[i + 1]))
|
420
|
+
time_edges.setdefault(t, set()).add(
|
421
|
+
(c, number_sequence[i + 1])
|
422
|
+
)
|
423
|
+
is_root[number_sequence[i + 1]] = False
|
424
|
+
pos[c] = pos_sequence[:3]
|
425
|
+
pos_sequence = pos_sequence[3:]
|
426
|
+
nodes.append(c)
|
427
|
+
time[c] = t
|
428
|
+
time_nodes.setdefault(t, set()).add(c)
|
429
|
+
t += 1
|
430
|
+
|
431
|
+
elif number_sequence[i + 1] == -2:
|
432
|
+
waiting_list += [c]
|
433
|
+
|
434
|
+
elif number_sequence[i + 1] == -1:
|
435
|
+
pos[c] = pos_sequence[:3]
|
436
|
+
pos_sequence = pos_sequence[3:]
|
437
|
+
nodes.append(c)
|
438
|
+
time[c] = t
|
439
|
+
time_nodes.setdefault(t, set()).add(c)
|
440
|
+
t += 1
|
441
|
+
i += 1
|
442
|
+
if waiting_list != []:
|
443
|
+
prev_mother = waiting_list.pop()
|
444
|
+
successor[prev_mother].insert(0, number_sequence[i + 1])
|
445
|
+
edges.append((prev_mother, number_sequence[i + 1]))
|
446
|
+
time_edges.setdefault(t, set()).add(
|
447
|
+
(prev_mother, number_sequence[i + 1])
|
448
|
+
)
|
449
|
+
if i + 1 < len(number_sequence):
|
450
|
+
is_root[number_sequence[i + 1]] = False
|
451
|
+
t = time[prev_mother] + 1
|
452
|
+
else:
|
453
|
+
if len(time_sequence) > 0:
|
454
|
+
t = time_sequence.pop(0)
|
455
|
+
if i + 1 < len(number_sequence):
|
456
|
+
is_root[number_sequence[i + 1]] = True
|
457
|
+
i += 1
|
458
|
+
|
459
|
+
predecessor = {vi: [k] for k, v in successor.items() for vi in v}
|
460
|
+
|
461
|
+
self.successor = successor
|
462
|
+
self.predecessor = predecessor
|
463
|
+
self.time = time
|
464
|
+
self.time_nodes = time_nodes
|
465
|
+
self.time_edges = time_edges
|
466
|
+
self.pos = pos
|
467
|
+
self.nodes = set(nodes)
|
468
|
+
self.t_b = min(time_nodes)
|
469
|
+
self.t_e = max(time_nodes)
|
470
|
+
self.is_root = is_root
|
471
|
+
self.max_id = max(self.nodes)
|
472
|
+
|
317
473
|
def read_from_txt_for_celegans(self, file: str):
|
318
474
|
"""
|
319
475
|
Read a C. elegans lineage tree
|
@@ -321,20 +477,6 @@ class lineageTreeLoaders:
|
|
321
477
|
Args:
|
322
478
|
file (str): Path to the file to read
|
323
479
|
"""
|
324
|
-
implicit_l_t = {
|
325
|
-
"AB": "P0",
|
326
|
-
"P1": "P0",
|
327
|
-
"EMS": "P1",
|
328
|
-
"P2": "P1",
|
329
|
-
"MS": "EMS",
|
330
|
-
"E": "EMS",
|
331
|
-
"C": "P2",
|
332
|
-
"P3": "P2",
|
333
|
-
"D": "P3",
|
334
|
-
"P4": "P3",
|
335
|
-
"Z2": "P4",
|
336
|
-
"Z3": "P4",
|
337
|
-
}
|
338
480
|
with open(file) as f:
|
339
481
|
raw = f.readlines()[1:]
|
340
482
|
f.close()
|
@@ -363,12 +505,9 @@ class lineageTreeLoaders:
|
|
363
505
|
p = name_to_id[self.name[c]]
|
364
506
|
elif self.name[c][:-1] in name_to_id:
|
365
507
|
p = name_to_id[self.name[c][:-1]]
|
366
|
-
elif implicit_l_t.get(self.name[c]) in name_to_id:
|
367
|
-
p = name_to_id[implicit_l_t.get(self.name[c])]
|
508
|
+
elif self.implicit_l_t.get(self.name[c]) in name_to_id:
|
509
|
+
p = name_to_id[self.implicit_l_t.get(self.name[c])]
|
368
510
|
else:
|
369
|
-
print(
|
370
|
-
"error, cell %s has no predecessors" % self.name[c]
|
371
|
-
)
|
372
511
|
p = None
|
373
512
|
self.predecessor.setdefault(c, []).append(p)
|
374
513
|
self.successor.setdefault(p, []).append(c)
|
@@ -389,21 +528,6 @@ class lineageTreeLoaders:
|
|
389
528
|
file (str): Path to the file to read
|
390
529
|
"""
|
391
530
|
|
392
|
-
implicit_l_t = {
|
393
|
-
"AB": "P0",
|
394
|
-
"P1": "P0",
|
395
|
-
"EMS": "P1",
|
396
|
-
"P2": "P1",
|
397
|
-
"MS": "EMS",
|
398
|
-
"E": "EMS",
|
399
|
-
"C": "P2",
|
400
|
-
"P3": "P2",
|
401
|
-
"D": "P3",
|
402
|
-
"P4": "P3",
|
403
|
-
"Z2": "P4",
|
404
|
-
"Z3": "P4",
|
405
|
-
}
|
406
|
-
|
407
531
|
def split_line(line):
|
408
532
|
return (
|
409
533
|
line.split()[0],
|
@@ -450,11 +574,12 @@ class lineageTreeLoaders:
|
|
450
574
|
p = name_to_id[self.name[c]]
|
451
575
|
elif self.name[c][:-1] in name_to_id:
|
452
576
|
p = name_to_id[self.name[c][:-1]]
|
453
|
-
elif implicit_l_t.get(self.name[c]) in name_to_id:
|
454
|
-
p = name_to_id[implicit_l_t.get(self.name[c])]
|
577
|
+
elif self.implicit_l_t.get(self.name[c]) in name_to_id:
|
578
|
+
p = name_to_id[self.implicit_l_t.get(self.name[c])]
|
455
579
|
else:
|
456
|
-
|
457
|
-
"error, cell
|
580
|
+
warn(
|
581
|
+
f"error, cell {self.name[c]} has no predecessors",
|
582
|
+
stacklevel=2,
|
458
583
|
)
|
459
584
|
p = None
|
460
585
|
self.predecessor.setdefault(c, []).append(p)
|
@@ -496,9 +621,6 @@ class lineageTreeLoaders:
|
|
496
621
|
self.intensity = {}
|
497
622
|
self.W = {}
|
498
623
|
for t in range(tb, te + 1):
|
499
|
-
print(t, end=" ")
|
500
|
-
if t % 10 == 0:
|
501
|
-
print()
|
502
624
|
tree = ET.parse(file_format.format(t=t))
|
503
625
|
root = tree.getroot()
|
504
626
|
self.time_nodes[t] = set()
|
@@ -720,3 +842,55 @@ class lineageTreeLoaders:
|
|
720
842
|
tracks[t_id].append((s, t))
|
721
843
|
self.t_b = min(self.time_nodes.keys())
|
722
844
|
self.t_e = max(self.time_nodes.keys())
|
845
|
+
|
846
|
+
def read_C_elegans_bao(self, path):
|
847
|
+
cell_times = {}
|
848
|
+
self.expression = {}
|
849
|
+
with open(path) as f:
|
850
|
+
for line in f:
|
851
|
+
if "cell_name" not in line:
|
852
|
+
cell_times[line.split("\t")[0]] = list(
|
853
|
+
line.split("\t")[-1].split(",")
|
854
|
+
)
|
855
|
+
new_dict = {}
|
856
|
+
end_dict = {}
|
857
|
+
self.t_e = 0
|
858
|
+
self.t_b = 0
|
859
|
+
for c, lc in cell_times.items():
|
860
|
+
new_dict[c] = self.add_node(0)
|
861
|
+
tmp = self.add_branch(
|
862
|
+
new_dict[c],
|
863
|
+
length=len(lc) - 1,
|
864
|
+
reverse=True,
|
865
|
+
move_timepoints=True,
|
866
|
+
)
|
867
|
+
for i, node in enumerate(self.get_cycle(tmp)):
|
868
|
+
self.expression[node] = int(lc[i])
|
869
|
+
self._labels[self.get_cycle(tmp)[0]] = c
|
870
|
+
self._labels.pop(tmp)
|
871
|
+
end_dict[c] = self.get_cycle(new_dict[c])[-1]
|
872
|
+
cell_names = list(cell_times.keys())
|
873
|
+
c_to_p = {}
|
874
|
+
while cell_names:
|
875
|
+
cur = cell_names.pop()
|
876
|
+
if cur[:-1] in cell_names:
|
877
|
+
c_to_p[cur] = cur[:-1]
|
878
|
+
c_to_p.update(self.implicit_l_t)
|
879
|
+
for c, p in c_to_p.items():
|
880
|
+
if p in cell_times:
|
881
|
+
cyc = end_dict[p]
|
882
|
+
self.predecessor[new_dict[c]] = [cyc]
|
883
|
+
if cyc not in self.successor:
|
884
|
+
self.successor[cyc] = []
|
885
|
+
self.successor[cyc].append(new_dict[c])
|
886
|
+
self.time_nodes.clear()
|
887
|
+
for root in self.roots:
|
888
|
+
to_do = [root]
|
889
|
+
while to_do:
|
890
|
+
cur = to_do.pop()
|
891
|
+
self.time_nodes.setdefault(self.time[cur], set()).add(cur)
|
892
|
+
_next = self.successor.get(cur, [])
|
893
|
+
to_do += _next
|
894
|
+
for n in _next:
|
895
|
+
self.time[n] = self.time[cur] + 1
|
896
|
+
self.t_e = max(self.time.values())
|
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
|
|
@@ -23,13 +24,15 @@ class abstract_trees(ABC):
|
|
23
24
|
root: int,
|
24
25
|
downsample: int,
|
25
26
|
end_time: int = None,
|
26
|
-
time_scale: int =
|
27
|
+
time_scale: int = 1,
|
27
28
|
):
|
28
29
|
self.lT: lineageTree = lT
|
29
30
|
self.root: int = root
|
30
31
|
self.downsample: int = downsample
|
31
32
|
self.end_time: int = end_time if end_time else self.lT.t_e
|
32
|
-
self.time_scale: int = time_scale if time_scale
|
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)")
|
33
36
|
self.tree: tuple = self.get_tree()
|
34
37
|
self.edist = self._edist_format(self.tree[0])
|
35
38
|
|
@@ -211,6 +214,13 @@ class downsample_tree(abstract_trees):
|
|
211
214
|
|
212
215
|
def __init__(self, **kwargs):
|
213
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
|
+
)
|
214
224
|
|
215
225
|
def get_tree(self):
|
216
226
|
self.out_dict = {}
|
@@ -219,7 +229,8 @@ class downsample_tree(abstract_trees):
|
|
219
229
|
while to_do:
|
220
230
|
current = to_do.pop()
|
221
231
|
_next = self.lT.get_cells_at_t_from_root(
|
222
|
-
current,
|
232
|
+
current,
|
233
|
+
self.lT.time[current] + (self.downsample / self.time_scale),
|
223
234
|
)
|
224
235
|
if _next == [current]:
|
225
236
|
_next = None
|
@@ -228,11 +239,11 @@ class downsample_tree(abstract_trees):
|
|
228
239
|
to_do.extend(_next)
|
229
240
|
else:
|
230
241
|
self.out_dict[current] = []
|
231
|
-
self.times[current] = self.downsample
|
242
|
+
self.times[current] = 1 # self.downsample
|
232
243
|
return self.out_dict, self.times
|
233
244
|
|
234
245
|
def get_norm(self):
|
235
|
-
return
|
246
|
+
return len(self.times.values()) * self.downsample / self.time_scale
|
236
247
|
|
237
248
|
def delta(self, x, y, corres1, corres2, times1, times2):
|
238
249
|
if x is None and y is None:
|
@@ -283,22 +294,23 @@ class full_tree(abstract_trees):
|
|
283
294
|
_next = self.lT.successor.get(current, [])
|
284
295
|
if _next and self.lT.time[_next[0]] <= self.end_time:
|
285
296
|
if self.time_scale > 1:
|
286
|
-
for _ in range(self.time_scale):
|
297
|
+
for _ in range(self.time_scale - 1):
|
287
298
|
next_id = self.lT.get_next_id()
|
288
|
-
self.out_dict[current] = next_id
|
299
|
+
self.out_dict[current] = [next_id]
|
289
300
|
current = next_id
|
290
301
|
self.out_dict[current] = _next
|
291
302
|
to_do.extend(_next)
|
292
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
|
293
308
|
self.out_dict[current] = []
|
294
309
|
self.times[current] = 1
|
295
310
|
return self.out_dict, self.times
|
296
311
|
|
297
312
|
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
|
-
)
|
313
|
+
return len(self.times) * self.time_scale
|
302
314
|
|
303
315
|
def delta(self, x, y, corres1, corres2, times1, times2):
|
304
316
|
if x is None and y is None:
|
LineageTree/utils.py
CHANGED
@@ -7,7 +7,8 @@ try:
|
|
7
7
|
import motile
|
8
8
|
except ImportError:
|
9
9
|
warnings.warn(
|
10
|
-
"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,
|
11
12
|
)
|
12
13
|
|
13
14
|
|
@@ -16,16 +17,13 @@ def to_motile(
|
|
16
17
|
):
|
17
18
|
try:
|
18
19
|
import networkx as nx
|
19
|
-
except:
|
20
|
-
raise Warning("Please install networkx")
|
20
|
+
except ImportError:
|
21
|
+
raise Warning("Please install networkx") # noqa: B904
|
21
22
|
|
22
23
|
fmt = nx.DiGraph()
|
23
24
|
if not crop:
|
24
25
|
crop = lT.t_e
|
25
|
-
# time_nodes = [
|
26
26
|
for time in range(crop):
|
27
|
-
# time_nodes += lT.time_nodes[time]
|
28
|
-
# print(time_nodes)
|
29
27
|
for time_node in lT.time_nodes[time]:
|
30
28
|
fmt.add_node(
|
31
29
|
time_node,
|
@@ -33,8 +31,6 @@ def to_motile(
|
|
33
31
|
pos=lT.pos[time_node],
|
34
32
|
score=1,
|
35
33
|
)
|
36
|
-
# for suc in lT.successor:
|
37
|
-
# fmt.add_edge(time_node, suc, **{"score":0})
|
38
34
|
|
39
35
|
motile.add_cand_edges(fmt, max_dist, max_skip_frames=max_skip_frames)
|
40
36
|
|
@@ -135,7 +131,9 @@ def hierarchical_pos(
|
|
135
131
|
elif len(succ) == 1:
|
136
132
|
pos_node[succ[0]] = [
|
137
133
|
pos_node[curr][0],
|
138
|
-
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)),
|
139
137
|
]
|
140
138
|
to_do.extend(succ)
|
141
139
|
prev_width[succ[0]] = prev_width[curr]
|
@@ -1,6 +1,6 @@
|
|
1
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
|
@@ -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=gRnB16cDWVXa8GC6errEK0JxnllNypanG8HOJeO9mgo,155
|
2
|
-
LineageTree/lineageTree.py,sha256=WLpKDzlt0vDL4zMOuyEsWYiLgNJcX8qMORgD0wPwQJg,107536
|
3
|
-
LineageTree/lineageTreeManager.py,sha256=OFcW4DmUtsPQb6dw1jKy3EPezs4UCQ666-Ki3IYuzbc,5641
|
4
|
-
LineageTree/loaders.py,sha256=D5hE928OD2RUGwG8CBW-69V8tp-k-VyNCm4I9AmtK2A,25976
|
5
|
-
LineageTree/tree_styles.py,sha256=U4mkcAYCK1WjWT3rdP1Hc-KxNXqEnwgZy0HAxTsUNpE,10746
|
6
|
-
LineageTree/utils.py,sha256=nhOThtLVA29cvt9SwrC9LE7kf6DZMaBr0OC7_hZf3Rk,4947
|
7
|
-
LineageTree-1.7.0.dist-info/LICENSE,sha256=IKncNCNpq93Kq7Ywg1V4I9Bu_99VMK-mX1EJyjTKLyc,1068
|
8
|
-
LineageTree-1.7.0.dist-info/METADATA,sha256=rdZOPPbmNa5YRbjOhLpLJPEeDPuFR4eHcPfOiXyud18,4195
|
9
|
-
LineageTree-1.7.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
10
|
-
LineageTree-1.7.0.dist-info/top_level.txt,sha256=CCqPCTX76zPTEUagUgTWbLaZun8752n59iOOwfUlvxs,12
|
11
|
-
LineageTree-1.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|