pycellin 0.3.5b3__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.
Files changed (34) hide show
  1. pycellin-0.3.5b3/LICENSE +27 -0
  2. pycellin-0.3.5b3/PKG-INFO +124 -0
  3. pycellin-0.3.5b3/README.md +91 -0
  4. pycellin-0.3.5b3/pycellin/__init__.py +66 -0
  5. pycellin-0.3.5b3/pycellin/classes/__init__.py +24 -0
  6. pycellin-0.3.5b3/pycellin/classes/data.py +263 -0
  7. pycellin-0.3.5b3/pycellin/classes/exceptions.py +107 -0
  8. pycellin-0.3.5b3/pycellin/classes/feature.py +812 -0
  9. pycellin-0.3.5b3/pycellin/classes/feature_calculator.py +431 -0
  10. pycellin-0.3.5b3/pycellin/classes/lineage.py +1323 -0
  11. pycellin-0.3.5b3/pycellin/classes/model.py +1708 -0
  12. pycellin-0.3.5b3/pycellin/classes/updater.py +192 -0
  13. pycellin-0.3.5b3/pycellin/custom_types.py +18 -0
  14. pycellin-0.3.5b3/pycellin/graph/__init__.py +0 -0
  15. pycellin-0.3.5b3/pycellin/graph/features/__init__.py +14 -0
  16. pycellin-0.3.5b3/pycellin/graph/features/morphology.py +587 -0
  17. pycellin-0.3.5b3/pycellin/graph/features/motion.py +473 -0
  18. pycellin-0.3.5b3/pycellin/graph/features/tracking.py +331 -0
  19. pycellin-0.3.5b3/pycellin/graph/features/utils.py +52 -0
  20. pycellin-0.3.5b3/pycellin/io/__init__.py +4 -0
  21. pycellin-0.3.5b3/pycellin/io/cell_tracking_challenge/__init__.py +2 -0
  22. pycellin-0.3.5b3/pycellin/io/cell_tracking_challenge/exporter.py +286 -0
  23. pycellin-0.3.5b3/pycellin/io/cell_tracking_challenge/loader.py +305 -0
  24. pycellin-0.3.5b3/pycellin/io/trackmate/__init__.py +2 -0
  25. pycellin-0.3.5b3/pycellin/io/trackmate/exporter.py +587 -0
  26. pycellin-0.3.5b3/pycellin/io/trackmate/loader.py +1234 -0
  27. pycellin-0.3.5b3/pycellin/utils.py +10 -0
  28. pycellin-0.3.5b3/pycellin.egg-info/PKG-INFO +124 -0
  29. pycellin-0.3.5b3/pycellin.egg-info/SOURCES.txt +32 -0
  30. pycellin-0.3.5b3/pycellin.egg-info/dependency_links.txt +1 -0
  31. pycellin-0.3.5b3/pycellin.egg-info/requires.txt +13 -0
  32. pycellin-0.3.5b3/pycellin.egg-info/top_level.txt +1 -0
  33. pycellin-0.3.5b3/pyproject.toml +55 -0
  34. pycellin-0.3.5b3/setup.cfg +4 -0
@@ -0,0 +1,27 @@
1
+ Copyright 2023 Institut Pasteur Paris, Laura Xénard
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice,
7
+ this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ 3. Neither the name of the copyright holder nor the names of its contributors
14
+ may be used to endorse or promote products derived from this software without
15
+ specific prior written permission.
16
+
17
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
+ POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,124 @@
1
+ Metadata-Version: 2.2
2
+ Name: pycellin
3
+ Version: 0.3.5b3
4
+ Summary: Graph-based framework to manipulate and analyze cell lineages from cell tracking data
5
+ Author-email: Laura Xénard <laura.xenard@pasteur.fr>
6
+ Project-URL: Documentation, https://Image-Analysis-Hub.github.io/pycellin/
7
+ Project-URL: Examples, https://Image-Analysis-Hub.github.io/pycellin/notebooks/
8
+ Project-URL: Issues, https://github.com/Image-Analysis-Hub/pycellin/issues
9
+ Project-URL: Source, https://github.com/Image-Analysis-Hub/pycellin/
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: BSD License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: igraph>=0.9
22
+ Requires-Dist: lxml>=5
23
+ Requires-Dist: matplotlib>=3
24
+ Requires-Dist: networkx>=3
25
+ Requires-Dist: pandas>=2
26
+ Requires-Dist: plotly>=5
27
+ Requires-Dist: scikit-image>=0.19
28
+ Requires-Dist: scipy>=1.15
29
+ Requires-Dist: shapely>=2
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest>=8.3; extra == "test"
32
+ Requires-Dist: pytest-cov>=6.0; extra == "test"
33
+
34
+ ![Build](https://github.com/Image-Analysis-Hub/pycellin/actions/workflows/build.yml/badge.svg)
35
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
36
+
37
+ # Pycellin
38
+
39
+ Pycellin is a graph-based Python framework to easily manipulate and extract information from cell tracking data, at the single-cell level. In Pycellin, cell lineages are modeled intuitively by directed acyclic graphs (DAG). Graph nodes represent cells at a specific point in time and space, and graph edges represent the time and space displacement of the cells. Please note that while Pycellin is built to support cell division events, **it does not authorize cell merging events**: a cell at a specific timepoint cannot have more than one parent.
40
+
41
+ Pycellin provides predefined features related to cell morphology, cell motion and tracking that can be automatically added to enrich lineages. More predefined features will be implemented in the future. The framework also facilitates the creation of new features defined by the user to accommodate the wide variety of experiments and biological questions.
42
+
43
+ Pycellin can read from and write to TrackMate XML and Cell Tracking Challenge text file formats. More tracking formats will progressively be supported.
44
+
45
+ While Pycellin has been designed with bacteria / cell lineages in mind, it could be used with more diverse tracking data provided the few conditions below are enforced:
46
+ - the tracking data can be modeled by a DAG, meaning no merging event
47
+ - time must flow homogeneously, i.e. all the edges of a lineage graph must represent the same elapsed time.
48
+
49
+
50
+ ## Installation
51
+
52
+ Pycellin supports Python 3.10 and above. It is tested with Python 3.10 and 3.13 on the latest versions of Ubuntu, Windows and MacOS. Please let me know if you encounter any compatibility issue with a different combination.
53
+
54
+ You can install Pycellin from [PyPI](https://pypi.org/):
55
+
56
+ ```
57
+ pip install pycellin
58
+ ```
59
+
60
+ To install Pycellin with the optional test related dependencies:
61
+
62
+ ```
63
+ pip install pycellin[test]
64
+ ```
65
+
66
+
67
+ ## Code Example
68
+
69
+ ```python
70
+ import pycellin
71
+
72
+ # Import data from an external tool, here TrackMate.
73
+ xml_path = "sample_data/Ecoli_growth_on_agar_pad.xml"
74
+ model = pycellin.load_TrackMate_XML(xml_path)
75
+
76
+ # Plot the cell lineages.
77
+ for lin in model.get_cell_lineages():
78
+ plot(lin)
79
+
80
+ # Compute and plot the cell cycle lineages.
81
+ model.add_cycle_data()
82
+ for clin in model.get_cycle_lineages():
83
+ plot(clin)
84
+
85
+ # Enrich your lineages with additional predefined features.
86
+ model.add_pycellin_features([
87
+ "cell_length",
88
+ "cell_width",
89
+ "cell_displacement",
90
+ "cell_speed",
91
+ "branch_mean_speed",
92
+ "relative_age",
93
+ "division_time",
94
+ "division_rate",
95
+ "cell_cycle_completeness",
96
+ ])
97
+ model.update()
98
+
99
+ # Export the enriched data as dataframes.
100
+ cell_df = model.to_cell_dataframe()
101
+ link_df = model.to_link_dataframe()
102
+ cycle_df = model.to_cycle_dataframe()
103
+ lineage_df = model.to_lineage_dataframe()
104
+ ```
105
+
106
+
107
+ ## Usage
108
+
109
+ Please note that the following notebooks are still a work in progress. There may be some mistakes in the code and some sections might move from one notebook to another.
110
+
111
+ | Notebook | Description | Level | State |
112
+ |------------------------------------------------------------------------------------------|-------------------------------------------------------------------|----------|-------|
113
+ | [Getting started](./notebooks/Getting%20started.ipynb) | The basics of Pycellin, through examples | Beginner | WIP |
114
+ | [Managing features](./notebooks/Managing%20features.ipynb) | How to add, compute and remove features from a model | Beginner | WIP |
115
+ | [Working with TrackMate data](./notebooks/Working%20with%20TrackMate%20data.ipynb) | How Pycellin can work with TrackMate, through an example | Beginner | WIP |
116
+ | [Creating a model from scratch](./notebooks/Creating%20a%20model%20from%20scratch.ipynb) | How to manually create a Pycellin model, including its lineages | Advanced | Stub |
117
+ | [Custom features](./notebooks/Custom%20features.ipynb) | How to create user-defined features and augment a model with them | Advanced | WIP |
118
+
119
+
120
+ ## Credits
121
+
122
+ - [NetworkX](https://networkx.org/) for lineages modeling ([Hagberg, Schult and Swart, 2008](http://conference.scipy.org.s3-website-us-east-1.amazonaws.com/proceedings/scipy2008/paper_2/))
123
+ - [TrackMate](https://imagej.net/plugins/trackmate/) for the TrackMate data loader and exporter ([Tinevez et al., 2017](https://doi.org/10.1016/j.ymeth.2016.09.016), [Ershov et al., 2022](https://doi:10.1038/s41592-022-01507-1))
124
+ - The [Cell Tracking Challenge](https://celltrackingchallenge.net/) for the CTC data loader and exporter ([Maška et al., 2023](https://doi.org/10.1038/s41592-023-01879-y))
@@ -0,0 +1,91 @@
1
+ ![Build](https://github.com/Image-Analysis-Hub/pycellin/actions/workflows/build.yml/badge.svg)
2
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
3
+
4
+ # Pycellin
5
+
6
+ Pycellin is a graph-based Python framework to easily manipulate and extract information from cell tracking data, at the single-cell level. In Pycellin, cell lineages are modeled intuitively by directed acyclic graphs (DAG). Graph nodes represent cells at a specific point in time and space, and graph edges represent the time and space displacement of the cells. Please note that while Pycellin is built to support cell division events, **it does not authorize cell merging events**: a cell at a specific timepoint cannot have more than one parent.
7
+
8
+ Pycellin provides predefined features related to cell morphology, cell motion and tracking that can be automatically added to enrich lineages. More predefined features will be implemented in the future. The framework also facilitates the creation of new features defined by the user to accommodate the wide variety of experiments and biological questions.
9
+
10
+ Pycellin can read from and write to TrackMate XML and Cell Tracking Challenge text file formats. More tracking formats will progressively be supported.
11
+
12
+ While Pycellin has been designed with bacteria / cell lineages in mind, it could be used with more diverse tracking data provided the few conditions below are enforced:
13
+ - the tracking data can be modeled by a DAG, meaning no merging event
14
+ - time must flow homogeneously, i.e. all the edges of a lineage graph must represent the same elapsed time.
15
+
16
+
17
+ ## Installation
18
+
19
+ Pycellin supports Python 3.10 and above. It is tested with Python 3.10 and 3.13 on the latest versions of Ubuntu, Windows and MacOS. Please let me know if you encounter any compatibility issue with a different combination.
20
+
21
+ You can install Pycellin from [PyPI](https://pypi.org/):
22
+
23
+ ```
24
+ pip install pycellin
25
+ ```
26
+
27
+ To install Pycellin with the optional test related dependencies:
28
+
29
+ ```
30
+ pip install pycellin[test]
31
+ ```
32
+
33
+
34
+ ## Code Example
35
+
36
+ ```python
37
+ import pycellin
38
+
39
+ # Import data from an external tool, here TrackMate.
40
+ xml_path = "sample_data/Ecoli_growth_on_agar_pad.xml"
41
+ model = pycellin.load_TrackMate_XML(xml_path)
42
+
43
+ # Plot the cell lineages.
44
+ for lin in model.get_cell_lineages():
45
+ plot(lin)
46
+
47
+ # Compute and plot the cell cycle lineages.
48
+ model.add_cycle_data()
49
+ for clin in model.get_cycle_lineages():
50
+ plot(clin)
51
+
52
+ # Enrich your lineages with additional predefined features.
53
+ model.add_pycellin_features([
54
+ "cell_length",
55
+ "cell_width",
56
+ "cell_displacement",
57
+ "cell_speed",
58
+ "branch_mean_speed",
59
+ "relative_age",
60
+ "division_time",
61
+ "division_rate",
62
+ "cell_cycle_completeness",
63
+ ])
64
+ model.update()
65
+
66
+ # Export the enriched data as dataframes.
67
+ cell_df = model.to_cell_dataframe()
68
+ link_df = model.to_link_dataframe()
69
+ cycle_df = model.to_cycle_dataframe()
70
+ lineage_df = model.to_lineage_dataframe()
71
+ ```
72
+
73
+
74
+ ## Usage
75
+
76
+ Please note that the following notebooks are still a work in progress. There may be some mistakes in the code and some sections might move from one notebook to another.
77
+
78
+ | Notebook | Description | Level | State |
79
+ |------------------------------------------------------------------------------------------|-------------------------------------------------------------------|----------|-------|
80
+ | [Getting started](./notebooks/Getting%20started.ipynb) | The basics of Pycellin, through examples | Beginner | WIP |
81
+ | [Managing features](./notebooks/Managing%20features.ipynb) | How to add, compute and remove features from a model | Beginner | WIP |
82
+ | [Working with TrackMate data](./notebooks/Working%20with%20TrackMate%20data.ipynb) | How Pycellin can work with TrackMate, through an example | Beginner | WIP |
83
+ | [Creating a model from scratch](./notebooks/Creating%20a%20model%20from%20scratch.ipynb) | How to manually create a Pycellin model, including its lineages | Advanced | Stub |
84
+ | [Custom features](./notebooks/Custom%20features.ipynb) | How to create user-defined features and augment a model with them | Advanced | WIP |
85
+
86
+
87
+ ## Credits
88
+
89
+ - [NetworkX](https://networkx.org/) for lineages modeling ([Hagberg, Schult and Swart, 2008](http://conference.scipy.org.s3-website-us-east-1.amazonaws.com/proceedings/scipy2008/paper_2/))
90
+ - [TrackMate](https://imagej.net/plugins/trackmate/) for the TrackMate data loader and exporter ([Tinevez et al., 2017](https://doi.org/10.1016/j.ymeth.2016.09.016), [Ershov et al., 2022](https://doi:10.1038/s41592-022-01507-1))
91
+ - The [Cell Tracking Challenge](https://celltrackingchallenge.net/) for the CTC data loader and exporter ([Maška et al., 2023](https://doi.org/10.1038/s41592-023-01879-y))
@@ -0,0 +1,66 @@
1
+ from .classes.data import Data
2
+ from .classes.lineage import CellLineage, CycleLineage
3
+ from .classes.feature import FeaturesDeclaration, Feature
4
+ from .classes.feature import (
5
+ frame_Feature,
6
+ cell_ID_Feature,
7
+ lineage_ID_Feature,
8
+ cell_coord_Feature,
9
+ link_coord_Feature,
10
+ lineage_coord_Feature,
11
+ cycle_ID_Feature,
12
+ cells_Feature,
13
+ cycle_length_Feature,
14
+ level_Feature,
15
+ )
16
+ from .classes.model import Model
17
+ from .classes.feature_calculator import (
18
+ NodeLocalFeatureCalculator,
19
+ EdgeLocalFeatureCalculator,
20
+ LineageLocalFeatureCalculator,
21
+ NodeGlobalFeatureCalculator,
22
+ EdgeGlobalFeatureCalculator,
23
+ LineageGlobalFeatureCalculator,
24
+ )
25
+
26
+ from .io.cell_tracking_challenge.loader import load_CTC_file
27
+ from .io.cell_tracking_challenge.exporter import export_CTC_file
28
+ from .io.trackmate.loader import load_TrackMate_XML
29
+ from .io.trackmate.exporter import export_TrackMate_XML
30
+
31
+ from .graph.features.utils import (
32
+ get_pycellin_cell_lineage_features,
33
+ get_pycellin_cycle_lineage_features,
34
+ )
35
+
36
+
37
+ __all__ = [
38
+ "Data",
39
+ "CellLineage",
40
+ "CycleLineage",
41
+ "FeaturesDeclaration",
42
+ "Feature",
43
+ "frame_Feature",
44
+ "cell_ID_Feature",
45
+ "lineage_ID_Feature",
46
+ "cell_coord_Feature",
47
+ "link_coord_Feature",
48
+ "lineage_coord_Feature",
49
+ "cycle_ID_Feature",
50
+ "cells_Feature",
51
+ "cycle_length_Feature",
52
+ "level_Feature",
53
+ "Model",
54
+ "NodeLocalFeatureCalculator",
55
+ "EdgeLocalFeatureCalculator",
56
+ "LineageLocalFeatureCalculator",
57
+ "NodeGlobalFeatureCalculator",
58
+ "EdgeGlobalFeatureCalculator",
59
+ "LineageGlobalFeatureCalculator",
60
+ "load_CTC_file",
61
+ "export_CTC_file",
62
+ "load_TrackMate_XML",
63
+ "export_TrackMate_XML",
64
+ "get_pycellin_cell_lineage_features",
65
+ "get_pycellin_cycle_lineage_features",
66
+ ]
@@ -0,0 +1,24 @@
1
+ from .data import Data
2
+ from .lineage import CellLineage, CycleLineage
3
+ from .feature import FeaturesDeclaration, Feature
4
+ from .feature import (
5
+ frame_Feature,
6
+ cell_ID_Feature,
7
+ lineage_ID_Feature,
8
+ cell_coord_Feature,
9
+ link_coord_Feature,
10
+ lineage_coord_Feature,
11
+ cycle_ID_Feature,
12
+ cells_Feature,
13
+ cycle_length_Feature,
14
+ level_Feature,
15
+ )
16
+ from .model import Model
17
+ from .feature_calculator import (
18
+ NodeLocalFeatureCalculator,
19
+ EdgeLocalFeatureCalculator,
20
+ LineageLocalFeatureCalculator,
21
+ NodeGlobalFeatureCalculator,
22
+ EdgeGlobalFeatureCalculator,
23
+ LineageGlobalFeatureCalculator,
24
+ )
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import math
5
+ from typing import Literal
6
+ import warnings
7
+
8
+ import networkx as nx
9
+
10
+ from pycellin.classes.lineage import Lineage, CellLineage, CycleLineage
11
+
12
+
13
+ class Data:
14
+ """
15
+ Class to store and manipulate cell lineages and cell cycle lineages.
16
+ """
17
+
18
+ def __init__(
19
+ self, data: dict[str, CellLineage], add_cycle_data: bool = False
20
+ ) -> None:
21
+ self.cell_data = data
22
+ if add_cycle_data:
23
+ self._compute_cycle_lineages()
24
+ else:
25
+ self.cycle_data = None
26
+
27
+ def __repr__(self) -> str:
28
+ return f"Data(cell_data={self.cell_data!r}, cycle_data={self.cycle_data!r})"
29
+
30
+ def __str__(self) -> str:
31
+ if self.cycle_data:
32
+ txt = f" and {self.number_of_lineages()} cycle lineages"
33
+ else:
34
+ txt = ""
35
+ return f"Data object with {self.number_of_lineages()} cell lineages{txt}."
36
+
37
+ def _add_cycle_lineages(self, lineage_IDs: list[int] | None = None) -> None:
38
+ """
39
+ Add the cell cycle lineages from the cell lineages.
40
+
41
+ Parameters
42
+ ----------
43
+ lineage_IDs : list[int], optional
44
+ The IDs of the lineages to compute the cycle lineages for,
45
+ by default None i.e. all lineages.
46
+ """
47
+ if lineage_IDs is None:
48
+ lineage_IDs = list(self.cell_data.keys())
49
+ self.cycle_data = {
50
+ lin_id: self._compute_cycle_lineage(lin_id) for lin_id in lineage_IDs
51
+ }
52
+
53
+ def _compute_cycle_lineage(self, lineage_ID: int) -> CycleLineage:
54
+ """
55
+ Compute and return the cycle lineage corresponding to a given cell lineage.
56
+
57
+ Parameters
58
+ ----------
59
+ lineage_ID : int
60
+ The ID of the cell lineage.
61
+
62
+ Returns
63
+ -------
64
+ CycleLineage
65
+ The cycle lineage corresponding to the cell lineage.
66
+ """
67
+ return CycleLineage(self.cell_data[lineage_ID])
68
+
69
+ def _freeze_lineage_data(self):
70
+ """
71
+ Freeze all cell lineages.
72
+
73
+ When a cell lineage is frozen, its structure cannot be modified:
74
+ nodes and edges cannot be added or removed. However, graph, node and edge
75
+ attributes can still be modified.
76
+ """
77
+ for lineage in self.cell_data.values():
78
+ if not nx.is_frozen(lineage):
79
+ nx.freeze(lineage)
80
+
81
+ def _unfreeze_lineage_data(self):
82
+ """
83
+ Unfreeze all cell lineages.
84
+ """
85
+ for lineage in self.cell_data.values():
86
+ Lineage.unfreeze(lineage)
87
+
88
+ def number_of_lineages(self) -> int:
89
+ """
90
+ Return the number of lineages in the data.
91
+
92
+ Returns
93
+ -------
94
+ int
95
+ The number of cell lineages in the data.
96
+
97
+ Raises
98
+ ------
99
+ Warning
100
+ If the number of cell lineages and cycle lineages do not match.
101
+ """
102
+ if self.cycle_data:
103
+ if len(self.cell_data) != len(self.cycle_data):
104
+ msg = (
105
+ f"Number of cell lineages ({len(self.cell_data)}) "
106
+ f"and cycle lineages ({len(self.cycle_data)}) do not match. "
107
+ "An update of the model is required. "
108
+ )
109
+ warnings.warn(msg)
110
+ return len(self.cell_data)
111
+
112
+ def get_closest_cell(
113
+ self,
114
+ noi: int,
115
+ lineage: CellLineage,
116
+ radius: float = 0,
117
+ time_window: int = 0,
118
+ time_window_type: Literal["before", "after", "symetric"] = "symetric",
119
+ lineages_to_search: list[CellLineage] = None,
120
+ reference: Literal["center", "border"] = "center",
121
+ ) -> tuple[CellLineage, int]:
122
+ """
123
+ Find the closest cell to a given cell of a lineage.
124
+
125
+ Parameters
126
+ ----------
127
+ noi : int
128
+ Node of interest, the one for which to find the closest cell.
129
+ lineage : CellLineage
130
+ The lineage the node belongs to.
131
+ radius : float, optional
132
+ The maximum distance to consider, by default 0.
133
+ If 0, the whole space is considered.
134
+ time_window : int, optional
135
+ The time window to consider, by default 0 i.e. only the current frame.
136
+ time_window_type : Literal["before", "after", "symetric"], optional
137
+ The type of time window to consider, by default "symetric".
138
+ lineages_to_search : list[CellLineage], optional
139
+ The lineages to search in, by default None i.e. all lineages.
140
+ reference : Literal["center", "border"], optional
141
+ The reference point to consider for the distance, by default "center".
142
+
143
+ Returns
144
+ -------
145
+ tuple[int, CellLineage]
146
+ The node ID of the closest cell and the lineage it belongs to.
147
+ """
148
+ distances = self.get_closest_cells(
149
+ noi=noi,
150
+ lineage=lineage,
151
+ radius=radius,
152
+ time_window=time_window,
153
+ time_window_type=time_window_type,
154
+ lineages_to_search=lineages_to_search,
155
+ reference=reference,
156
+ )
157
+ return distances[0]
158
+
159
+ def get_closest_cells(
160
+ self,
161
+ noi: int,
162
+ lineage: CellLineage,
163
+ radius: float = 0,
164
+ time_window: int = 0,
165
+ time_window_type: Literal["before", "after", "symetric"] = "symetric",
166
+ lineages_to_search: list[CellLineage] = None,
167
+ reference: Literal["center", "border"] = "center",
168
+ ) -> list[tuple[int, CellLineage]]:
169
+ """
170
+ Find the closest cells to a given cell of a lineage.
171
+
172
+ Parameters
173
+ ----------
174
+ noi : int
175
+ Node of interest, the one for which to find the closest cell.
176
+ lineage : CellLineage
177
+ The lineage the node belongs to.
178
+ radius : float, optional
179
+ The maximum distance to consider, by default 0.
180
+ If 0, the whole space is considered.
181
+ time_window : int, optional
182
+ The time window to consider, by default 0 i.e. only the current frame.
183
+ time_window_type : Literal["before", "after", "symetric"], optional
184
+ The type of time window to consider, by default "symetric".
185
+ lineages_to_search : list[CellLineage], optional
186
+ The lineages to search in, by default None i.e. all lineages.
187
+ reference : Literal["center", "border"], optional
188
+ The reference point to consider for the distance, by default "center".
189
+
190
+ Returns
191
+ -------
192
+ tuple[int, CellLineage]
193
+ The node ID of the closest cells and the lineages it belongs to,
194
+ sorted by increasing distance.
195
+ """
196
+ # TODO: implement the reference parameter
197
+
198
+ # Identification of the frames to search in.
199
+ center_frame = lineage.nodes[noi]["frame"]
200
+ if time_window == 0:
201
+ frames_to_search = [center_frame]
202
+ else:
203
+ if time_window_type == "symetric":
204
+ frames_to_search = list(
205
+ range(center_frame - time_window, center_frame + time_window + 1)
206
+ )
207
+ elif time_window_type == "before":
208
+ frames_to_search = list(
209
+ range(center_frame - time_window, center_frame + 1)
210
+ )
211
+ elif time_window_type == "after":
212
+ frames_to_search = list(
213
+ range(center_frame, center_frame + time_window + 1)
214
+ )
215
+ else:
216
+ raise ValueError(
217
+ f"Unknown time window type: '{time_window_type}'."
218
+ " Should be 'before', 'after' or 'symetric'."
219
+ )
220
+ frames_to_search.sort()
221
+
222
+ # Identification of nodes that are good candidates,
223
+ # i.e. nodes that are in the time window
224
+ # and in the lineages to search in.
225
+ if not lineages_to_search:
226
+ lineages_to_search = list(self.cell_data.values())
227
+ candidate_cells = {}
228
+ for lin in lineages_to_search:
229
+ nodes = [
230
+ node
231
+ for node, frame in lin.nodes(data="frame")
232
+ if frame in frames_to_search
233
+ ]
234
+ if nodes:
235
+ candidate_cells[lin] = nodes
236
+ # Need to remove the node itself from the candidates.
237
+ candidate_cells[lineage].remove(noi)
238
+
239
+ # Identification of the closest cell.
240
+ distances = []
241
+ for lin, nodes in candidate_cells.items():
242
+ for node in nodes:
243
+ distance = math.dist(
244
+ lineage.nodes[noi]["location"], lin.nodes[node]["location"]
245
+ )
246
+ if radius == 0 or distance <= radius:
247
+ distances.append((node, lin, distance))
248
+ distances.sort(key=lambda x: x[2])
249
+ return [(node, lin) for node, lin, _ in distances]
250
+
251
+ # def get_neighbouring_cells(
252
+ # lineage: CellLineage,
253
+ # node: int,
254
+ # radius: float,
255
+ # time_window: int | tuple[int, int],
256
+ # ) -> list[tuple[CellLineage, int]]:
257
+ # """ """
258
+ # # TODO: implement get_neighbouring_cells()
259
+ # # Parameter to define sort order? By default closest to farthest
260
+ # # Need to implement get_distance() between 2 nodes, not necessarily
261
+ # # from the same lineage...
262
+ # # To identify a node, need to have lineage_ID and cell_ID
263
+ # pass