LineageTree 1.0.2__tar.gz → 1.2.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.
- {LineageTree-1.0.2/src/LineageTree.egg-info → LineageTree-1.2.0}/PKG-INFO +20 -5
- {LineageTree-1.0.2 → LineageTree-1.2.0}/pyproject.toml +1 -4
- LineageTree-1.2.0/setup.cfg +57 -0
- {LineageTree-1.0.2 → LineageTree-1.2.0}/src/LineageTree/__init__.py +1 -1
- {LineageTree-1.0.2 → LineageTree-1.2.0}/src/LineageTree/lineageTree.py +257 -228
- {LineageTree-1.0.2 → LineageTree-1.2.0/src/LineageTree.egg-info}/PKG-INFO +20 -5
- {LineageTree-1.0.2 → LineageTree-1.2.0}/src/LineageTree.egg-info/SOURCES.txt +2 -1
- LineageTree-1.2.0/src/LineageTree.egg-info/entry_points.txt +2 -0
- {LineageTree-1.0.2 → LineageTree-1.2.0}/src/LineageTree.egg-info/requires.txt +1 -0
- LineageTree-1.0.2/setup.cfg +0 -4
- LineageTree-1.0.2/setup.py +0 -32
- {LineageTree-1.0.2 → LineageTree-1.2.0}/LICENSE +0 -0
- {LineageTree-1.0.2 → LineageTree-1.2.0}/README.md +0 -0
- {LineageTree-1.0.2 → LineageTree-1.2.0}/src/LineageTree.egg-info/dependency_links.txt +0 -0
- {LineageTree-1.0.2 → LineageTree-1.2.0}/src/LineageTree.egg-info/top_level.txt +0 -0
- {LineageTree-1.0.2 → LineageTree-1.2.0}/test/test_lineageTree.py +0 -0
@@ -1,13 +1,28 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: LineageTree
|
3
|
-
Version: 1.0
|
4
|
-
Summary: Lineage tree structure
|
5
|
-
Home-page: https://github.com/leoguignard/
|
6
|
-
Author:
|
7
|
-
|
3
|
+
Version: 1.2.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
|
14
|
+
Classifier: Framework :: napari
|
8
15
|
Classifier: Intended Audience :: Developers
|
9
16
|
Classifier: License :: OSI Approved :: MIT License
|
17
|
+
Classifier: Operating System :: OS Independent
|
18
|
+
Classifier: Programming Language :: Python
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
10
21
|
Classifier: Programming Language :: Python :: 3.8
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
25
|
+
Requires-Python: >=3.8
|
11
26
|
Description-Content-Type: text/markdown
|
12
27
|
Provides-Extra: svg
|
13
28
|
License-File: LICENSE
|
@@ -10,7 +10,7 @@ profile = "black"
|
|
10
10
|
line_length = 79
|
11
11
|
|
12
12
|
[tool.bumpver]
|
13
|
-
current_version = "1.0
|
13
|
+
current_version = "1.2.0"
|
14
14
|
version_pattern = "MAJOR.MINOR.PATCH[-TAG]"
|
15
15
|
commit_message = "bump version {old_version} -> {new_version}"
|
16
16
|
commit = true
|
@@ -23,7 +23,4 @@ push = false
|
|
23
23
|
]
|
24
24
|
"src/LineageTree/__init__.py" = [
|
25
25
|
'__version__ = "{version}"',
|
26
|
-
]
|
27
|
-
"setup.py" = [
|
28
|
-
'version="{version}"',
|
29
26
|
]
|
@@ -0,0 +1,57 @@
|
|
1
|
+
[metadata]
|
2
|
+
name = LineageTree
|
3
|
+
version = attr: LineageTree.__version__
|
4
|
+
description = Lineage tree structure
|
5
|
+
long_description = file: README.md
|
6
|
+
long_description_content_type = text/markdown
|
7
|
+
url = https://github.com/leoguignard/LineageTree
|
8
|
+
author = Léo Guignard
|
9
|
+
author_email = leo.guignard@univ-amu.fr
|
10
|
+
license = MIT
|
11
|
+
license_files = LICENSE
|
12
|
+
classifiers =
|
13
|
+
Development Status :: 2 - Pre-Alpha
|
14
|
+
Framework :: napari
|
15
|
+
Intended Audience :: Developers
|
16
|
+
License :: OSI Approved :: MIT License
|
17
|
+
Operating System :: OS Independent
|
18
|
+
Programming Language :: Python
|
19
|
+
Programming Language :: Python :: 3
|
20
|
+
Programming Language :: Python :: 3 :: Only
|
21
|
+
Programming Language :: Python :: 3.8
|
22
|
+
Programming Language :: Python :: 3.9
|
23
|
+
Programming Language :: Python :: 3.10
|
24
|
+
Topic :: Scientific/Engineering :: Image Processing
|
25
|
+
project_urls =
|
26
|
+
Bug Tracker = https://github.com/leoguignard/LineageTree/issues
|
27
|
+
Documentation = https://github.com/leoguignard/LineageTree#README.md
|
28
|
+
Source Code = https://github.com/leoguignard/LineageTree
|
29
|
+
User Support = https://github.com/leoguignard/LineageTree/issues
|
30
|
+
|
31
|
+
[options]
|
32
|
+
packages = find:
|
33
|
+
install_requires =
|
34
|
+
scipy>=1.9
|
35
|
+
numpy>=1.23
|
36
|
+
mastodon-reader
|
37
|
+
python_requires = >=3.8
|
38
|
+
include_package_data = True
|
39
|
+
package_dir =
|
40
|
+
=src
|
41
|
+
|
42
|
+
[options.packages.find]
|
43
|
+
where = src
|
44
|
+
|
45
|
+
[options.entry_points]
|
46
|
+
napari.manifest =
|
47
|
+
napari-lineageTree = napari_lineagetree:napari.yaml
|
48
|
+
|
49
|
+
[options.extras_require]
|
50
|
+
svg =
|
51
|
+
svgwrite
|
52
|
+
matplotlib
|
53
|
+
|
54
|
+
[egg_info]
|
55
|
+
tag_build =
|
56
|
+
tag_date = 0
|
57
|
+
|
@@ -1,2 +1,2 @@
|
|
1
|
-
__version__ = "1.0
|
1
|
+
__version__ = "1.2.0"
|
2
2
|
from .lineageTree import lineageTree
|
@@ -15,6 +15,7 @@ from itertools import combinations
|
|
15
15
|
from numbers import Number
|
16
16
|
import struct
|
17
17
|
from scipy.spatial.distance import cdist
|
18
|
+
import pickle as pkl
|
18
19
|
|
19
20
|
|
20
21
|
class lineageTree(object):
|
@@ -277,7 +278,11 @@ class lineageTree(object):
|
|
277
278
|
stroke_color=None,
|
278
279
|
positions=None,
|
279
280
|
node_color_map=None,
|
281
|
+
normalize=True,
|
280
282
|
):
|
283
|
+
|
284
|
+
##### remove background? default True background value? default 1
|
285
|
+
|
281
286
|
"""Writes the lineage tree to an SVG file.
|
282
287
|
Node and edges coloring and size can be provided.
|
283
288
|
|
@@ -300,7 +305,7 @@ class lineageTree(object):
|
|
300
305
|
node_color: func | str, a function that maps a node id to a triplet between 0 and 255.
|
301
306
|
The triplet will determine the color of the node. If a string is given instead and it is a property
|
302
307
|
of the tree, the the color will be mapped according to the property
|
303
|
-
node_color_map: str, the name of the colormap to use to color the nodes
|
308
|
+
node_color_map: str | func, the name of the colormap to use to color the nodes, or a colormap function
|
304
309
|
stroke_color: func, a function that maps a node id to a triplet between 0 and 255.
|
305
310
|
The triplet will determine the color of the stroke of the inward edge.
|
306
311
|
positions: {int: [float, float], ...}, dictionary that maps a node id to a 2D position.
|
@@ -317,6 +322,7 @@ class lineageTree(object):
|
|
317
322
|
|
318
323
|
if roots is None:
|
319
324
|
roots = list(set(self.successor).difference(self.predecessor))
|
325
|
+
roots = [cell for cell in roots if self.image_label[cell] != 1]
|
320
326
|
|
321
327
|
if node_size is None:
|
322
328
|
node_size = lambda x: vert_space_factor / 2.1
|
@@ -455,36 +461,54 @@ class lineageTree(object):
|
|
455
461
|
)
|
456
462
|
dwg.save()
|
457
463
|
|
458
|
-
def to_treex(self):
|
459
|
-
"""
|
460
|
-
|
461
|
-
|
462
|
-
# id_to_tree = {id: Tree() for id in self.nodes}
|
464
|
+
def to_treex(self, sampling=1, start=0, finish=10000, many=True):
|
465
|
+
"""
|
466
|
+
Convert the lineage tree into a treex file.
|
463
467
|
|
464
|
-
|
465
|
-
# for id_d in ids_d:
|
466
|
-
# id_to_tree[id_m].add_subtree(id_to_tree[id_d])
|
467
|
-
# roots = [id_to_tree[id] for id in set(self.successor).difference(self.predecessor)]
|
468
|
+
start/finish refer to first index in the new array times_to_consider
|
468
469
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
470
|
+
"""
|
471
|
+
from treex.tree import Tree
|
472
|
+
from warnings import warn
|
473
|
+
|
474
|
+
start //= sampling
|
475
|
+
finish //= sampling
|
476
|
+
if finish - start <= 0:
|
477
|
+
warn("Will return None, because start = finish")
|
478
|
+
return None
|
479
|
+
id_to_tree = {id: Tree() for id in self.nodes}
|
480
|
+
times_to_consider = [
|
481
|
+
t for t, n in self.time_nodes.items() if 0 < len(n)
|
482
|
+
]
|
483
|
+
times_to_consider = times_to_consider[start:finish:sampling]
|
484
|
+
start_time = times_to_consider[0]
|
485
|
+
for t in times_to_consider:
|
486
|
+
for id_mother in self.time_nodes[t]:
|
487
|
+
ids_daughters = self.successor.get(id_mother, [])
|
488
|
+
new_ids_daughters = ids_daughters.copy()
|
489
|
+
for _ in range(sampling - 1):
|
490
|
+
tmp = []
|
491
|
+
for d in new_ids_daughters:
|
492
|
+
tmp.extend(self.successor.get(d, [d]))
|
493
|
+
new_ids_daughters = tmp
|
494
|
+
for (
|
495
|
+
daugther
|
496
|
+
) in (
|
497
|
+
new_ids_daughters
|
498
|
+
): ## For each daughter in the list of daughters
|
499
|
+
id_to_tree[id_mother].add_subtree(
|
500
|
+
id_to_tree[daugther]
|
501
|
+
) ## Add the Treex daughter as a subtree of the Treex mother
|
502
|
+
roots = [id_to_tree[id] for id in set(self.time_nodes[start_time])]
|
503
|
+
for root, ids in zip(roots, set(self.time_nodes[start_time])):
|
504
|
+
root.add_attribute_to_id("ID", ids)
|
505
|
+
if not many:
|
506
|
+
reroot = Tree()
|
507
|
+
for root in roots:
|
508
|
+
reroot.add_subtree(root)
|
509
|
+
return reroot
|
510
|
+
else:
|
511
|
+
return roots
|
488
512
|
|
489
513
|
def to_tlp(
|
490
514
|
self,
|
@@ -576,9 +600,7 @@ class lineageTree(object):
|
|
576
600
|
]
|
577
601
|
if spatial:
|
578
602
|
edges_to_use += [
|
579
|
-
|
580
|
-
for e in s_edges
|
581
|
-
if t_min < self.time[e[0]] < t_max
|
603
|
+
e for e in s_edges if t_min < self.time[e[0]] < t_max
|
582
604
|
]
|
583
605
|
else:
|
584
606
|
nodes_to_use = list(self.nodes)
|
@@ -768,222 +790,142 @@ class lineageTree(object):
|
|
768
790
|
eigen (bool): whether or not to read the eigen values, default False
|
769
791
|
"""
|
770
792
|
self._astec_keydictionary = {
|
771
|
-
"
|
772
|
-
"
|
773
|
-
"
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
"
|
782
|
-
"
|
783
|
-
|
784
|
-
"
|
785
|
-
|
786
|
-
"
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
"
|
795
|
-
"
|
796
|
-
|
797
|
-
"
|
798
|
-
"
|
799
|
-
"
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
"
|
807
|
-
"
|
808
|
-
"
|
809
|
-
|
810
|
-
|
811
|
-
"
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
"
|
819
|
-
"
|
820
|
-
"
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
"
|
835
|
-
"output_key": "cell_fate_3",
|
836
|
-
"input_keys": ["cell_fate_3", "Fate3"],
|
837
|
-
},
|
838
|
-
"fate4": {
|
839
|
-
"output_key": "cell_fate_4",
|
840
|
-
"input_keys": ["cell_fate_4", "Fate4"],
|
841
|
-
},
|
842
|
-
"all-cells": {
|
843
|
-
"output_key": "all_cells",
|
844
|
-
"input_keys": [
|
845
|
-
"all_cells",
|
846
|
-
"All Cells",
|
847
|
-
"All_Cells",
|
848
|
-
"all cells",
|
849
|
-
"tot_cells",
|
850
|
-
],
|
851
|
-
},
|
852
|
-
"principal-value": {
|
853
|
-
"output_key": "cell_principal_values",
|
854
|
-
"input_keys": ["cell_principal_values", "Principal values"],
|
855
|
-
},
|
856
|
-
"name": {
|
857
|
-
"output_key": "cell_name",
|
858
|
-
"input_keys": ["cell_name", "Names", "names", "cell_names"],
|
859
|
-
},
|
860
|
-
"contact": {
|
861
|
-
"output_key": "cell_contact_surface",
|
862
|
-
"input_keys": [
|
863
|
-
"cell_contact_surface",
|
864
|
-
"cell_cell_contact_information",
|
865
|
-
],
|
866
|
-
},
|
867
|
-
"history": {
|
868
|
-
"output_key": "cell_history",
|
869
|
-
"input_keys": [
|
870
|
-
"cell_history",
|
871
|
-
"Cells history",
|
872
|
-
"cell_life",
|
873
|
-
"life",
|
874
|
-
],
|
875
|
-
},
|
876
|
-
"principal-vector": {
|
877
|
-
"output_key": "cell_principal_vectors",
|
878
|
-
"input_keys": ["cell_principal_vectors", "Principal vectors"],
|
879
|
-
},
|
880
|
-
"name-score": {
|
881
|
-
"output_key": "cell_naming_score",
|
882
|
-
"input_keys": ["cell_naming_score", "Scores", "scores"],
|
883
|
-
},
|
884
|
-
"problems": {
|
885
|
-
"output_key": "problematic_cells",
|
886
|
-
"input_keys": ["problematic_cells"],
|
887
|
-
},
|
888
|
-
"unknown": {
|
889
|
-
"output_key": "unknown_key",
|
890
|
-
"input_keys": ["unknown_key"],
|
891
|
-
},
|
793
|
+
"cell_lineage": [
|
794
|
+
"lineage_tree",
|
795
|
+
"lin_tree",
|
796
|
+
"Lineage tree",
|
797
|
+
"cell_lineage",
|
798
|
+
],
|
799
|
+
"cell_h_min": ["cell_h_min", "h_mins_information"],
|
800
|
+
"cell_volume": [
|
801
|
+
"cell_volume",
|
802
|
+
"volumes_information",
|
803
|
+
"volumes information",
|
804
|
+
"vol",
|
805
|
+
],
|
806
|
+
"cell_surface": ["cell_surface", "cell surface"],
|
807
|
+
"cell_compactness": [
|
808
|
+
"cell_compactness",
|
809
|
+
"Cell Compactness",
|
810
|
+
"compacity",
|
811
|
+
"cell_sphericity",
|
812
|
+
],
|
813
|
+
"cell_sigma": ["cell_sigma", "sigmas_information", "sigmas"],
|
814
|
+
"cell_labels_in_time": [
|
815
|
+
"cell_labels_in_time",
|
816
|
+
"Cells labels in time",
|
817
|
+
"time_labels",
|
818
|
+
],
|
819
|
+
"cell_barycenter": [
|
820
|
+
"cell_barycenter",
|
821
|
+
"Barycenters",
|
822
|
+
"barycenters",
|
823
|
+
],
|
824
|
+
"cell_fate": ["cell_fate", "Fate"],
|
825
|
+
"cell_fate_2": ["cell_fate_2", "Fate2"],
|
826
|
+
"cell_fate_3": ["cell_fate_3", "Fate3"],
|
827
|
+
"cell_fate_4": ["cell_fate_4", "Fate4"],
|
828
|
+
"all_cells": [
|
829
|
+
"all_cells",
|
830
|
+
"All Cells",
|
831
|
+
"All_Cells",
|
832
|
+
"all cells",
|
833
|
+
"tot_cells",
|
834
|
+
],
|
835
|
+
"cell_principal_values": [
|
836
|
+
"cell_principal_values",
|
837
|
+
"Principal values",
|
838
|
+
],
|
839
|
+
"cell_name": ["cell_name", "Names", "names", "cell_names"],
|
840
|
+
"cell_contact_surface": [
|
841
|
+
"cell_contact_surface",
|
842
|
+
"cell_cell_contact_information",
|
843
|
+
],
|
844
|
+
"cell_history": [
|
845
|
+
"cell_history",
|
846
|
+
"Cells history",
|
847
|
+
"cell_life",
|
848
|
+
"life",
|
849
|
+
],
|
850
|
+
"cell_principal_vectors": [
|
851
|
+
"cell_principal_vectors",
|
852
|
+
"Principal vectors",
|
853
|
+
],
|
854
|
+
"cell_naming_score": ["cell_naming_score", "Scores", "scores"],
|
855
|
+
"problematic_cells": ["problematic_cells"],
|
856
|
+
"unknown_key": ["unknown_key"],
|
892
857
|
}
|
858
|
+
|
893
859
|
if os.path.splitext(file_path)[-1] == ".xml":
|
894
860
|
tmp_data = self._read_from_ASTEC_xml(file_path)
|
895
861
|
else:
|
896
862
|
tmp_data = self._read_from_ASTEC_pkl(file_path, eigen)
|
897
863
|
|
864
|
+
# make sure these are all named liked they are in tmp_data (or change dictionary above)
|
898
865
|
self.name = {}
|
899
866
|
self.volume = {}
|
900
867
|
self.lT2pkl = {}
|
901
868
|
self.pkl2lT = {}
|
902
869
|
self.contact = {}
|
903
870
|
self.prob_cells = set()
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
names = tmp_data["Names"]
|
914
|
-
else:
|
915
|
-
names = {}
|
916
|
-
if "cell_fate" in tmp_data:
|
917
|
-
self.fates = {}
|
918
|
-
fates = tmp_data["cell_fate"]
|
919
|
-
do_fates = True
|
920
|
-
else:
|
921
|
-
do_fates = False
|
922
|
-
if "cell_volume" in tmp_data:
|
923
|
-
do_volumes = True
|
924
|
-
volumes = tmp_data["cell_volume"]
|
925
|
-
elif "volume_information" in tmp_data:
|
926
|
-
do_volumes = True
|
927
|
-
volumes = tmp_data["volume_information"]
|
928
|
-
else:
|
929
|
-
do_volumes = False
|
871
|
+
self.image_label = {}
|
872
|
+
|
873
|
+
lt = tmp_data["cell_lineage"]
|
874
|
+
|
875
|
+
# if "cell_name" in tmp_data:
|
876
|
+
# names = tmp_data["cell_name"]
|
877
|
+
# else:
|
878
|
+
# names = {}
|
879
|
+
|
930
880
|
if "cell_contact_surface" in tmp_data:
|
931
881
|
do_surf = True
|
932
882
|
surfaces = tmp_data["cell_contact_surface"]
|
933
883
|
else:
|
934
884
|
do_surf = False
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
885
|
+
|
886
|
+
# if "problematic_cells" in tmp_data:
|
887
|
+
# prob_cells = set(tmp_data["problematic_cells"])
|
888
|
+
# else:
|
889
|
+
# prob_cells = set()
|
890
|
+
# if eigen:
|
891
|
+
# self.eigen_vectors = {}
|
892
|
+
# self.eigen_values = {}
|
893
|
+
# eig_val = tmp_data["cell_principal_values"]
|
894
|
+
# eig_vec = tmp_data["cell_principal_vectors"]
|
944
895
|
|
945
896
|
inv = {vi: [c] for c, v in lt.items() for vi in v}
|
946
897
|
nodes = set(lt).union(inv)
|
947
|
-
if "cell_barycenter" in tmp_data:
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
else:
|
952
|
-
pos = dict(
|
953
|
-
list(
|
954
|
-
zip(
|
955
|
-
nodes,
|
956
|
-
[
|
957
|
-
[0.0, 0.0, 0.0],
|
958
|
-
]
|
959
|
-
* len(nodes),
|
960
|
-
)
|
961
|
-
)
|
962
|
-
)
|
898
|
+
# if "cell_barycenter" in tmp_data:
|
899
|
+
# pos = tmp_data["cell_barycenter"]
|
900
|
+
# else:
|
901
|
+
# pos = dict(list(zip(nodes,[[0.0, 0.0, 0.0]]* len(nodes))))
|
963
902
|
|
964
903
|
unique_id = 0
|
965
|
-
id_corres = {}
|
904
|
+
# id_corres = {}
|
905
|
+
|
966
906
|
for n in nodes:
|
967
|
-
if n in prob_cells:
|
968
|
-
|
907
|
+
# if n in prob_cells:
|
908
|
+
# self.prob_cells.add(unique_id)
|
969
909
|
# if n in pos and n in names:
|
970
910
|
t = n // 10**4
|
911
|
+
self.image_label[unique_id] = n % 10**4
|
971
912
|
self.lT2pkl[unique_id] = n
|
972
913
|
self.pkl2lT[n] = unique_id
|
973
|
-
self.name[unique_id] = names.get(n, "")
|
974
|
-
position = np.array(pos.get(n, [0, 0, 0]), dtype=float)
|
914
|
+
# self.name[unique_id] = names.get(n, "")
|
915
|
+
# position = np.array(pos.get(n, [0, 0, 0]), dtype=float)
|
975
916
|
self.time_nodes.setdefault(t, set()).add(unique_id)
|
976
917
|
self.nodes.add(unique_id)
|
977
|
-
self.pos[unique_id] = position
|
918
|
+
# self.pos[unique_id] = position
|
978
919
|
self.time[unique_id] = t
|
979
|
-
id_corres[n] = unique_id
|
980
|
-
if
|
981
|
-
self.volume[unique_id] =
|
982
|
-
if eigen:
|
983
|
-
|
984
|
-
|
985
|
-
if
|
986
|
-
self.fates
|
920
|
+
# id_corres[n] = unique_id
|
921
|
+
if "cell_volume" in tmp_data:
|
922
|
+
self.volume[unique_id] = tmp_data["cell_volume"].get(n, 0.0)
|
923
|
+
# if eigen:
|
924
|
+
# self.eigen_vectors[unique_id] = eig_vec.get(n)
|
925
|
+
# self.eigen_values[unique_id] = eig_val.get(n)
|
926
|
+
if "cell_fate" in tmp_data:
|
927
|
+
self.fates = {}
|
928
|
+
self.fates[unique_id] = tmp_data["cell_fate"].get(n, "")
|
987
929
|
|
988
930
|
unique_id += 1
|
989
931
|
# self.contact = {self.pkl2lT[c]: v for c, v in surfaces.iteritems() if c in self.pkl2lT}
|
@@ -996,13 +938,13 @@ class lineageTree(object):
|
|
996
938
|
if n % 10**4 == 1 or n in self.pkl2lT
|
997
939
|
}
|
998
940
|
|
999
|
-
for n, new_id in
|
941
|
+
for n, new_id in self.pkl2lT.items():
|
1000
942
|
# new_id = id_corres[n]
|
1001
943
|
if n in inv:
|
1002
|
-
self.predecessor[new_id] = [
|
944
|
+
self.predecessor[new_id] = [self.pkl2lT[ni] for ni in inv[n]]
|
1003
945
|
if n in lt:
|
1004
946
|
self.successor[new_id] = [
|
1005
|
-
|
947
|
+
self.pkl2lT[ni] for ni in lt[n] if ni in self.pkl2lT
|
1006
948
|
]
|
1007
949
|
self.edges.update(
|
1008
950
|
[(new_id, ni) for ni in self.successor[new_id]]
|
@@ -1015,6 +957,35 @@ class lineageTree(object):
|
|
1015
957
|
self.t_e = max(self.time_nodes)
|
1016
958
|
self.max_id = unique_id
|
1017
959
|
|
960
|
+
# do this in the end of the process, skip lineage tree and whatever is stored already
|
961
|
+
for prop_name, prop_values in tmp_data.items():
|
962
|
+
if hasattr(self, prop_name):
|
963
|
+
continue
|
964
|
+
else:
|
965
|
+
if isinstance(prop_values, dict):
|
966
|
+
dictionary = {
|
967
|
+
self.pkl2lT[k]: v for k, v in prop_values.items()
|
968
|
+
}
|
969
|
+
# is it a regular dictionary or a dictionary with dictionaries inside?
|
970
|
+
for key, value in dictionary.items():
|
971
|
+
if isinstance(value, dict):
|
972
|
+
# rename all ids from old to new
|
973
|
+
dictionary[key] = {
|
974
|
+
self.pkl2lT[k]: v for k, v in value
|
975
|
+
}
|
976
|
+
self.__dict__[prop_name] = dictionary
|
977
|
+
# is any of this necessary? Or does it mean it anyways does not contain information about the id and a simple else: is enough?
|
978
|
+
elif (
|
979
|
+
prop_values.isinstance(set)
|
980
|
+
or prop_values.isinstance(list)
|
981
|
+
or prop_values.isinstance(np.array)
|
982
|
+
):
|
983
|
+
self.__dict__[prop_name] = prop_values
|
984
|
+
|
985
|
+
# what else could it be?
|
986
|
+
|
987
|
+
# add a list of all available properties
|
988
|
+
|
1018
989
|
def _read_from_ASTEC_xml(self, file_path):
|
1019
990
|
def _set_dictionary_value(root):
|
1020
991
|
if len(root) == 0:
|
@@ -1036,7 +1007,7 @@ class lineageTree(object):
|
|
1036
1007
|
dictionary = {}
|
1037
1008
|
|
1038
1009
|
for k, v in self._astec_keydictionary.items():
|
1039
|
-
if root.tag ==
|
1010
|
+
if root.tag == k:
|
1040
1011
|
dictionary[str(root.tag)] = _set_dictionary_value(root)
|
1041
1012
|
break
|
1042
1013
|
else:
|
@@ -1047,20 +1018,21 @@ class lineageTree(object):
|
|
1047
1018
|
return dictionary
|
1048
1019
|
|
1049
1020
|
def _read_from_ASTEC_pkl(self, file_path, eigen=False):
|
1050
|
-
import pickle as pkl
|
1051
1021
|
|
1052
1022
|
with open(file_path, "rb") as f:
|
1053
1023
|
tmp_data = pkl.load(f, encoding="latin1")
|
1054
1024
|
f.close()
|
1055
1025
|
new_ref = {}
|
1056
1026
|
for k, v in self._astec_keydictionary.items():
|
1057
|
-
for key in v
|
1058
|
-
new_ref[key] =
|
1027
|
+
for key in v:
|
1028
|
+
new_ref[key] = k
|
1059
1029
|
new_dict = {}
|
1060
1030
|
|
1061
1031
|
for k, v in tmp_data.items():
|
1062
1032
|
if k in new_ref:
|
1063
1033
|
new_dict[new_ref[k]] = v
|
1034
|
+
else:
|
1035
|
+
new_dict[k] = v
|
1064
1036
|
return new_dict
|
1065
1037
|
|
1066
1038
|
def read_from_txt_for_celegans(self, file):
|
@@ -1296,6 +1268,41 @@ class lineageTree(object):
|
|
1296
1268
|
self.ind_cells[t] = 1
|
1297
1269
|
self.max_id = unique_id - 1
|
1298
1270
|
|
1271
|
+
def read_from_mastodon(self, path, name):
|
1272
|
+
from mastodon_reader import MastodonReader
|
1273
|
+
|
1274
|
+
mr = MastodonReader(path)
|
1275
|
+
spots, links = mr.read_tables()
|
1276
|
+
mr.read_tags(spots, links)
|
1277
|
+
|
1278
|
+
self.node_name = {}
|
1279
|
+
|
1280
|
+
for c in spots.iloc:
|
1281
|
+
unique_id = c.name
|
1282
|
+
x, y, z = c.x, c.y, c.z
|
1283
|
+
t = c.t
|
1284
|
+
if name is not None:
|
1285
|
+
n = c[name]
|
1286
|
+
else:
|
1287
|
+
n = ""
|
1288
|
+
self.time_nodes.setdefault(t, set()).add(unique_id)
|
1289
|
+
self.nodes.add(unique_id)
|
1290
|
+
self.time[unique_id] = t
|
1291
|
+
self.node_name[unique_id] = n
|
1292
|
+
self.pos[unique_id] = np.array([x, y, z])
|
1293
|
+
|
1294
|
+
for e in links.iloc:
|
1295
|
+
source = e.source_idx
|
1296
|
+
target = e.target_idx
|
1297
|
+
self.predecessor.setdefault(target, []).append(source)
|
1298
|
+
self.successor.setdefault(source, []).append(target)
|
1299
|
+
self.edges.add((source, target))
|
1300
|
+
self.time_edges.setdefault(self.time[source], set()).add(
|
1301
|
+
(source, target)
|
1302
|
+
)
|
1303
|
+
self.t_b = min(self.time_nodes.keys())
|
1304
|
+
self.t_e = max(self.time_nodes.keys())
|
1305
|
+
|
1299
1306
|
def read_from_mamut_xml(self, path):
|
1300
1307
|
"""Read a lineage tree from a MaMuT xml.
|
1301
1308
|
|
@@ -1351,9 +1358,12 @@ class lineageTree(object):
|
|
1351
1358
|
self.predecessor = {}
|
1352
1359
|
self.track_name = {}
|
1353
1360
|
for track in AllTracks:
|
1354
|
-
|
1355
|
-
track.attrib["
|
1356
|
-
|
1361
|
+
if "TRACK_DURATION" in track.attrib:
|
1362
|
+
t_id, l = int(track.attrib["TRACK_ID"]), float(
|
1363
|
+
track.attrib["TRACK_DURATION"]
|
1364
|
+
)
|
1365
|
+
else:
|
1366
|
+
t_id = int(track.attrib["TRACK_ID"])
|
1357
1367
|
t_name = track.attrib["name"]
|
1358
1368
|
tracks[t_id] = []
|
1359
1369
|
for edge in track:
|
@@ -1577,6 +1587,20 @@ class lineageTree(object):
|
|
1577
1587
|
self.is_root = is_root
|
1578
1588
|
self.max_id = max(self.nodes)
|
1579
1589
|
|
1590
|
+
def write(self, fname):
|
1591
|
+
if os.path.splitext(fname)[-1] != ".lT":
|
1592
|
+
fname = os.path.extsep.join((fname, "lT"))
|
1593
|
+
with open(fname, "bw") as f:
|
1594
|
+
pkl.dump(self, f)
|
1595
|
+
f.close()
|
1596
|
+
|
1597
|
+
@classmethod
|
1598
|
+
def load(clf, fname):
|
1599
|
+
with open(fname, "br") as f:
|
1600
|
+
lT = pkl.load(f)
|
1601
|
+
f.close()
|
1602
|
+
return lT
|
1603
|
+
|
1580
1604
|
def get_idx3d(self, t):
|
1581
1605
|
"""Get a 3d kdtree for the dataset at time *t* .
|
1582
1606
|
The kdtree is stored in *self.kdtrees[t]*
|
@@ -1888,6 +1912,7 @@ class lineageTree(object):
|
|
1888
1912
|
raw_size=None,
|
1889
1913
|
reorder=False,
|
1890
1914
|
xml_attributes=[],
|
1915
|
+
name=None,
|
1891
1916
|
):
|
1892
1917
|
"""Main library to build tree graph representation of lineage tree data
|
1893
1918
|
It can read TGMM, ASTEC, SVF, MaMuT and TrackMate outputs.
|
@@ -1926,6 +1951,8 @@ class lineageTree(object):
|
|
1926
1951
|
self.t_e = te
|
1927
1952
|
elif file_type == "mamut" or file_type == "trackmate":
|
1928
1953
|
self.read_from_mamut_xml(file_format)
|
1954
|
+
elif file_type == "mastodon":
|
1955
|
+
self.read_from_mastodon(file_format, name)
|
1929
1956
|
elif file_type == "celegans":
|
1930
1957
|
self.read_from_txt_for_celegans(file_format)
|
1931
1958
|
elif file_type == "celegans_cao":
|
@@ -1936,5 +1963,7 @@ class lineageTree(object):
|
|
1936
1963
|
self.read_from_ASTEC(file_format, eigen)
|
1937
1964
|
elif file_type == "csv":
|
1938
1965
|
self.read_from_csv(file_format, z_mult, link=1, delim=delim)
|
1966
|
+
elif file_format is not None and ".lT" in file_format:
|
1967
|
+
self.load(file_format)
|
1939
1968
|
elif file_format is not None:
|
1940
1969
|
self.read_from_binary(file_format)
|
@@ -1,13 +1,28 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: LineageTree
|
3
|
-
Version: 1.0
|
4
|
-
Summary: Lineage tree structure
|
5
|
-
Home-page: https://github.com/leoguignard/
|
6
|
-
Author:
|
7
|
-
|
3
|
+
Version: 1.2.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
|
14
|
+
Classifier: Framework :: napari
|
8
15
|
Classifier: Intended Audience :: Developers
|
9
16
|
Classifier: License :: OSI Approved :: MIT License
|
17
|
+
Classifier: Operating System :: OS Independent
|
18
|
+
Classifier: Programming Language :: Python
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
10
21
|
Classifier: Programming Language :: Python :: 3.8
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
25
|
+
Requires-Python: >=3.8
|
11
26
|
Description-Content-Type: text/markdown
|
12
27
|
Provides-Extra: svg
|
13
28
|
License-File: LICENSE
|
@@ -1,12 +1,13 @@
|
|
1
1
|
LICENSE
|
2
2
|
README.md
|
3
3
|
pyproject.toml
|
4
|
-
setup.
|
4
|
+
setup.cfg
|
5
5
|
src/LineageTree/__init__.py
|
6
6
|
src/LineageTree/lineageTree.py
|
7
7
|
src/LineageTree.egg-info/PKG-INFO
|
8
8
|
src/LineageTree.egg-info/SOURCES.txt
|
9
9
|
src/LineageTree.egg-info/dependency_links.txt
|
10
|
+
src/LineageTree.egg-info/entry_points.txt
|
10
11
|
src/LineageTree.egg-info/requires.txt
|
11
12
|
src/LineageTree.egg-info/top_level.txt
|
12
13
|
test/test_lineageTree.py
|
LineageTree-1.0.2/setup.cfg
DELETED
LineageTree-1.0.2/setup.py
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
from setuptools import setup, find_packages
|
2
|
-
from codecs import open
|
3
|
-
from os import path
|
4
|
-
|
5
|
-
here = path.abspath(path.dirname(__file__))
|
6
|
-
|
7
|
-
# Get the long description from the README file
|
8
|
-
with open(path.join(here, "README.md")) as f:
|
9
|
-
long_description = f.read()
|
10
|
-
|
11
|
-
# Arguments marked as "Required" below must be included for upload to PyPI.
|
12
|
-
# Fields marked as "Optional" may be commented out.
|
13
|
-
|
14
|
-
setup(
|
15
|
-
name="LineageTree",
|
16
|
-
version="1.0.2",
|
17
|
-
description="Lineage tree structure for TGMM algorithm",
|
18
|
-
long_description=long_description,
|
19
|
-
long_description_content_type='text/markdown',
|
20
|
-
url="https://github.com/leoguignard/TGMMlibraries",
|
21
|
-
author="Leo Guignard",
|
22
|
-
classifiers=[
|
23
|
-
"Development Status :: 5 - Production/Stable",
|
24
|
-
"Intended Audience :: Developers",
|
25
|
-
"License :: OSI Approved :: MIT License",
|
26
|
-
"Programming Language :: Python :: 3.8",
|
27
|
-
],
|
28
|
-
packages=["LineageTree"],
|
29
|
-
package_dir={"": "src"},
|
30
|
-
install_requires=["scipy>=1.9", "numpy>=1.23"],
|
31
|
-
extras_require={"svg": ["svgwrite", "matplotlib"]},
|
32
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|