|
24 | 24 | }, |
25 | 25 | { |
26 | 26 | "cell_type": "code", |
27 | | - "execution_count": 19, |
| 27 | + "execution_count": 1, |
28 | 28 | "metadata": {}, |
29 | 29 | "outputs": [], |
30 | 30 | "source": [ |
|
42 | 42 | }, |
43 | 43 | { |
44 | 44 | "cell_type": "code", |
45 | | - "execution_count": 20, |
| 45 | + "execution_count": 2, |
46 | 46 | "metadata": {}, |
47 | 47 | "outputs": [ |
48 | 48 | { |
|
95 | 95 | "Where does the property come from? For Pycellin computed properties, Pycellin uses `Pycellin`. For imported properties, Pycellin uses the name of the tool the data was imported from, like `TrackMate` or `CTC`. For custom properties, you can use `custom`, your initials or whatever works for you. This field is useful for traceability (e.g. reopening a model after a long time, sharing a model with other people...).\n", |
96 | 96 | "\n", |
97 | 97 | "**prop_type** \n", |
98 | | - "The type of graph element the property applies to, either `node`, `edge`, `lineage`.\n", |
| 98 | + "The type of graph element the property applies to, either `node`, `edge`, `lineage` or a combination of those.\n", |
99 | 99 | "\n", |
100 | 100 | "**lin_type** \n", |
101 | 101 | "Either `CellLineage`, `CycleLineage` or just `Lineage` depending on which type of lineage your property is related to. See [Pycellin data structure](./Pycellin%20data%20structure.ipynb) if in doubt.\n", |
|
118 | 118 | }, |
119 | 119 | { |
120 | 120 | "cell_type": "code", |
121 | | - "execution_count": 21, |
| 121 | + "execution_count": 3, |
122 | 122 | "metadata": {}, |
123 | 123 | "outputs": [], |
124 | 124 | "source": [ |
|
143 | 143 | }, |
144 | 144 | { |
145 | 145 | "cell_type": "code", |
146 | | - "execution_count": 22, |
| 146 | + "execution_count": 4, |
147 | 147 | "metadata": {}, |
148 | 148 | "outputs": [ |
149 | 149 | { |
|
192 | 192 | }, |
193 | 193 | { |
194 | 194 | "cell_type": "code", |
195 | | - "execution_count": 23, |
| 195 | + "execution_count": 5, |
196 | 196 | "metadata": {}, |
197 | 197 | "outputs": [], |
198 | 198 | "source": [ |
199 | 199 | "class AgeCalculator(pc.NodeGlobalPropCalculator):\n", |
200 | 200 | " def compute(self, data: pc.Data, lineage: pc.CellLineage, nid: int) -> int:\n", |
201 | 201 | " root = lineage.get_root()\n", |
202 | | - " return lineage.nodes[nid][\"frame\"] - lineage.nodes[root][\"frame\"]" |
| 202 | + " # FRAME is the name of the one of the time properties in TrackMate.\n", |
| 203 | + " return lineage.nodes[nid][\"FRAME\"] - lineage.nodes[root][\"FRAME\"]" |
203 | 204 | ] |
204 | 205 | }, |
205 | 206 | { |
|
267 | 268 | }, |
268 | 269 | { |
269 | 270 | "cell_type": "code", |
270 | | - "execution_count": 24, |
| 271 | + "execution_count": 6, |
271 | 272 | "metadata": {}, |
272 | 273 | "outputs": [], |
273 | 274 | "source": [ |
|
304 | 305 | }, |
305 | 306 | { |
306 | 307 | "cell_type": "code", |
307 | | - "execution_count": 25, |
| 308 | + "execution_count": 7, |
308 | 309 | "metadata": {}, |
309 | 310 | "outputs": [], |
310 | 311 | "source": [ |
|
322 | 323 | }, |
323 | 324 | { |
324 | 325 | "cell_type": "code", |
325 | | - "execution_count": 26, |
| 326 | + "execution_count": 8, |
326 | 327 | "metadata": {}, |
327 | 328 | "outputs": [ |
328 | 329 | { |
|
2539 | 2540 | }, |
2540 | 2541 | { |
2541 | 2542 | "cell_type": "code", |
2542 | | - "execution_count": 27, |
| 2543 | + "execution_count": 9, |
2543 | 2544 | "metadata": {}, |
2544 | 2545 | "outputs": [], |
2545 | 2546 | "source": [ |
|
2577 | 2578 | }, |
2578 | 2579 | { |
2579 | 2580 | "cell_type": "code", |
2580 | | - "execution_count": 28, |
| 2581 | + "execution_count": 10, |
2581 | 2582 | "metadata": {}, |
2582 | 2583 | "outputs": [], |
2583 | 2584 | "source": [ |
|
2602 | 2603 | }, |
2603 | 2604 | { |
2604 | 2605 | "cell_type": "code", |
2605 | | - "execution_count": 29, |
| 2606 | + "execution_count": 11, |
2606 | 2607 | "metadata": { |
2607 | 2608 | "tags": [ |
2608 | 2609 | "raises-exception" |
|
2616 | 2617 | "traceback": [ |
2617 | 2618 | "\u001b[31m---------------------------------------------------------------------------\u001b[39m", |
2618 | 2619 | "\u001b[31mTypeError\u001b[39m Traceback (most recent call last)", |
2619 | | - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[29]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m 1\u001b[39m calc = ParityCalculator_incorrect(prop_incorrect)\n\u001b[32m 2\u001b[39m model.add_custom_property(calc)\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43mmodel\u001b[49m\u001b[43m.\u001b[49m\u001b[43mupdate\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", |
2620 | | - "\u001b[36mFile \u001b[39m\u001b[32m/media/lxenard/data/Code/pycellin/pycellin/pycellin/classes/model.py:910\u001b[39m, in \u001b[36mModel.update\u001b[39m\u001b[34m(self, props_to_update)\u001b[39m\n\u001b[32m 903\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m time_step \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 904\u001b[39m \u001b[38;5;66;03m# TODO: add \"see documentation\" to the error message or explain\u001b[39;00m\n\u001b[32m 905\u001b[39m \u001b[38;5;66;03m# directly how to set it?\u001b[39;00m\n\u001b[32m 906\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 907\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mThe time step of the model is currently not defined \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 908\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mbut is required for cycle lineage computation.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 909\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m910\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_updater\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_update\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 911\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 912\u001b[39m \u001b[43m \u001b[49m\u001b[43mtime_prop\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_metadata\u001b[49m\u001b[43m.\u001b[49m\u001b[43mreference_time_property\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 913\u001b[39m \u001b[43m \u001b[49m\u001b[43mtime_step\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtime_step\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 914\u001b[39m \u001b[43m \u001b[49m\u001b[43mprops_to_update\u001b[49m\u001b[43m=\u001b[49m\u001b[43mprops_to_update\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 915\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", |
| 2620 | + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 3\u001b[39m\n\u001b[32m 1\u001b[39m calc = ParityCalculator_incorrect(prop_incorrect)\n\u001b[32m 2\u001b[39m model.add_custom_property(calc)\n\u001b[32m----> \u001b[39m\u001b[32m3\u001b[39m \u001b[43mmodel\u001b[49m\u001b[43m.\u001b[49m\u001b[43mupdate\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", |
| 2621 | + "\u001b[36mFile \u001b[39m\u001b[32m/media/lxenard/data/Code/pycellin/pycellin/pycellin/classes/model.py:963\u001b[39m, in \u001b[36mModel.update\u001b[39m\u001b[34m(self, props_to_update)\u001b[39m\n\u001b[32m 956\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m time_step \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 957\u001b[39m \u001b[38;5;66;03m# TODO: add \"see documentation\" to the error message or explain\u001b[39;00m\n\u001b[32m 958\u001b[39m \u001b[38;5;66;03m# directly how to set it?\u001b[39;00m\n\u001b[32m 959\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[32m 960\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mThe time step of the model is currently not defined \u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 961\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mbut is required for cycle lineage computation.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 962\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m963\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_updater\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_update\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 964\u001b[39m \u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 965\u001b[39m \u001b[43m \u001b[49m\u001b[43mtime_prop\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mmodel_metadata\u001b[49m\u001b[43m.\u001b[49m\u001b[43mreference_time_property\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 966\u001b[39m \u001b[43m \u001b[49m\u001b[43mtime_step\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtime_step\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 967\u001b[39m \u001b[43m \u001b[49m\u001b[43mprops_to_update\u001b[49m\u001b[43m=\u001b[49m\u001b[43mprops_to_update\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 968\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", |
2621 | 2622 | "\u001b[36mFile \u001b[39m\u001b[32m/media/lxenard/data/Code/pycellin/pycellin/pycellin/classes/updater.py:280\u001b[39m, in \u001b[36mModelUpdater._update\u001b[39m\u001b[34m(self, data, time_prop, time_step, props_to_update)\u001b[39m\n\u001b[32m 276\u001b[39m \u001b[38;5;66;03m# Recompute the properties as needed.\u001b[39;00m\n\u001b[32m 277\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m calc \u001b[38;5;129;01min\u001b[39;00m cell_calculators:\n\u001b[32m 278\u001b[39m \u001b[38;5;66;03m# Depending on the class of the calculator, a different version of\u001b[39;00m\n\u001b[32m 279\u001b[39m \u001b[38;5;66;03m# the enrich() method is called.\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m280\u001b[39m \u001b[43mcalc\u001b[49m\u001b[43m.\u001b[49m\u001b[43menrich\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 281\u001b[39m \u001b[43m \u001b[49m\u001b[43mdata\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 282\u001b[39m \u001b[43m \u001b[49m\u001b[43mnodes_to_enrich\u001b[49m\u001b[43m=\u001b[49m\u001b[43mnodes_to_process\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# self._added_cells,\u001b[39;49;00m\n\u001b[32m 283\u001b[39m \u001b[43m \u001b[49m\u001b[43medges_to_enrich\u001b[49m\u001b[43m=\u001b[49m\u001b[43medges_to_process\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# self._added_links,\u001b[39;49;00m\n\u001b[32m 284\u001b[39m \u001b[43m \u001b[49m\u001b[43mlineages_to_enrich\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlins_to_process\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# self._added_lineages | self._modified_lineages\u001b[39;49;00m\n\u001b[32m 285\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 287\u001b[39m \u001b[38;5;66;03m# In case of modifications in the structure of some cell lineages,\u001b[39;00m\n\u001b[32m 288\u001b[39m \u001b[38;5;66;03m# we need to recompute the cycle lineages and their properties.\u001b[39;00m\n\u001b[32m 289\u001b[39m \u001b[38;5;66;03m# TODO: optimize so we don't have to recompute EVERYTHING for cycle lineages?\u001b[39;00m\n\u001b[32m 290\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m lin_ID \u001b[38;5;129;01min\u001b[39;00m lins_to_process:\n", |
2622 | 2623 | "\u001b[36mFile \u001b[39m\u001b[32m/media/lxenard/data/Code/pycellin/pycellin/pycellin/classes/property_calculator.py:181\u001b[39m, in \u001b[36mNodeLocalPropCalculator.enrich\u001b[39m\u001b[34m(self, data, nodes_to_enrich, **kwargs)\u001b[39m\n\u001b[32m 179\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m nid, lin_ID \u001b[38;5;129;01min\u001b[39;00m nodes_to_enrich:\n\u001b[32m 180\u001b[39m lin = lineages[lin_ID]\n\u001b[32m--> \u001b[39m\u001b[32m181\u001b[39m lin.nodes[nid][\u001b[38;5;28mself\u001b[39m.prop.identifier] = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mcompute\u001b[49m\u001b[43m(\u001b[49m\u001b[43mlin\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mnid\u001b[49m\u001b[43m)\u001b[49m\n", |
2623 | 2624 | "\u001b[31mTypeError\u001b[39m: ParityCalculator_incorrect.compute() takes 2 positional arguments but 3 were given" |
|
2646 | 2647 | }, |
2647 | 2648 | { |
2648 | 2649 | "cell_type": "code", |
2649 | | - "execution_count": 30, |
| 2650 | + "execution_count": 12, |
2650 | 2651 | "metadata": {}, |
2651 | 2652 | "outputs": [], |
2652 | 2653 | "source": [ |
|
2672 | 2673 | }, |
2673 | 2674 | { |
2674 | 2675 | "cell_type": "code", |
2675 | | - "execution_count": 31, |
| 2676 | + "execution_count": 13, |
2676 | 2677 | "metadata": {}, |
2677 | 2678 | "outputs": [ |
2678 | 2679 | { |
|
2688 | 2689 | "calc = ParityCalculator_correct(prop_correct)\n", |
2689 | 2690 | "model.add_custom_property(calc)\n", |
2690 | 2691 | "\n", |
2691 | | - "# We clean the model by removing the improper property,\n", |
| 2692 | + "# We clean the model by removing the incorrect property,\n", |
2692 | 2693 | "# otherwise we won't be able to update the model.\n", |
2693 | 2694 | "model.remove_property(\"node_ID_parity_incorrect\")\n", |
2694 | 2695 | "model.update()\n", |
|
2735 | 2736 | }, |
2736 | 2737 | { |
2737 | 2738 | "cell_type": "code", |
2738 | | - "execution_count": 32, |
| 2739 | + "execution_count": 14, |
2739 | 2740 | "metadata": {}, |
2740 | 2741 | "outputs": [], |
2741 | 2742 | "source": [ |
|
2747 | 2748 | " prop_type=\"node\",\n", |
2748 | 2749 | " lin_type=\"CellLineage\",\n", |
2749 | 2750 | " dtype=\"float\",\n", |
2750 | | - " unit=model.get_time_step(),\n", |
| 2751 | + " unit=model.get_time_unit(),\n", |
2751 | 2752 | ")\n", |
2752 | 2753 | "\n", |
2753 | 2754 | "\n", |
2754 | 2755 | "class MyRelativeAgeCalculator(pc.NodeGlobalPropCalculator):\n", |
2755 | | - " def __init__(self, property: pc.Property, time_step: float):\n", |
| 2756 | + " def __init__(self, property: pc.Property, time_prop_name: float):\n", |
2756 | 2757 | " # Call the parent __init__ method.\n", |
2757 | 2758 | " super().__init__(property)\n", |
2758 | 2759 | " # Do whatever you want with the additional argument(s).\n", |
2759 | 2760 | " # Usually you store them in the object to be able to use them\n", |
2760 | 2761 | " # in the `compute` method.\n", |
2761 | | - " self.time_step = time_step\n", |
| 2762 | + " self.time_prop_name = time_prop_name\n", |
2762 | 2763 | "\n", |
2763 | 2764 | " def compute(self, data: pc.Data, lineage: pc.CellLineage, nid: int):\n", |
2764 | 2765 | " # As said before, the signature of the `compute` method must be respected,\n", |
2765 | 2766 | " # even if we don't use the `data` argument here.\n", |
2766 | 2767 | " first_cell = lineage.get_cell_cycle(nid)[0]\n", |
2767 | | - " first_cell_frame = lineage.nodes[first_cell][\"frame\"]\n", |
2768 | | - " current_cell_frame = lineage.nodes[nid][\"frame\"]\n", |
2769 | | - " age_in_frame = current_cell_frame - first_cell_frame\n", |
2770 | | - " # Here we use the additional time step argument.\n", |
2771 | | - " age_in_time = age_in_frame * self.time_step\n", |
2772 | | - " return age_in_time" |
| 2768 | + " # We use the additional time_prop_name argument so that this method can work\n", |
| 2769 | + " # with any time property, including the reference time property of the model.\n", |
| 2770 | + " first_cell_time = lineage.nodes[first_cell][self.time_prop_name]\n", |
| 2771 | + " current_cell_time = lineage.nodes[nid][self.time_prop_name]\n", |
| 2772 | + " return current_cell_time - first_cell_time" |
2773 | 2773 | ] |
2774 | 2774 | }, |
2775 | 2775 | { |
|
2781 | 2781 | }, |
2782 | 2782 | { |
2783 | 2783 | "cell_type": "code", |
2784 | | - "execution_count": 33, |
| 2784 | + "execution_count": 15, |
2785 | 2785 | "metadata": {}, |
2786 | 2786 | "outputs": [], |
2787 | 2787 | "source": [ |
2788 | | - "calc = MyRelativeAgeCalculator(age_prop2, model.get_time_step())\n", |
| 2788 | + "calc = MyRelativeAgeCalculator(age_prop2, model.reference_time_property)\n", |
2789 | 2789 | "model.add_custom_property(calc)" |
2790 | 2790 | ] |
2791 | 2791 | }, |
2792 | 2792 | { |
2793 | 2793 | "cell_type": "code", |
2794 | | - "execution_count": 34, |
| 2794 | + "execution_count": 16, |
2795 | 2795 | "metadata": {}, |
2796 | 2796 | "outputs": [ |
2797 | 2797 | { |
|
5001 | 5001 | }, |
5002 | 5002 | { |
5003 | 5003 | "cell_type": "code", |
5004 | | - "execution_count": 35, |
| 5004 | + "execution_count": 17, |
5005 | 5005 | "metadata": {}, |
5006 | 5006 | "outputs": [], |
5007 | 5007 | "source": [ |
|
5036 | 5036 | }, |
5037 | 5037 | { |
5038 | 5038 | "cell_type": "code", |
5039 | | - "execution_count": 36, |
| 5039 | + "execution_count": 18, |
5040 | 5040 | "metadata": {}, |
5041 | 5041 | "outputs": [ |
5042 | 5042 | { |
|
0 commit comments