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.
@@ -1,13 +1,28 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LineageTree
3
- Version: 1.0.2
4
- Summary: Lineage tree structure for TGMM algorithm
5
- Home-page: https://github.com/leoguignard/TGMMlibraries
6
- Author: Leo Guignard
7
- Classifier: Development Status :: 5 - Production/Stable
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.2"
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.2"
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
- """Convert the lineage tree into a treex file."""
460
- from treex.tree import Tree
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
- # for id_m, ids_d in self.successor.items():
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
- roots = set(self.successor).difference(self.predecessor)
470
- id_to_tree = {}
471
- root_trees = []
472
- for r in roots:
473
- to_do = [r]
474
- while 0 < len(to_do):
475
- curr = to_do.pop()
476
- cycle = self.get_cycle(curr)
477
- lifetime = len(cycle)
478
- cell = Tree()
479
- cell.add_attribute_to_id("len", lifetime)
480
- if cycle[0] in self.predecessor:
481
- id_to_tree[self.predecessor[cycle[0]][0]].add_subtree(cell)
482
- else:
483
- root_trees.append(cell)
484
- id_to_tree[cycle[-1]] = cell
485
- to_do.extend(self.successor.get(cycle[-1], []))
486
-
487
- return roots
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
- (e, ei)
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
- "lineage": {
772
- "output_key": "cell_lineage",
773
- "input_keys": [
774
- "lineage_tree",
775
- "lin_tree",
776
- "Lineage tree",
777
- "cell_lineage",
778
- ],
779
- },
780
- "h_min": {
781
- "output_key": "cell_h_min",
782
- "input_keys": ["cell_h_min", "h_mins_information"],
783
- },
784
- "volume": {
785
- "output_key": "cell_volume",
786
- "input_keys": [
787
- "cell_volume",
788
- "volumes_information",
789
- "volumes information",
790
- "vol",
791
- ],
792
- },
793
- "surface": {
794
- "output_key": "cell_surface",
795
- "input_keys": ["cell_surface", "cell surface"],
796
- },
797
- "compactness": {
798
- "output_key": "cell_compactness",
799
- "input_keys": [
800
- "cell_compactness",
801
- "Cell Compactness",
802
- "compacity",
803
- "cell_sphericity",
804
- ],
805
- },
806
- "sigma": {
807
- "output_key": "cell_sigma",
808
- "input_keys": ["cell_sigma", "sigmas_information", "sigmas"],
809
- },
810
- "label_in_time": {
811
- "output_key": "cell_labels_in_time",
812
- "input_keys": [
813
- "cell_labels_in_time",
814
- "Cells labels in time",
815
- "time_labels",
816
- ],
817
- },
818
- "barycenter": {
819
- "output_key": "cell_barycenter",
820
- "input_keys": [
821
- "cell_barycenter",
822
- "Barycenters",
823
- "barycenters",
824
- ],
825
- },
826
- "fate": {
827
- "output_key": "cell_fate",
828
- "input_keys": ["cell_fate", "Fate"],
829
- },
830
- "fate2": {
831
- "output_key": "cell_fate_2",
832
- "input_keys": ["cell_fate_2", "Fate2"],
833
- },
834
- "fate3": {
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
- if "cell_lineage" in tmp_data:
905
- lt = tmp_data["cell_lineage"]
906
- elif "lin_tree" in tmp_data:
907
- lt = tmp_data["lin_tree"]
908
- else:
909
- lt = tmp_data["Lineage tree"]
910
- if "cell_name" in tmp_data:
911
- names = tmp_data["cell_name"]
912
- elif "Names" in tmp_data:
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
- if "problematic_cells" in tmp_data:
936
- prob_cells = set(tmp_data["problematic_cells"])
937
- else:
938
- prob_cells = set()
939
- if eigen:
940
- self.eigen_vectors = {}
941
- self.eigen_values = {}
942
- eig_val = tmp_data["cell_principal_values"]
943
- eig_vec = tmp_data["cell_principal_vectors"]
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
- pos = tmp_data["cell_barycenter"]
949
- elif "Barycenters" in tmp_data:
950
- pos = tmp_data["Barycenters"]
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
- self.prob_cells.add(unique_id)
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 do_volumes:
981
- self.volume[unique_id] = volumes.get(n, 0.0)
982
- if eigen:
983
- self.eigen_vectors[unique_id] = eig_vec.get(n)
984
- self.eigen_values[unique_id] = eig_val.get(n)
985
- if do_fates:
986
- self.fates[unique_id] = fates.get(n, "")
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 id_corres.items():
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] = [id_corres[ni] for ni in inv[n]]
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
- id_corres[ni] for ni in lt[n] if ni in id_corres
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 == v["output_key"]:
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["input_keys"]:
1058
- new_ref[key] = v["output_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
- t_id, l = int(track.attrib["TRACK_ID"]), float(
1355
- track.attrib["TRACK_DURATION"]
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.2
4
- Summary: Lineage tree structure for TGMM algorithm
5
- Home-page: https://github.com/leoguignard/TGMMlibraries
6
- Author: Leo Guignard
7
- Classifier: Development Status :: 5 - Production/Stable
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.py
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
@@ -0,0 +1,2 @@
1
+ [napari.manifest]
2
+ napari-lineageTree = napari_lineagetree:napari.yaml
@@ -1,5 +1,6 @@
1
1
  scipy>=1.9
2
2
  numpy>=1.23
3
+ mastodon-reader
3
4
 
4
5
  [svg]
5
6
  svgwrite
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
@@ -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