LineageTree 1.1.0__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.1.0/src/LineageTree.egg-info → LineageTree-1.2.0}/PKG-INFO +20 -5
- {LineageTree-1.1.0 → LineageTree-1.2.0}/pyproject.toml +1 -4
- LineageTree-1.2.0/setup.cfg +57 -0
- {LineageTree-1.1.0 → LineageTree-1.2.0}/src/LineageTree/__init__.py +1 -1
- {LineageTree-1.1.0 → LineageTree-1.2.0}/src/LineageTree/lineageTree.py +238 -218
- {LineageTree-1.1.0 → LineageTree-1.2.0/src/LineageTree.egg-info}/PKG-INFO +20 -5
- {LineageTree-1.1.0 → 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.1.0 → LineageTree-1.2.0}/src/LineageTree.egg-info/requires.txt +1 -0
- LineageTree-1.1.0/setup.cfg +0 -4
- LineageTree-1.1.0/setup.py +0 -32
- {LineageTree-1.1.0 → LineageTree-1.2.0}/LICENSE +0 -0
- {LineageTree-1.1.0 → LineageTree-1.2.0}/README.md +0 -0
- {LineageTree-1.1.0 → LineageTree-1.2.0}/src/LineageTree.egg-info/dependency_links.txt +0 -0
- {LineageTree-1.1.0 → LineageTree-1.2.0}/src/LineageTree.egg-info/top_level.txt +0 -0
- {LineageTree-1.1.0 → 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.
|
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.
|
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.
|
1
|
+
__version__ = "1.2.0"
|
2
2
|
from .lineageTree import lineageTree
|
@@ -17,6 +17,7 @@ import struct
|
|
17
17
|
from scipy.spatial.distance import cdist
|
18
18
|
import pickle as pkl
|
19
19
|
|
20
|
+
|
20
21
|
class lineageTree(object):
|
21
22
|
def get_next_id(self):
|
22
23
|
"""Computes the next authorized id.
|
@@ -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,28 +461,54 @@ class lineageTree(object):
|
|
455
461
|
)
|
456
462
|
dwg.save()
|
457
463
|
|
458
|
-
def to_treex(self, sampling=1):
|
459
|
-
"""
|
464
|
+
def to_treex(self, sampling=1, start=0, finish=10000, many=True):
|
465
|
+
"""
|
466
|
+
Convert the lineage tree into a treex file.
|
467
|
+
|
468
|
+
start/finish refer to first index in the new array times_to_consider
|
469
|
+
|
470
|
+
"""
|
460
471
|
from treex.tree import Tree
|
472
|
+
from warnings import warn
|
461
473
|
|
474
|
+
start //= sampling
|
475
|
+
finish //= sampling
|
476
|
+
if finish - start <= 0:
|
477
|
+
warn("Will return None, because start = finish")
|
478
|
+
return None
|
462
479
|
id_to_tree = {id: Tree() for id in self.nodes}
|
463
|
-
times_to_consider = [
|
464
|
-
|
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]
|
465
485
|
for t in times_to_consider:
|
466
486
|
for id_mother in self.time_nodes[t]:
|
467
487
|
ids_daughters = self.successor.get(id_mother, [])
|
468
488
|
new_ids_daughters = ids_daughters.copy()
|
469
|
-
for _ in range(sampling-1):
|
489
|
+
for _ in range(sampling - 1):
|
470
490
|
tmp = []
|
471
491
|
for d in new_ids_daughters:
|
472
492
|
tmp.extend(self.successor.get(d, [d]))
|
473
493
|
new_ids_daughters = tmp
|
474
|
-
for
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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
|
480
512
|
|
481
513
|
def to_tlp(
|
482
514
|
self,
|
@@ -568,9 +600,7 @@ class lineageTree(object):
|
|
568
600
|
]
|
569
601
|
if spatial:
|
570
602
|
edges_to_use += [
|
571
|
-
e
|
572
|
-
for e in s_edges
|
573
|
-
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
|
574
604
|
]
|
575
605
|
else:
|
576
606
|
nodes_to_use = list(self.nodes)
|
@@ -760,224 +790,142 @@ class lineageTree(object):
|
|
760
790
|
eigen (bool): whether or not to read the eigen values, default False
|
761
791
|
"""
|
762
792
|
self._astec_keydictionary = {
|
763
|
-
"
|
764
|
-
"
|
765
|
-
"
|
766
|
-
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
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
|
-
"output_key": "cell_fate_3",
|
828
|
-
"input_keys": ["cell_fate_3", "Fate3"],
|
829
|
-
},
|
830
|
-
"fate4": {
|
831
|
-
"output_key": "cell_fate_4",
|
832
|
-
"input_keys": ["cell_fate_4", "Fate4"],
|
833
|
-
},
|
834
|
-
"all-cells": {
|
835
|
-
"output_key": "all_cells",
|
836
|
-
"input_keys": [
|
837
|
-
"all_cells",
|
838
|
-
"All Cells",
|
839
|
-
"All_Cells",
|
840
|
-
"all cells",
|
841
|
-
"tot_cells",
|
842
|
-
],
|
843
|
-
},
|
844
|
-
"principal-value": {
|
845
|
-
"output_key": "cell_principal_values",
|
846
|
-
"input_keys": ["cell_principal_values", "Principal values"],
|
847
|
-
},
|
848
|
-
"name": {
|
849
|
-
"output_key": "cell_name",
|
850
|
-
"input_keys": ["cell_name", "Names", "names", "cell_names"],
|
851
|
-
},
|
852
|
-
"contact": {
|
853
|
-
"output_key": "cell_contact_surface",
|
854
|
-
"input_keys": [
|
855
|
-
"cell_contact_surface",
|
856
|
-
"cell_cell_contact_information",
|
857
|
-
],
|
858
|
-
},
|
859
|
-
"history": {
|
860
|
-
"output_key": "cell_history",
|
861
|
-
"input_keys": [
|
862
|
-
"cell_history",
|
863
|
-
"Cells history",
|
864
|
-
"cell_life",
|
865
|
-
"life",
|
866
|
-
],
|
867
|
-
},
|
868
|
-
"principal-vector": {
|
869
|
-
"output_key": "cell_principal_vectors",
|
870
|
-
"input_keys": ["cell_principal_vectors", "Principal vectors"],
|
871
|
-
},
|
872
|
-
"name-score": {
|
873
|
-
"output_key": "cell_naming_score",
|
874
|
-
"input_keys": ["cell_naming_score", "Scores", "scores"],
|
875
|
-
},
|
876
|
-
"problems": {
|
877
|
-
"output_key": "problematic_cells",
|
878
|
-
"input_keys": ["problematic_cells"],
|
879
|
-
},
|
880
|
-
"unknown": {
|
881
|
-
"output_key": "unknown_key",
|
882
|
-
"input_keys": ["unknown_key"],
|
883
|
-
},
|
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"],
|
884
857
|
}
|
858
|
+
|
885
859
|
if os.path.splitext(file_path)[-1] == ".xml":
|
886
860
|
tmp_data = self._read_from_ASTEC_xml(file_path)
|
887
861
|
else:
|
888
862
|
tmp_data = self._read_from_ASTEC_pkl(file_path, eigen)
|
889
863
|
|
864
|
+
# make sure these are all named liked they are in tmp_data (or change dictionary above)
|
890
865
|
self.name = {}
|
891
866
|
self.volume = {}
|
892
867
|
self.lT2pkl = {}
|
893
868
|
self.pkl2lT = {}
|
894
869
|
self.contact = {}
|
895
870
|
self.prob_cells = set()
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
names = tmp_data["Names"]
|
906
|
-
else:
|
907
|
-
names = {}
|
908
|
-
if "cell_fate" in tmp_data:
|
909
|
-
self.fates = {}
|
910
|
-
fates = tmp_data["cell_fate"]
|
911
|
-
do_fates = True
|
912
|
-
else:
|
913
|
-
do_fates = False
|
914
|
-
if "cell_volume" in tmp_data:
|
915
|
-
do_volumes = True
|
916
|
-
volumes = tmp_data["cell_volume"]
|
917
|
-
elif "volume_information" in tmp_data:
|
918
|
-
do_volumes = True
|
919
|
-
volumes = tmp_data["volume_information"]
|
920
|
-
else:
|
921
|
-
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
|
+
|
922
880
|
if "cell_contact_surface" in tmp_data:
|
923
881
|
do_surf = True
|
924
882
|
surfaces = tmp_data["cell_contact_surface"]
|
925
883
|
else:
|
926
884
|
do_surf = False
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
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"]
|
936
895
|
|
937
896
|
inv = {vi: [c] for c, v in lt.items() for vi in v}
|
938
897
|
nodes = set(lt).union(inv)
|
939
|
-
if "cell_barycenter" in tmp_data:
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
else:
|
944
|
-
pos = dict(
|
945
|
-
list(
|
946
|
-
zip(
|
947
|
-
nodes,
|
948
|
-
[
|
949
|
-
[0.0, 0.0, 0.0],
|
950
|
-
]
|
951
|
-
* len(nodes),
|
952
|
-
)
|
953
|
-
)
|
954
|
-
)
|
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))))
|
955
902
|
|
956
903
|
unique_id = 0
|
957
|
-
id_corres = {}
|
958
|
-
|
904
|
+
# id_corres = {}
|
905
|
+
|
959
906
|
for n in nodes:
|
960
|
-
if n in prob_cells:
|
961
|
-
|
907
|
+
# if n in prob_cells:
|
908
|
+
# self.prob_cells.add(unique_id)
|
962
909
|
# if n in pos and n in names:
|
963
910
|
t = n // 10**4
|
964
911
|
self.image_label[unique_id] = n % 10**4
|
965
912
|
self.lT2pkl[unique_id] = n
|
966
913
|
self.pkl2lT[n] = unique_id
|
967
|
-
self.name[unique_id] = names.get(n, "")
|
968
|
-
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)
|
969
916
|
self.time_nodes.setdefault(t, set()).add(unique_id)
|
970
917
|
self.nodes.add(unique_id)
|
971
|
-
self.pos[unique_id] = position
|
918
|
+
# self.pos[unique_id] = position
|
972
919
|
self.time[unique_id] = t
|
973
|
-
id_corres[n] = unique_id
|
974
|
-
if
|
975
|
-
self.volume[unique_id] =
|
976
|
-
if eigen:
|
977
|
-
|
978
|
-
|
979
|
-
if
|
980
|
-
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, "")
|
981
929
|
|
982
930
|
unique_id += 1
|
983
931
|
# self.contact = {self.pkl2lT[c]: v for c, v in surfaces.iteritems() if c in self.pkl2lT}
|
@@ -990,13 +938,13 @@ class lineageTree(object):
|
|
990
938
|
if n % 10**4 == 1 or n in self.pkl2lT
|
991
939
|
}
|
992
940
|
|
993
|
-
for n, new_id in
|
941
|
+
for n, new_id in self.pkl2lT.items():
|
994
942
|
# new_id = id_corres[n]
|
995
943
|
if n in inv:
|
996
|
-
self.predecessor[new_id] = [
|
944
|
+
self.predecessor[new_id] = [self.pkl2lT[ni] for ni in inv[n]]
|
997
945
|
if n in lt:
|
998
946
|
self.successor[new_id] = [
|
999
|
-
|
947
|
+
self.pkl2lT[ni] for ni in lt[n] if ni in self.pkl2lT
|
1000
948
|
]
|
1001
949
|
self.edges.update(
|
1002
950
|
[(new_id, ni) for ni in self.successor[new_id]]
|
@@ -1009,6 +957,35 @@ class lineageTree(object):
|
|
1009
957
|
self.t_e = max(self.time_nodes)
|
1010
958
|
self.max_id = unique_id
|
1011
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
|
+
|
1012
989
|
def _read_from_ASTEC_xml(self, file_path):
|
1013
990
|
def _set_dictionary_value(root):
|
1014
991
|
if len(root) == 0:
|
@@ -1030,7 +1007,7 @@ class lineageTree(object):
|
|
1030
1007
|
dictionary = {}
|
1031
1008
|
|
1032
1009
|
for k, v in self._astec_keydictionary.items():
|
1033
|
-
if root.tag ==
|
1010
|
+
if root.tag == k:
|
1034
1011
|
dictionary[str(root.tag)] = _set_dictionary_value(root)
|
1035
1012
|
break
|
1036
1013
|
else:
|
@@ -1041,19 +1018,21 @@ class lineageTree(object):
|
|
1041
1018
|
return dictionary
|
1042
1019
|
|
1043
1020
|
def _read_from_ASTEC_pkl(self, file_path, eigen=False):
|
1044
|
-
|
1021
|
+
|
1045
1022
|
with open(file_path, "rb") as f:
|
1046
1023
|
tmp_data = pkl.load(f, encoding="latin1")
|
1047
1024
|
f.close()
|
1048
1025
|
new_ref = {}
|
1049
1026
|
for k, v in self._astec_keydictionary.items():
|
1050
|
-
for key in v
|
1051
|
-
new_ref[key] =
|
1027
|
+
for key in v:
|
1028
|
+
new_ref[key] = k
|
1052
1029
|
new_dict = {}
|
1053
1030
|
|
1054
1031
|
for k, v in tmp_data.items():
|
1055
1032
|
if k in new_ref:
|
1056
1033
|
new_dict[new_ref[k]] = v
|
1034
|
+
else:
|
1035
|
+
new_dict[k] = v
|
1057
1036
|
return new_dict
|
1058
1037
|
|
1059
1038
|
def read_from_txt_for_celegans(self, file):
|
@@ -1289,6 +1268,41 @@ class lineageTree(object):
|
|
1289
1268
|
self.ind_cells[t] = 1
|
1290
1269
|
self.max_id = unique_id - 1
|
1291
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
|
+
|
1292
1306
|
def read_from_mamut_xml(self, path):
|
1293
1307
|
"""Read a lineage tree from a MaMuT xml.
|
1294
1308
|
|
@@ -1344,9 +1358,12 @@ class lineageTree(object):
|
|
1344
1358
|
self.predecessor = {}
|
1345
1359
|
self.track_name = {}
|
1346
1360
|
for track in AllTracks:
|
1347
|
-
|
1348
|
-
track.attrib["
|
1349
|
-
|
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"])
|
1350
1367
|
t_name = track.attrib["name"]
|
1351
1368
|
tracks[t_id] = []
|
1352
1369
|
for edge in track:
|
@@ -1571,15 +1588,15 @@ class lineageTree(object):
|
|
1571
1588
|
self.max_id = max(self.nodes)
|
1572
1589
|
|
1573
1590
|
def write(self, fname):
|
1574
|
-
if os.path.splitext(fname)[-1] !=
|
1575
|
-
os.path.extsep.join(fname,
|
1576
|
-
with open(fname,
|
1591
|
+
if os.path.splitext(fname)[-1] != ".lT":
|
1592
|
+
fname = os.path.extsep.join((fname, "lT"))
|
1593
|
+
with open(fname, "bw") as f:
|
1577
1594
|
pkl.dump(self, f)
|
1578
1595
|
f.close()
|
1579
1596
|
|
1580
1597
|
@classmethod
|
1581
1598
|
def load(clf, fname):
|
1582
|
-
with open(fname,
|
1599
|
+
with open(fname, "br") as f:
|
1583
1600
|
lT = pkl.load(f)
|
1584
1601
|
f.close()
|
1585
1602
|
return lT
|
@@ -1895,6 +1912,7 @@ class lineageTree(object):
|
|
1895
1912
|
raw_size=None,
|
1896
1913
|
reorder=False,
|
1897
1914
|
xml_attributes=[],
|
1915
|
+
name=None,
|
1898
1916
|
):
|
1899
1917
|
"""Main library to build tree graph representation of lineage tree data
|
1900
1918
|
It can read TGMM, ASTEC, SVF, MaMuT and TrackMate outputs.
|
@@ -1933,6 +1951,8 @@ class lineageTree(object):
|
|
1933
1951
|
self.t_e = te
|
1934
1952
|
elif file_type == "mamut" or file_type == "trackmate":
|
1935
1953
|
self.read_from_mamut_xml(file_format)
|
1954
|
+
elif file_type == "mastodon":
|
1955
|
+
self.read_from_mastodon(file_format, name)
|
1936
1956
|
elif file_type == "celegans":
|
1937
1957
|
self.read_from_txt_for_celegans(file_format)
|
1938
1958
|
elif file_type == "celegans_cao":
|
@@ -1943,7 +1963,7 @@ class lineageTree(object):
|
|
1943
1963
|
self.read_from_ASTEC(file_format, eigen)
|
1944
1964
|
elif file_type == "csv":
|
1945
1965
|
self.read_from_csv(file_format, z_mult, link=1, delim=delim)
|
1946
|
-
elif file_format is not None and
|
1947
|
-
self.
|
1966
|
+
elif file_format is not None and ".lT" in file_format:
|
1967
|
+
self.load(file_format)
|
1948
1968
|
elif file_format is not None:
|
1949
1969
|
self.read_from_binary(file_format)
|
@@ -1,13 +1,28 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: LineageTree
|
3
|
-
Version: 1.
|
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.1.0/setup.cfg
DELETED
LineageTree-1.1.0/setup.py
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
from setuptools import setup
|
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.1.0",
|
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
|