swcgeom 0.17.1__tar.gz → 0.17.2__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.
Potentially problematic release.
This version of swcgeom might be problematic. Click here for more details.
- {swcgeom-0.17.1 → swcgeom-0.17.2}/CHANGELOG.md +20 -0
- {swcgeom-0.17.1/swcgeom.egg-info → swcgeom-0.17.2}/PKG-INFO +2 -2
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/pytorch/branch_dataset.py +1 -1
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/pytorch/tree_folder_dataset.py +1 -1
- {swcgeom-0.17.1 → swcgeom-0.17.2}/pyproject.toml +1 -1
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/_version.py +2 -2
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/feature_extractor.py +12 -3
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/features.py +16 -4
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/lmeasure.py +28 -7
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/sholl.py +6 -24
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/node.py +15 -1
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/path.py +3 -7
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/population.py +28 -3
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/checker.py +3 -11
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/tree.py +22 -9
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/tree_utils.py +2 -7
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/images/folder.py +2 -10
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/images/io.py +2 -5
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/image_stack.py +14 -3
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/images.py +2 -10
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/tree.py +30 -18
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/download.py +44 -21
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/sdf.py +2 -5
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/transforms.py +1 -1
- {swcgeom-0.17.1 → swcgeom-0.17.2/swcgeom.egg-info}/PKG-INFO +2 -2
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom.egg-info/requires.txt +1 -1
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.github/workflows/build.yml +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.github/workflows/github-publish.yml +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.github/workflows/pypi-publish.yml +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.github/workflows/test.yml +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.gitignore +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.pylintrc +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/.vscode/settings.json +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/LICENSE +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/README.md +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/Branch.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/BranchTree.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/CollectTips.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/CutTree.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/Features.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/GeometryTransform.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/ImageStack.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/MST.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/SpectralClustering.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/Tree.ipynb +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/data/101711-10_4p5-of-16_initial.CNG.swc +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/data/101711-11_16-of-16_initial.CNG.swc +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/data/1059283677_15257_2226-X16029-Y23953.swc +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/data/toydata.swc +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/dgl/graph.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/examples/pytorch/branch.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/git-conventional-commits.yaml +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/setup.cfg +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/trunk.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/visualization.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/visualization3d.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/analysis/volume.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/branch.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/branch_tree.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/compartment.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/assembler.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/base.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/io.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/normalizer.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/swc_utils/subtree.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/core/tree_utils_impl.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/images/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/images/augmentation.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/images/contrast.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/base.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/branch.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/geometry.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/image_preprocess.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/mst.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/neurolucida_asc.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/path.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/population.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/transforms/tree_assembler.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/debug.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/dsu.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/ellipse.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/file.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/neuromorpho.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/numpy_helper.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/plotter_2d.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/plotter_3d.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/renderer.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/solid_geometry.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom/utils/volumetric_object.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom.egg-info/SOURCES.txt +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom.egg-info/dependency_links.txt +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/swcgeom.egg-info/top_level.txt +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/analysis/test_volume.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/transforms/test_neurolucida_asc.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/__init__.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/test_dsu.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/test_numpy_helper.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/test_sdf.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/test_solid_geometry.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/test_transforms.py +0 -0
- {swcgeom-0.17.1 → swcgeom-0.17.2}/tests/utils/test_volumetric_object.py +0 -0
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## **0.17.2** <sub><sup>2024-08-07 ([9d5347d...59a5cbf](https://github.com/yzx9/swcgeom/compare/9d5347d0a427f460b92fd86c596e40a23812e48d...59a5cbfbe78f18a95ff8f35e72a4a0f0cdbc5009?diff=split))</sup></sub>
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
#####  `analysis`
|
|
8
|
+
|
|
9
|
+
- handle zero partition asymmetry in lmeasure ([1f332f2](https://github.com/yzx9/swcgeom/commit/1f332f22f465ea1ec7ff0b5e4013ddb6d64ffa5d))
|
|
10
|
+
- workaround with angle ([05d2ff7](https://github.com/yzx9/swcgeom/commit/05d2ff77ba5f4b4331daca0bade89e10a56f564d))
|
|
11
|
+
|
|
12
|
+
#####  `utils`
|
|
13
|
+
|
|
14
|
+
- download with multiprocessing should avoid locals func ([6aff3e0](https://github.com/yzx9/swcgeom/commit/6aff3e0ee06fb527f759e6ec9ac09ab1ca745dad))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### BREAKING CHANGES
|
|
18
|
+
- rename bifurcation to furcation ([26e9dec](https://github.com/yzx9/swcgeom/commit/26e9dec2db5b7d457dfe52d9a8e0177d26bc5b07))
|
|
19
|
+
- `core` lazy loading in range indexing ([7d38ea8](https://github.com/yzx9/swcgeom/commit/7d38ea80fefc907c3f55663c052db9b81f144d68))
|
|
20
|
+
<br>
|
|
21
|
+
|
|
22
|
+
|
|
3
23
|
## **0.17.1** <sub><sup>2024-04-05 ([a8007ac...0ef3c2b](https://github.com/yzx9/swcgeom/compare/a8007ac6c5eb7de03298dbdcc6be59d3c282e125...0ef3c2b370a570366a79ef456c1125d12f006409?diff=split))</sup></sub>
|
|
4
24
|
|
|
5
25
|
### Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: swcgeom
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.2
|
|
4
4
|
Summary: Neuron geometry library for swc format
|
|
5
5
|
Author-email: yzx9 <yuan.zx@outlook.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -20,7 +20,7 @@ Requires-Dist: seaborn>=0.12.0
|
|
|
20
20
|
Requires-Dist: tifffile>=2022.8.12
|
|
21
21
|
Requires-Dist: typing_extensions>=4.4.0
|
|
22
22
|
Requires-Dist: tqdm>=4.46.1
|
|
23
|
-
Requires-Dist: v3d-py-helper
|
|
23
|
+
Requires-Dist: v3d-py-helper==0.1.0
|
|
24
24
|
Provides-Extra: all
|
|
25
25
|
Requires-Dist: beautifulsoup4>=4.11.1; extra == "all"
|
|
26
26
|
Requires-Dist: certifi>=2023.5.7; extra == "all"
|
|
@@ -41,7 +41,7 @@ class BranchDataset(torch.utils.data.Dataset, Generic[T]):
|
|
|
41
41
|
save : Union[str, bool], default `True`
|
|
42
42
|
Save branch data to file if not False. If `True`, automatically
|
|
43
43
|
generate file name.
|
|
44
|
-
|
|
44
|
+
transforms : Transforms[Branch, T], optional
|
|
45
45
|
Branch transformations.
|
|
46
46
|
|
|
47
47
|
See Also
|
|
@@ -19,8 +19,8 @@ import seaborn as sns
|
|
|
19
19
|
from matplotlib.axes import Axes
|
|
20
20
|
|
|
21
21
|
from swcgeom.analysis.features import (
|
|
22
|
-
BifurcationFeatures,
|
|
23
22
|
BranchFeatures,
|
|
23
|
+
FurcationFeatures,
|
|
24
24
|
NodeFeatures,
|
|
25
25
|
PathFeatures,
|
|
26
26
|
TipFeatures,
|
|
@@ -40,6 +40,9 @@ Feature = Literal[
|
|
|
40
40
|
"node_count",
|
|
41
41
|
"node_radial_distance",
|
|
42
42
|
"node_branch_order",
|
|
43
|
+
# furcation nodes
|
|
44
|
+
"furcation_count",
|
|
45
|
+
"furcation_radial_distance",
|
|
43
46
|
# bifurcation nodes
|
|
44
47
|
"bifurcation_count",
|
|
45
48
|
"bifurcation_radial_distance",
|
|
@@ -57,7 +60,7 @@ Feature = Literal[
|
|
|
57
60
|
NDArrayf32 = npt.NDArray[np.float32]
|
|
58
61
|
FeatAndKwargs = Feature | tuple[Feature, dict[str, Any]]
|
|
59
62
|
|
|
60
|
-
Feature1D = set(["length", "volume", "node_count", "
|
|
63
|
+
Feature1D = set(["length", "volume", "node_count", "furcation_count", "tip_count"])
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
class Features:
|
|
@@ -70,7 +73,7 @@ class Features:
|
|
|
70
73
|
@cached_property
|
|
71
74
|
def node_features(self) -> NodeFeatures: return NodeFeatures(self.tree)
|
|
72
75
|
@cached_property
|
|
73
|
-
def
|
|
76
|
+
def furcation_features(self) -> FurcationFeatures: return FurcationFeatures(self.node_features)
|
|
74
77
|
@cached_property
|
|
75
78
|
def tip_features(self) -> TipFeatures: return TipFeatures(self.node_features)
|
|
76
79
|
@cached_property
|
|
@@ -214,6 +217,12 @@ class FeatureExtractor(ABC):
|
|
|
214
217
|
) -> Axes:
|
|
215
218
|
raise NotImplementedError()
|
|
216
219
|
|
|
220
|
+
def get_bifurcation_count(self, **kwargs):
|
|
221
|
+
raise DeprecationWarning("Use `furcation_count` instead.")
|
|
222
|
+
|
|
223
|
+
def get_bifurcation_radial_distance(self, **kwargs):
|
|
224
|
+
raise DeprecationWarning("Use `furcation_radial_distance` instead.")
|
|
225
|
+
|
|
217
226
|
|
|
218
227
|
class TreeFeatureExtractor(FeatureExtractor):
|
|
219
228
|
"""Extract feature from tree."""
|
|
@@ -6,7 +6,7 @@ from typing import TypeVar
|
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
8
|
import numpy.typing as npt
|
|
9
|
-
from typing_extensions import Self
|
|
9
|
+
from typing_extensions import Self, deprecated
|
|
10
10
|
|
|
11
11
|
from swcgeom.core import Branch, BranchTree, Tree
|
|
12
12
|
|
|
@@ -121,12 +121,24 @@ class _SubsetNodesFeatures(ABC):
|
|
|
121
121
|
return cls(NodeFeatures(tree))
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
class
|
|
125
|
-
"""Evaluate
|
|
124
|
+
class FurcationFeatures(_SubsetNodesFeatures):
|
|
125
|
+
"""Evaluate furcation node feature of tree."""
|
|
126
126
|
|
|
127
127
|
@cached_property
|
|
128
128
|
def nodes(self) -> npt.NDArray[np.bool_]:
|
|
129
|
-
return np.array([n.
|
|
129
|
+
return np.array([n.is_furcation() for n in self._features.tree])
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@deprecated("Use FurcationFeatures instead")
|
|
133
|
+
class BifurcationFeatures(FurcationFeatures):
|
|
134
|
+
"""Evaluate bifurcation node feature of tree.
|
|
135
|
+
|
|
136
|
+
Notes
|
|
137
|
+
-----
|
|
138
|
+
Deprecated due to the wrong spelling of furcation. For now, it
|
|
139
|
+
is just an alias of `FurcationFeatures` and raise a warning. It
|
|
140
|
+
will be change to raise an error in the future.
|
|
141
|
+
"""
|
|
130
142
|
|
|
131
143
|
|
|
132
144
|
class TipFeatures(_SubsetNodesFeatures):
|
|
@@ -5,9 +5,7 @@ from typing import Literal
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import numpy.typing as npt
|
|
8
|
-
|
|
9
8
|
from swcgeom.core import Branch, Compartment, Node, Tree
|
|
10
|
-
from swcgeom.utils import angle
|
|
11
9
|
|
|
12
10
|
__all__ = ["LMeasure"]
|
|
13
11
|
|
|
@@ -69,7 +67,7 @@ class LMeasure:
|
|
|
69
67
|
--------
|
|
70
68
|
L-Measure: http://cng.gmu.edu:8080/Lm/help/N_bifs.htm
|
|
71
69
|
"""
|
|
72
|
-
return len(tree.
|
|
70
|
+
return len(tree.get_furcations())
|
|
73
71
|
|
|
74
72
|
def n_branch(self, tree: Tree) -> int:
|
|
75
73
|
"""Number of branches.
|
|
@@ -163,12 +161,15 @@ class LMeasure:
|
|
|
163
161
|
--------
|
|
164
162
|
L-Measure: http://cng.gmu.edu:8080/Lm/help/Partition_asymmetry.htm
|
|
165
163
|
"""
|
|
164
|
+
|
|
166
165
|
children = n.children()
|
|
167
166
|
assert (
|
|
168
167
|
len(children) == 2
|
|
169
168
|
), "Partition asymmetry is only defined for bifurcations"
|
|
170
169
|
n1 = len(children[0].subtree().get_tips())
|
|
171
170
|
n2 = len(children[1].subtree().get_tips())
|
|
171
|
+
if n1 == n2:
|
|
172
|
+
return 0
|
|
172
173
|
return abs(n1 - n2) / (n1 + n2 - 2)
|
|
173
174
|
|
|
174
175
|
def fractal_dim(self):
|
|
@@ -336,7 +337,7 @@ class LMeasure:
|
|
|
336
337
|
return (da**rall_power + db**rall_power) / dp**rall_power
|
|
337
338
|
|
|
338
339
|
def bif_ampl_local(self, bif: Tree.Node) -> float:
|
|
339
|
-
"""
|
|
340
|
+
"""Bifurcation angle.
|
|
340
341
|
|
|
341
342
|
Given a bifurcation, this function returns the angle between
|
|
342
343
|
the first two compartments (in degree).
|
|
@@ -350,7 +351,7 @@ class LMeasure:
|
|
|
350
351
|
return np.degrees(angle(v1, v2))
|
|
351
352
|
|
|
352
353
|
def bif_ampl_remote(self, bif: Tree.Node) -> float:
|
|
353
|
-
"""
|
|
354
|
+
"""Bifurcation angle.
|
|
354
355
|
|
|
355
356
|
This function returns the angle between two bifurcation points
|
|
356
357
|
or between bifurcation point and terminal point or between two
|
|
@@ -361,7 +362,7 @@ class LMeasure:
|
|
|
361
362
|
L-Measure: http://cng.gmu.edu:8080/Lm/help/Bif_ampl_remote.htm
|
|
362
363
|
"""
|
|
363
364
|
|
|
364
|
-
v1, v2 = self.
|
|
365
|
+
v1, v2 = self._bif_vector_remote(bif)
|
|
365
366
|
return np.degrees(angle(v1, v2))
|
|
366
367
|
|
|
367
368
|
def bif_tilt_local(self, bif: Tree.Node) -> float:
|
|
@@ -665,7 +666,7 @@ class LMeasure:
|
|
|
665
666
|
n = node
|
|
666
667
|
order = 0
|
|
667
668
|
while n is not None:
|
|
668
|
-
if n.
|
|
669
|
+
if n.is_furcation():
|
|
669
670
|
order += 1
|
|
670
671
|
n = n.parent()
|
|
671
672
|
return order
|
|
@@ -819,3 +820,23 @@ def pill_surface_area(ra: float, rb: float, h: float) -> float:
|
|
|
819
820
|
bottom_hemisphere_area = 2 * math.pi * rb**2
|
|
820
821
|
total_area = lateral_area + top_hemisphere_area + bottom_hemisphere_area
|
|
821
822
|
return total_area
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
# TODO: move to `utils`
|
|
826
|
+
def angle(a: npt.ArrayLike, b: npt.ArrayLike) -> float:
|
|
827
|
+
"""Get the angle of vectors.
|
|
828
|
+
|
|
829
|
+
Returns
|
|
830
|
+
-------
|
|
831
|
+
angle : float
|
|
832
|
+
Angle [0, PI) in radians.
|
|
833
|
+
"""
|
|
834
|
+
|
|
835
|
+
a, b = np.array(a), np.array(b)
|
|
836
|
+
if np.linalg.norm(a) == 0 or np.linalg.norm(b) == 0:
|
|
837
|
+
raise ValueError("Input vectors must not be zero vectors.")
|
|
838
|
+
|
|
839
|
+
costheta = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
|
|
840
|
+
costheta = np.clip(costheta, -1, 1) # avoid numerical errors
|
|
841
|
+
theta = np.arccos(costheta)
|
|
842
|
+
return theta
|
|
@@ -8,6 +8,7 @@ import numpy.typing as npt
|
|
|
8
8
|
import seaborn as sns
|
|
9
9
|
from matplotlib.axes import Axes
|
|
10
10
|
from matplotlib.figure import Figure
|
|
11
|
+
from typing_extensions import deprecated
|
|
11
12
|
|
|
12
13
|
from swcgeom.analysis.visualization import draw
|
|
13
14
|
from swcgeom.core import Tree
|
|
@@ -160,20 +161,17 @@ class Sholl:
|
|
|
160
161
|
|
|
161
162
|
return self.get_rs(self.rmax, steps)
|
|
162
163
|
|
|
164
|
+
@deprecated("Use `Sholl.get(x)` instead")
|
|
163
165
|
def get_count(self) -> npt.NDArray[np.int32]:
|
|
164
166
|
"""Get the count of intersection.
|
|
165
167
|
|
|
166
168
|
.. deprecated:: 0.5.0
|
|
167
|
-
Use :meth:`Sholl.get` instead.
|
|
169
|
+
Use :meth:`Sholl(x).get()` instead.
|
|
168
170
|
"""
|
|
169
171
|
|
|
170
|
-
warnings.warn(
|
|
171
|
-
"`Sholl.get_count` has been renamed to `get` since v0.5.0, "
|
|
172
|
-
"and will be removed in next version",
|
|
173
|
-
DeprecationWarning,
|
|
174
|
-
)
|
|
175
172
|
return self.get().astype(np.int32)
|
|
176
173
|
|
|
174
|
+
@deprecated("Use `Shool(x).get().mean()` instead")
|
|
177
175
|
def avg(self) -> float:
|
|
178
176
|
"""Get the average of the count of intersection.
|
|
179
177
|
|
|
@@ -181,14 +179,9 @@ class Sholl:
|
|
|
181
179
|
Use :meth:`Shool(x).get().mean()` instead.
|
|
182
180
|
"""
|
|
183
181
|
|
|
184
|
-
warnings.warn(
|
|
185
|
-
"`Sholl.avg` has been deprecated since v0.6.0 and will be "
|
|
186
|
-
"removed in next version, use `Shool(x).get().mean()` "
|
|
187
|
-
"instead",
|
|
188
|
-
DeprecationWarning,
|
|
189
|
-
)
|
|
190
182
|
return self.get().mean()
|
|
191
183
|
|
|
184
|
+
@deprecated("Use `Shool(x).get().std()` instead")
|
|
192
185
|
def std(self) -> float:
|
|
193
186
|
"""Get the std of the count of intersection.
|
|
194
187
|
|
|
@@ -196,14 +189,9 @@ class Sholl:
|
|
|
196
189
|
Use :meth:`Shool(x).get().std()` instead.
|
|
197
190
|
"""
|
|
198
191
|
|
|
199
|
-
warnings.warn(
|
|
200
|
-
"`Sholl.std` has been deprecate since v0.6.0 and will be "
|
|
201
|
-
"removed in next version, use `Shool(x).get().std()` "
|
|
202
|
-
"instead",
|
|
203
|
-
DeprecationWarning,
|
|
204
|
-
)
|
|
205
192
|
return self.get().std()
|
|
206
193
|
|
|
194
|
+
@deprecated("Use `Shool(x).get().sum()` instead")
|
|
207
195
|
def sum(self) -> int:
|
|
208
196
|
"""Get the sum of the count of intersection.
|
|
209
197
|
|
|
@@ -211,10 +199,4 @@ class Sholl:
|
|
|
211
199
|
Use :meth:`Shool(x).get().sum()` instead.
|
|
212
200
|
"""
|
|
213
201
|
|
|
214
|
-
warnings.warn(
|
|
215
|
-
"`Sholl.sum` has been deprecate since v0.6.0 and will be "
|
|
216
|
-
"removed in next version, use `Shool(x).get().sum()` "
|
|
217
|
-
"instead",
|
|
218
|
-
DeprecationWarning,
|
|
219
|
-
)
|
|
220
202
|
return self.get().sum()
|
|
@@ -5,6 +5,7 @@ from typing import Any, Generic
|
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import numpy.typing as npt
|
|
8
|
+
from typing_extensions import deprecated
|
|
8
9
|
|
|
9
10
|
from swcgeom.core.swc import DictSWC, SWCTypeVar
|
|
10
11
|
from swcgeom.core.swc_utils import SWCNames
|
|
@@ -95,9 +96,22 @@ class Node(Generic[SWCTypeVar]):
|
|
|
95
96
|
items = [self.id, self.type, x, y, z, r, self.pid]
|
|
96
97
|
return " ".join(map(str, items))
|
|
97
98
|
|
|
98
|
-
def
|
|
99
|
+
def is_furcation(self) -> bool:
|
|
100
|
+
"""Is furcation node."""
|
|
99
101
|
return np.count_nonzero(self.attach.pid() == self.id) > 1
|
|
100
102
|
|
|
103
|
+
@deprecated("Use is_furcation instead")
|
|
104
|
+
def is_bifurcation(self) -> bool:
|
|
105
|
+
"""Is furcation node.
|
|
106
|
+
|
|
107
|
+
Notes
|
|
108
|
+
-----
|
|
109
|
+
Deprecated due to the wrong spelling of furcation. For now, it
|
|
110
|
+
is just an alias of `is_furcation` and raise a warning. It will
|
|
111
|
+
be change to raise an error in the future.
|
|
112
|
+
"""
|
|
113
|
+
return self.is_furcation()
|
|
114
|
+
|
|
101
115
|
def is_tip(self) -> bool:
|
|
102
116
|
return self.id not in self.attach.pid()
|
|
103
117
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Nueron path."""
|
|
2
2
|
|
|
3
|
-
import warnings
|
|
4
3
|
from collections.abc import Iterable, Iterator
|
|
5
4
|
from typing import Generic, overload
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
import numpy.typing as npt
|
|
8
|
+
from typing_extensions import deprecated
|
|
9
9
|
|
|
10
10
|
from swcgeom.core.node import Node
|
|
11
11
|
from swcgeom.core.swc import DictSWC, SWCLike, SWCTypeVar
|
|
@@ -16,7 +16,7 @@ __all__ = ["Path"]
|
|
|
16
16
|
class Path(SWCLike, Generic[SWCTypeVar]):
|
|
17
17
|
"""Neuron path.
|
|
18
18
|
|
|
19
|
-
A path is a linear set of points without
|
|
19
|
+
A path is a linear set of points without furcations.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
attach: SWCTypeVar
|
|
@@ -75,6 +75,7 @@ class Path(SWCLike, Generic[SWCTypeVar]):
|
|
|
75
75
|
def get_ndata(self, key: str) -> npt.NDArray:
|
|
76
76
|
return self.attach.get_ndata(key)[self.idx]
|
|
77
77
|
|
|
78
|
+
@deprecated("Use `path.node` instead.")
|
|
78
79
|
def get_node(self, idx: int | np.integer) -> Node:
|
|
79
80
|
"""Get the count of intersection.
|
|
80
81
|
|
|
@@ -82,11 +83,6 @@ class Path(SWCLike, Generic[SWCTypeVar]):
|
|
|
82
83
|
Use :meth:`path.node` instead.
|
|
83
84
|
"""
|
|
84
85
|
|
|
85
|
-
warnings.warn(
|
|
86
|
-
"`Path.get_node` has been deprecated since v0.16.0 and "
|
|
87
|
-
"will be removed in future version",
|
|
88
|
-
DeprecationWarning,
|
|
89
|
-
)
|
|
90
86
|
return self.node(idx)
|
|
91
87
|
|
|
92
88
|
def node(self, idx: int | np.integer) -> Node:
|
|
@@ -97,6 +97,19 @@ class ChainTrees:
|
|
|
97
97
|
return (self[i] for i in range(self.__len__()))
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
class NestTrees:
|
|
101
|
+
def __init__(self, trees: Trees, idx: Iterable[int], /) -> None:
|
|
102
|
+
super().__init__()
|
|
103
|
+
self.trees = trees
|
|
104
|
+
self.idx = list(idx)
|
|
105
|
+
|
|
106
|
+
def __getitem__(self, key: int, /) -> Tree:
|
|
107
|
+
return self.trees[self.idx[key]]
|
|
108
|
+
|
|
109
|
+
def __len__(self) -> int:
|
|
110
|
+
return len(self.idx)
|
|
111
|
+
|
|
112
|
+
|
|
100
113
|
class Population:
|
|
101
114
|
"""Neuron population."""
|
|
102
115
|
|
|
@@ -135,13 +148,14 @@ class Population:
|
|
|
135
148
|
|
|
136
149
|
# fmt:off
|
|
137
150
|
@overload
|
|
138
|
-
def __getitem__(self, key: slice) ->
|
|
151
|
+
def __getitem__(self, key: slice) -> Trees: ...
|
|
139
152
|
@overload
|
|
140
153
|
def __getitem__(self, key: int) -> Tree: ...
|
|
141
154
|
# fmt:on
|
|
142
|
-
def __getitem__(self, key):
|
|
155
|
+
def __getitem__(self, key: int | slice):
|
|
143
156
|
if isinstance(key, slice):
|
|
144
|
-
|
|
157
|
+
trees = NestTrees(self.trees, range(*key.indices(len(self))))
|
|
158
|
+
return cast(Trees, trees)
|
|
145
159
|
|
|
146
160
|
if isinstance(key, (int, np.integer)):
|
|
147
161
|
return cast(Tree, self.trees[int(key)])
|
|
@@ -328,3 +342,14 @@ def _get_idx(key: int, length: int) -> int:
|
|
|
328
342
|
key += length
|
|
329
343
|
|
|
330
344
|
return key
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
# experimental
|
|
348
|
+
def filter_population(
|
|
349
|
+
pop: Population, predicate: Callable[[Tree], bool]
|
|
350
|
+
) -> "Population":
|
|
351
|
+
"""Filter trees in the population."""
|
|
352
|
+
|
|
353
|
+
# TODO: how to avoid load trees
|
|
354
|
+
idx = [i for i, t in enumerate(pop) if predicate(t)]
|
|
355
|
+
return Population(NestTrees(pop.trees, idx), root=pop.root)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Check common """
|
|
2
2
|
|
|
3
|
-
import warnings
|
|
4
3
|
from collections import defaultdict
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
7
6
|
import numpy as np
|
|
8
7
|
import pandas as pd
|
|
8
|
+
from typing_extensions import deprecated
|
|
9
9
|
|
|
10
10
|
from swcgeom.core.swc_utils.base import SWCNames, Topology, get_dsu, get_names, traverse
|
|
11
11
|
from swcgeom.utils import DisjointSetUnion
|
|
@@ -81,6 +81,7 @@ def has_cyclic(topology: Topology) -> bool:
|
|
|
81
81
|
return False
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
@deprecated("Use `is_single_root` instead")
|
|
84
85
|
def check_single_root(*args, **kwargs) -> bool:
|
|
85
86
|
"""Check if the tree is single root.
|
|
86
87
|
|
|
@@ -88,14 +89,10 @@ def check_single_root(*args, **kwargs) -> bool:
|
|
|
88
89
|
Use :meth:`is_single_root` instead.
|
|
89
90
|
"""
|
|
90
91
|
|
|
91
|
-
warnings.warn(
|
|
92
|
-
"`check_single_root` has been renamed to `is_single_root` since"
|
|
93
|
-
"v0.5.0, and will be removed in next version",
|
|
94
|
-
DeprecationWarning,
|
|
95
|
-
)
|
|
96
92
|
return is_single_root(*args, **kwargs)
|
|
97
93
|
|
|
98
94
|
|
|
95
|
+
@deprecated("Use `is_bifurcate` instead")
|
|
99
96
|
def is_binary_tree(
|
|
100
97
|
df: pd.DataFrame, exclude_root: bool = True, *, names: Optional[SWCNames] = None
|
|
101
98
|
) -> bool:
|
|
@@ -105,11 +102,6 @@ def is_binary_tree(
|
|
|
105
102
|
Use :meth:`is_bifurcate` instead.
|
|
106
103
|
"""
|
|
107
104
|
|
|
108
|
-
warnings.warn(
|
|
109
|
-
"`is_binary_tree` has been replaced by to `is_bifurcate` since"
|
|
110
|
-
"v0.8.0, and will be removed in next version",
|
|
111
|
-
DeprecationWarning,
|
|
112
|
-
)
|
|
113
105
|
names = get_names(names)
|
|
114
106
|
topo = (df[names.id].to_numpy(), df[names.pid].to_numpy())
|
|
115
107
|
return is_bifurcate(topo, exclude_root=exclude_root)
|
|
@@ -8,6 +8,7 @@ from typing import Literal, Optional, TypeVar, Union, overload
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import numpy.typing as npt
|
|
10
10
|
import pandas as pd
|
|
11
|
+
from typing_extensions import deprecated
|
|
11
12
|
|
|
12
13
|
from swcgeom.core.branch import Branch
|
|
13
14
|
from swcgeom.core.compartment import Compartment, Compartments
|
|
@@ -38,11 +39,11 @@ class Tree(DictSWC):
|
|
|
38
39
|
|
|
39
40
|
def branch(self) -> "Tree.Branch":
|
|
40
41
|
ns: list["Tree.Node"] = [self]
|
|
41
|
-
while not ns[-1].
|
|
42
|
+
while not ns[-1].is_furcation() and (p := ns[-1].parent()) is not None:
|
|
42
43
|
ns.append(p)
|
|
43
44
|
|
|
44
45
|
ns.reverse()
|
|
45
|
-
while not (ns[-1].
|
|
46
|
+
while not (ns[-1].is_furcation() or ns[-1].is_tip()):
|
|
46
47
|
ns.append(ns[-1].children()[0])
|
|
47
48
|
|
|
48
49
|
return Tree.Branch(self.attach, [n.id for n in ns])
|
|
@@ -187,16 +188,28 @@ class Tree(DictSWC):
|
|
|
187
188
|
raise ValueError(f"no soma found in: {self.source}")
|
|
188
189
|
return n
|
|
189
190
|
|
|
190
|
-
def
|
|
191
|
-
"""Get all node of
|
|
192
|
-
|
|
191
|
+
def get_furcations(self) -> list[Node]:
|
|
192
|
+
"""Get all node of furcations."""
|
|
193
|
+
furcations: list[int] = []
|
|
193
194
|
|
|
194
|
-
def
|
|
195
|
+
def collect_furcations(n: Tree.Node, children: list[None]) -> None:
|
|
195
196
|
if len(children) > 1:
|
|
196
|
-
|
|
197
|
+
furcations.append(n.id)
|
|
198
|
+
|
|
199
|
+
self.traverse(leave=collect_furcations)
|
|
200
|
+
return [self.node(i) for i in furcations]
|
|
197
201
|
|
|
198
|
-
|
|
199
|
-
|
|
202
|
+
@deprecated("Use `get_furcations` instead")
|
|
203
|
+
def get_bifurcations(self) -> list[Node]:
|
|
204
|
+
"""Get all node of furcations.
|
|
205
|
+
|
|
206
|
+
Notes
|
|
207
|
+
-----
|
|
208
|
+
Deprecated due to the wrong spelling of furcation. For now, it
|
|
209
|
+
is just an alias of `get_furcations` and raise a warning. It
|
|
210
|
+
will be change to raise an error in the future.
|
|
211
|
+
"""
|
|
212
|
+
return self.get_furcations()
|
|
200
213
|
|
|
201
214
|
def get_tips(self) -> list[Node]:
|
|
202
215
|
"""Get all node of tips."""
|
|
@@ -5,6 +5,7 @@ from collections.abc import Callable, Iterable
|
|
|
5
5
|
from typing import Optional, TypeVar, overload
|
|
6
6
|
|
|
7
7
|
import numpy as np
|
|
8
|
+
from typing_extensions import deprecated
|
|
8
9
|
|
|
9
10
|
from swcgeom.core.swc import SWCLike
|
|
10
11
|
from swcgeom.core.swc_utils import (
|
|
@@ -95,6 +96,7 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
|
|
|
95
96
|
return to_subtree(tree, removals)
|
|
96
97
|
|
|
97
98
|
|
|
99
|
+
@deprecated("Use `to_subtree` instead")
|
|
98
100
|
def to_sub_tree(swc_like: SWCLike, sub: Topology) -> tuple[Tree, dict[int, int]]:
|
|
99
101
|
"""Create subtree from origin tree.
|
|
100
102
|
|
|
@@ -111,13 +113,6 @@ def to_sub_tree(swc_like: SWCLike, sub: Topology) -> tuple[Tree, dict[int, int]]
|
|
|
111
113
|
id_map : dict[int, int]
|
|
112
114
|
"""
|
|
113
115
|
|
|
114
|
-
warnings.warn(
|
|
115
|
-
"`to_sub_tree` will be removed in v0.6.0, it is replaced by "
|
|
116
|
-
"`to_subtree` beacuse it is easy to use, and this will be "
|
|
117
|
-
"removed in next version",
|
|
118
|
-
DeprecationWarning,
|
|
119
|
-
)
|
|
120
|
-
|
|
121
116
|
sub = propagate_removal(sub)
|
|
122
117
|
(new_id, new_pid), id_map_arr = to_sub_topology(sub)
|
|
123
118
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import math
|
|
4
4
|
import os
|
|
5
5
|
import re
|
|
6
|
-
import warnings
|
|
7
6
|
from collections.abc import Callable, Iterable
|
|
8
7
|
from dataclasses import dataclass
|
|
9
8
|
from typing import Generic, Literal, Optional, TypeVar, overload
|
|
@@ -11,7 +10,7 @@ from typing import Generic, Literal, Optional, TypeVar, overload
|
|
|
11
10
|
import numpy as np
|
|
12
11
|
import numpy.typing as npt
|
|
13
12
|
from tqdm import tqdm
|
|
14
|
-
from typing_extensions import Self
|
|
13
|
+
from typing_extensions import Self, deprecated
|
|
15
14
|
|
|
16
15
|
from swcgeom.images.io import ScalarType, read_imgs
|
|
17
16
|
from swcgeom.transforms import Identity, Transform
|
|
@@ -65,6 +64,7 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
|
|
|
65
64
|
return fs
|
|
66
65
|
|
|
67
66
|
@staticmethod
|
|
67
|
+
@deprecated("Use `~swcgeom.images.io.read_imgs(fname).get_full()` instead")
|
|
68
68
|
def read_imgs(fname: str) -> npt.NDArray[np.float32]:
|
|
69
69
|
"""Read images.
|
|
70
70
|
|
|
@@ -72,14 +72,6 @@ class ImageStackFolderBase(Generic[ScalarType, T]):
|
|
|
72
72
|
Use :meth:`~swcgeom.images.io.read_imgs(fname).get_full()` instead.
|
|
73
73
|
"""
|
|
74
74
|
|
|
75
|
-
warnings.warn(
|
|
76
|
-
"`ImageStackFolderBase.read_imgs` serves as a "
|
|
77
|
-
"straightforward wrapper for `~swcgeom.images.io.read_imgs(fname).get_full()`. "
|
|
78
|
-
"However, as it is not utilized within our internal "
|
|
79
|
-
"processes, it is scheduled for removal in the "
|
|
80
|
-
"forthcoming version.",
|
|
81
|
-
DeprecationWarning,
|
|
82
|
-
)
|
|
83
75
|
return read_imgs(fname).get_full()
|
|
84
76
|
|
|
85
77
|
|
|
@@ -12,6 +12,7 @@ import nrrd
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
import numpy.typing as npt
|
|
14
14
|
import tifffile
|
|
15
|
+
from typing_extensions import deprecated
|
|
15
16
|
from v3dpy.loaders import PBD, Raw
|
|
16
17
|
|
|
17
18
|
__all__ = ["read_imgs", "save_tiff", "read_images"]
|
|
@@ -601,6 +602,7 @@ class GrayImageStack:
|
|
|
601
602
|
return self.imgs.shape[:-1]
|
|
602
603
|
|
|
603
604
|
|
|
605
|
+
@deprecated("Use `read_imgs` instead")
|
|
604
606
|
def read_images(*args, **kwargs) -> GrayImageStack:
|
|
605
607
|
"""Read images.
|
|
606
608
|
|
|
@@ -608,9 +610,4 @@ def read_images(*args, **kwargs) -> GrayImageStack:
|
|
|
608
610
|
Use :meth:`read_imgs` instead.
|
|
609
611
|
"""
|
|
610
612
|
|
|
611
|
-
warnings.warn(
|
|
612
|
-
"`read_images` has been replaced by `read_imgs` because it"
|
|
613
|
-
"provide rgb support, and this will be removed in next version",
|
|
614
|
-
DeprecationWarning,
|
|
615
|
-
)
|
|
616
613
|
return GrayImageStack(read_imgs(*args, **kwargs))
|