swcgeom 0.14.0__py3-none-any.whl → 0.15.0__py3-none-any.whl

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/transforms/mst.py CHANGED
@@ -140,13 +140,8 @@ class PointsToCuntzMST(Transform[npt.NDArray[np.float32], Tree]):
140
140
  t = sort_tree(t)
141
141
  return t
142
142
 
143
- def __repr__(self) -> str:
144
- return (
145
- f"PointsToCuntzMST"
146
- f"-bf-{self.bf}"
147
- f"-furcations-{self.furcations}"
148
- f"-{'exclude-soma' if self.exclude_soma else 'include-soma'}"
149
- ) # TODO: names, types
143
+ def extra_repr(self) -> str: # TODO: names, types
144
+ return f"bf={self.bf:.4f}, furcations={self.furcations}, exclude_soma={self.exclude_soma}, sort={self.sort}"
150
145
 
151
146
 
152
147
  class PointsToMST(PointsToCuntzMST): # pylint: disable=too-few-public-methods
@@ -173,6 +168,7 @@ class PointsToMST(PointsToCuntzMST): # pylint: disable=too-few-public-methods
173
168
  names : SWCNames, optional
174
169
  types : SWCTypes, optional
175
170
  """
171
+
176
172
  if k_furcations is not None:
177
173
  warnings.warn(
178
174
  "`PointsToMST(k_furcations=...)` has been renamed to "
@@ -191,9 +187,5 @@ class PointsToMST(PointsToCuntzMST): # pylint: disable=too-few-public-methods
191
187
  **kwargs,
192
188
  )
193
189
 
194
- def __repr__(self) -> str:
195
- return (
196
- f"PointsToMST"
197
- f"-furcations-{self.furcations}"
198
- f"-{'exclude-soma' if self.exclude_soma else 'include-soma'}"
199
- )
190
+ def extra_repr(self) -> str:
191
+ return f"furcations-{self.furcations}, exclude-soma={self.exclude_soma}"
@@ -25,5 +25,5 @@ class PopulationTransform(Transform[Population, Population]):
25
25
 
26
26
  return Population(trees, root=population.root)
27
27
 
28
- def __repr__(self) -> str:
29
- return f"pop({self.transform})"
28
+ def extra_repr(self) -> str:
29
+ return f"transform={self.transform}"
@@ -64,8 +64,8 @@ class TreeSmoother(Transform[Tree, Tree]): # pylint: disable=missing-class-docs
64
64
 
65
65
  return x
66
66
 
67
- def __repr__(self) -> str:
68
- return f"TreeSmoother-{self.n_nodes}"
67
+ def extra_repr(self):
68
+ return f"n_nodes={self.n_nodes}"
69
69
 
70
70
 
71
71
  class TreeNormalizer(Normalizer[Tree]):
@@ -107,8 +107,8 @@ class CutByType(Transform[Tree, Tree]):
107
107
  y = to_subtree(x, removals)
108
108
  return y
109
109
 
110
- def __repr__(self) -> str:
111
- return f"CutByType-{self.type}"
110
+ def extra_repr(self):
111
+ return f"type={self.type}"
112
112
 
113
113
 
114
114
  class CutAxonTree(CutByType):
@@ -118,9 +118,6 @@ class CutAxonTree(CutByType):
118
118
  types = get_types(types)
119
119
  super().__init__(type=types.axon)
120
120
 
121
- def __repr__(self) -> str:
122
- return "CutAxonTree"
123
-
124
121
 
125
122
  class CutDendriteTree(CutByType):
126
123
  """Cut dendrite tree."""
@@ -129,9 +126,6 @@ class CutDendriteTree(CutByType):
129
126
  types = get_types(types)
130
127
  super().__init__(type=types.basal_dendrite) # TODO: apical dendrite
131
128
 
132
- def __repr__(self) -> str:
133
- return "CutDenriteTree"
134
-
135
129
 
136
130
  class CutByBifurcationOrder(Transform[Tree, Tree]):
137
131
  """Cut tree by bifurcation order."""
@@ -177,9 +171,6 @@ class CutShortTipBranch(Transform[Tree, Tree]):
177
171
  if callback is not None:
178
172
  self.callbacks.append(callback)
179
173
 
180
- def __repr__(self) -> str:
181
- return f"CutShortTipBranch-{self.thre}"
182
-
183
174
  def __call__(self, x: Tree) -> Tree:
184
175
  removals: List[int] = []
185
176
  self.callbacks.append(lambda br: removals.append(br[1].id))
@@ -187,6 +178,9 @@ class CutShortTipBranch(Transform[Tree, Tree]):
187
178
  self.callbacks.pop()
188
179
  return to_subtree(x, removals)
189
180
 
181
+ def extra_repr(self):
182
+ return f"threshold={self.thre}"
183
+
190
184
  def _leave(
191
185
  self, n: Tree.Node, children: List[Tuple[float, Tree.Node] | None]
192
186
  ) -> Tuple[float, Tree.Node] | None:
@@ -1,17 +1,22 @@
1
1
  """Assemble a tree."""
2
2
 
3
+ from copy import copy
3
4
  from typing import Iterable, List, Optional, Tuple
4
5
 
6
+ import numpy as np
5
7
  import pandas as pd
6
8
 
7
9
  from swcgeom.core import Tree
8
- from swcgeom.core.swc_utils import SWCNames
9
- from swcgeom.core.swc_utils.assembler import (
10
- assemble_lines_impl,
11
- try_assemble_lines_impl,
10
+ from swcgeom.core.swc_utils import (
11
+ SWCNames,
12
+ get_names,
13
+ link_roots_to_nearest_,
14
+ sort_nodes_,
12
15
  )
13
16
  from swcgeom.transforms.base import Transform
14
17
 
18
+ EPS = 1e-5
19
+
15
20
 
16
21
  class LinesToTree(Transform[List[pd.DataFrame], Tree]):
17
22
  """Assemble lines to swc."""
@@ -35,11 +40,12 @@ class LinesToTree(Transform[List[pd.DataFrame], Tree]):
35
40
  ):
36
41
  return self.assemble(lines, names=names)
37
42
 
38
- def __repr__(self) -> str:
39
- return f"LinesToTree-thre-{self.thre}-{'undirected' if self.undirected else 'directed'}"
40
-
41
43
  def assemble(
42
- self, lines: Iterable[pd.DataFrame], *, names: Optional[SWCNames] = None
44
+ self,
45
+ lines: Iterable[pd.DataFrame],
46
+ *,
47
+ undirected: bool = True,
48
+ names: Optional[SWCNames] = None,
43
49
  ) -> pd.DataFrame:
44
50
  """Assemble lines to a tree.
45
51
 
@@ -51,6 +57,8 @@ class LinesToTree(Transform[List[pd.DataFrame], Tree]):
51
57
  lines : List of ~pd.DataFrame
52
58
  An array of tables containing a line, columns should follwing
53
59
  the swc.
60
+ undirected : bool, default `True`
61
+ Forwarding to `self.try_assemble`.
54
62
  names : SWCNames, optional
55
63
  Forwarding to `self.try_assemble`.
56
64
 
@@ -60,17 +68,33 @@ class LinesToTree(Transform[List[pd.DataFrame], Tree]):
60
68
 
61
69
  See Also
62
70
  --------
63
- self.try_assemble_lines
71
+ self.try_assemble
64
72
  """
65
- return assemble_lines_impl(
66
- lines, thre=self.thre, undirected=self.undirected, names=names
73
+
74
+ tree, lines = self.try_assemble(
75
+ lines, sort_nodes=False, undirected=undirected, names=names
67
76
  )
77
+ while len(lines) > 0:
78
+ t, lines = self.try_assemble(
79
+ lines,
80
+ id_offset=len(tree),
81
+ sort_nodes=False,
82
+ undirected=undirected,
83
+ names=names,
84
+ )
85
+ tree = pd.concat([tree, t])
86
+
87
+ tree = tree.reset_index()
88
+ link_roots_to_nearest_(tree)
89
+ sort_nodes_(tree)
90
+ return tree
68
91
 
69
92
  def try_assemble(
70
93
  self,
71
94
  lines: Iterable[pd.DataFrame],
72
95
  *,
73
96
  id_offset: int = 0,
97
+ undirected: bool = True,
74
98
  sort_nodes: bool = True,
75
99
  names: Optional[SWCNames] = None,
76
100
  ) -> Tuple[pd.DataFrame, List[pd.DataFrame]]:
@@ -88,6 +112,9 @@ class LinesToTree(Transform[List[pd.DataFrame], Tree]):
88
112
  the swc.
89
113
  id_offset : int, default `0`
90
114
  The offset of the line node id.
115
+ undirected : bool, default `True`
116
+ Both ends of a line can be considered connection point. If
117
+ `False`, only the starting point.
91
118
  sort_nodes : bool, default `True`
92
119
  sort nodes of subtree.
93
120
  names : SWCNames, optional
@@ -97,11 +124,50 @@ class LinesToTree(Transform[List[pd.DataFrame], Tree]):
97
124
  tree : ~pandas.DataFrame
98
125
  remaining_lines : List of ~pandas.DataFrame
99
126
  """
100
- return try_assemble_lines_impl(
101
- lines,
102
- undirected=self.undirected,
103
- thre=self.thre,
104
- id_offset=id_offset,
105
- sort_nodes=sort_nodes,
106
- names=names,
107
- )
127
+
128
+ names = get_names(names)
129
+ lines = copy(list(lines))
130
+
131
+ tree = lines[0]
132
+ tree[names.id] = id_offset + np.arange(len(tree))
133
+ tree[names.pid] = tree[names.id] - 1
134
+ tree.at[0, names.pid] = -1
135
+ del lines[0]
136
+
137
+ while True:
138
+ for i, line in enumerate(lines):
139
+ for p in [0, -1] if undirected else [0]:
140
+ xyz = [names.x, names.y, names.z]
141
+ vs = tree[xyz] - line.iloc[p][xyz]
142
+ dis = np.linalg.norm(vs, axis=1)
143
+ ind = np.argmin(dis)
144
+ if dis[ind] > self.thre:
145
+ continue
146
+
147
+ if dis[ind] < EPS:
148
+ line = line.drop((p + len(line)) % len(line)).reset_index(
149
+ drop=True
150
+ )
151
+
152
+ line[names.id] = id_offset + len(tree) + np.arange(len(line))
153
+ line[names.pid] = line[names.id] + (-1 if p == 0 else 1)
154
+ line.at[(p + len(line)) % len(line), names.pid] = tree.iloc[ind][
155
+ names.id
156
+ ]
157
+ tree = pd.concat([tree, line])
158
+ del lines[i]
159
+ break
160
+ else:
161
+ continue
162
+
163
+ break
164
+ else:
165
+ break
166
+
167
+ if sort_nodes:
168
+ sort_nodes_(tree)
169
+
170
+ return tree, lines
171
+
172
+ def extra_repr(self):
173
+ return f"thre={self.thre}, undirected={self.undirected}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: swcgeom
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: Neuron geometry library for swc format
5
5
  Author-email: yzx9 <yuan.zx@outlook.com>
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
1
  swcgeom/__init__.py,sha256=z88Zwcjv-ii7c7dYd9QPg9XrUVorQjtrgGbQCsEnQhc,265
2
- swcgeom/_version.py,sha256=9XbM6DRatFmjNkeSNxF4a6enpQeo8nPyAy9RTPPwPpY,413
2
+ swcgeom/_version.py,sha256=oHv-EAjiXbJma3jZ0Tq6UPimiWYyyw2Ao9S8zdq9uWs,413
3
3
  swcgeom/analysis/__init__.py,sha256=esL_poW8u-_bmp7vR9ldcumX3_xodtaVfM1USqxQo5w,377
4
4
  swcgeom/analysis/branch_features.py,sha256=s6PTMwwvxrVtZXRZlQbUSIw4M9-1IG63kf-Nxc0tMB0,1958
5
5
  swcgeom/analysis/feature_extractor.py,sha256=coe07_bJau96BkimcnXzuf4KqjY5_QRLwqaumFsu_tQ,14031
@@ -11,37 +11,37 @@ swcgeom/analysis/visualization.py,sha256=mKOpzTPkLpr1ggGL1MZPZRTG92GEg4idLT4eN5z
11
11
  swcgeom/analysis/volume.py,sha256=nWPR7wGOk3Wl5eh97YMws0X-2jk8K7lmFp4-03wL3lY,4628
12
12
  swcgeom/core/__init__.py,sha256=ZUudZavxAIUU6Q0lBHrQ4ybmL5lBfvzyYsTtpuih9wg,332
13
13
  swcgeom/core/branch.py,sha256=uuJCxaByRu-OdDZVWEffSFcmZWY-6ZWUhHN1M2Awj1s,3980
14
- swcgeom/core/branch_tree.py,sha256=sN0viBVg5A4r9dMCkGNAaVttrdR4bEoZZBbHZFKdXj4,1892
14
+ swcgeom/core/branch_tree.py,sha256=Ece6q1VNCRLLMj29N_MjXmmlHT8h4tpWCuDE0uSgKJo,1873
15
15
  swcgeom/core/node.py,sha256=HvfgsW4WU01hkRIPci8KF4bQMAkwtAxOGfUL4yUbuBs,3623
16
16
  swcgeom/core/path.py,sha256=CsEelHiDR0JPBP1dTvoCSRvX3kBlZxkQilosnncV4nQ,4188
17
- swcgeom/core/population.py,sha256=YZLAtkZKJeErOsE5MwhqieGjoMa7sWjFl5xxmXVPTco,8786
17
+ swcgeom/core/population.py,sha256=I9xSeGUveLhxkOg_5FWLcvf4yJVJ6j9_n03Hna3y_6w,8790
18
18
  swcgeom/core/segment.py,sha256=yabRdFj7KlkJP4V67jAlCIRzpHduNnq3bRBIRMuANfA,3158
19
19
  swcgeom/core/swc.py,sha256=lSYxAa25l6O8WZ9JtSSET-RZMr6EA1Tq_aXL_x0H9Rc,6795
20
- swcgeom/core/tree.py,sha256=oAx31r2jFzxSW7vnM17m8sMVVI3_xzxRyDQh19MPTlQ,12126
21
- swcgeom/core/tree_utils.py,sha256=xPy9b3MO43QR7VSvJvrwioXyQLGAPLotQjekn_-UiLg,7308
22
- swcgeom/core/tree_utils_impl.py,sha256=5Cb63ziVVLADnkjfuq1T-ePw2TQQ5TKk4gcPZR6f_wY,1123
20
+ swcgeom/core/tree.py,sha256=K2k7o8OZ9r2YeMmIebT8_0qvkwJYtLLzFHECz3fmmts,12365
21
+ swcgeom/core/tree_utils.py,sha256=3aCHghny5Z727sxkt6P8E2MMr34vK6irfPCelMn3vk4,7681
22
+ swcgeom/core/tree_utils_impl.py,sha256=kN2ByjqqQtZUfmC_ac25tXOaE-CMiV2lP58VxFphLEU,1616
23
23
  swcgeom/core/swc_utils/__init__.py,sha256=qghRxjtzvq5KKfN4HhvLpZNsGPfZQu-Jj2x62_5-TbQ,575
24
- swcgeom/core/swc_utils/assembler.py,sha256=RoMJ3RjLC4O7mk62QxXVTQ5SUHagrFmpEw3nOnnqeJo,4563
24
+ swcgeom/core/swc_utils/assembler.py,sha256=_ByaVFc61rfCS2p9QUw4g40uF4pZ6NJaDc--TcV4jWo,642
25
25
  swcgeom/core/swc_utils/base.py,sha256=huVxjuMLlTHbEb-KSEFDLgU0Ss3723t2Gr4Z_gQtl00,4737
26
26
  swcgeom/core/swc_utils/checker.py,sha256=E72GtLZ_1IqQQ7aWQGs0dZ3Z609__bw3EYQqeWrk-EI,2657
27
27
  swcgeom/core/swc_utils/io.py,sha256=6_--Qoe8kDja4PWsjwqRAvPJZNMFILFgauHaeWeGikU,6444
28
28
  swcgeom/core/swc_utils/normalizer.py,sha256=_Ysi8bSJ2JBnIGB8o6BvAg2mcz6xuJp9rgNLZqpLuR8,5083
29
- swcgeom/core/swc_utils/subtree.py,sha256=bd4XOLmRDfQSn_ktfQM3Hn8ONpCuZ_TdTWhE9-7QXW4,1999
29
+ swcgeom/core/swc_utils/subtree.py,sha256=43QITYvgXu3b_kfIod2Irrj3dSfrA-gTFev5VxzRafI,1995
30
30
  swcgeom/images/__init__.py,sha256=QBP1ZGGo2nWAcV7Krz-vbvW_jN4ChqXrrpoScXcUURs,96
31
31
  swcgeom/images/augmentation.py,sha256=v9zluYXmBEbafaDBTpvJovi4_KWJmHZZSvcYHzG0oWo,4099
32
- swcgeom/images/folder.py,sha256=urh60ITreGgEwSbCbgexJFM8_-C9WLAQ9jUN50sox10,4034
33
- swcgeom/images/io.py,sha256=VZWBq-_TMrKKdMaqpbZWZP6fCGIxWdhdIfb0ePcDy-4,20272
32
+ swcgeom/images/folder.py,sha256=2REkrdNghLm1z8kZ2PDVvtsupzog8kARkeMjLuLiLFo,4955
33
+ swcgeom/images/io.py,sha256=jUyKjtau8_5V-PN1kRsPHtP1OtueJi1zEf5-7PZ_tG8,21226
34
34
  swcgeom/transforms/__init__.py,sha256=Mi2mOgkQ50JbZ9LmgXgJIuAA5eio67oe2AOs2CCCxTQ,463
35
- swcgeom/transforms/base.py,sha256=22kkpQ11YCrH1SWWMHVqYjHp6cAlhslhdi1wKgAnT_s,3416
36
- swcgeom/transforms/branch.py,sha256=mdrTC9T4zENS9DdwlFaoFHcWDuFAgKgiNicJZ1gLsOc,5445
37
- swcgeom/transforms/geometry.py,sha256=zPmEf6ApJKzeZBbZ7qJYZXpY3-mrABN2dr7AUNqENiQ,6619
38
- swcgeom/transforms/image_stack.py,sha256=uJfRhXz86NQUxA3MguCoHpq4WBUAUjgZjMH1n-Wqoiw,5297
39
- swcgeom/transforms/images.py,sha256=tUl3dtqWfrhWR7WrOUc6LzWHnOzz3fwEgV3mMLRhn0c,908
40
- swcgeom/transforms/mst.py,sha256=B6ZRqeEfCHLHFu0tdOsOgplbYHra4VPYjnneXMU-cNY,6413
35
+ swcgeom/transforms/base.py,sha256=gN5Iqi-OHkYrsjllSOdxI6Yzav3jJGoi6kUPy-38FAs,4101
36
+ swcgeom/transforms/branch.py,sha256=R0rVti--u70IiUKyHSx6MsDYJyy6zSCf18Uia2Cmh28,5410
37
+ swcgeom/transforms/geometry.py,sha256=XR73fO_8T7otUFIllqKOWW0OnrsXBc7yA01oDT99yMc,7385
38
+ swcgeom/transforms/image_stack.py,sha256=KhZ26Ps88jk_7NkI9dkS2E7NZXUvMaN3Ln9WzJ-vPu0,5823
39
+ swcgeom/transforms/images.py,sha256=l5Hx8x27zoClUz3s11j2oj0Lt9ROh5JJfjoIU_vd3H8,898
40
+ swcgeom/transforms/mst.py,sha256=ceL_EWpCtoSy9zYD6wPP5zO68eNTrDl9JExoPLcE_Lw,6236
41
41
  swcgeom/transforms/path.py,sha256=Gk2iunGQMX7vE83bdo8xoDO-KAT1Vvep0iZs7oFLzFQ,1089
42
- swcgeom/transforms/population.py,sha256=ZrKfMAMx4l729f-JLgw0dnGIPtPUoV0ZZoNNyA5cBw8,826
43
- swcgeom/transforms/tree.py,sha256=Q5OnSti0ZeTNb-WpA_UZsDN7dhLN8YweEF_Siyrj66c,6420
44
- swcgeom/transforms/tree_assembler.py,sha256=UZ9OUg1bQNecYIY_7ippg3S8gpuoi617ZUE0jg6BrQE,3177
42
+ swcgeom/transforms/population.py,sha256=EmZ6ntuOKe8mXJxMW7nCUA-w2DVlEVe2n0IOVz49tCY,833
43
+ swcgeom/transforms/tree.py,sha256=Q45sVA7yi8pINV7ENOWPKz6DnEM3U5uJxwkFKNFS5VE,6262
44
+ swcgeom/transforms/tree_assembler.py,sha256=vi_X9CNo5IxHP5J7bRl2z91PWufU6HmYlz1iyfdPUxE,5121
45
45
  swcgeom/utils/__init__.py,sha256=QfezYuQzgmPziNiWz2T1h77V-gjpuBoUi3mC-K-PZlI,427
46
46
  swcgeom/utils/debug.py,sha256=qay2qJpViLX82mzxdndxQFn-pi1vaEj9CbLGuGt8Y9k,465
47
47
  swcgeom/utils/download.py,sha256=By2qZezo6h1Ke_4YpSIhDgcisOrpjVqRmNzbhynC2xs,2834
@@ -55,8 +55,8 @@ swcgeom/utils/sdf.py,sha256=K-LSnwwNsGF85GW1kNBaqXAAVesxZAEsrn0nvOA2LcA,10614
55
55
  swcgeom/utils/solid_geometry.py,sha256=TV02jhcoCLCqtYA9hfE50LFD_VRfixMiOSiHB5Jb2_U,2431
56
56
  swcgeom/utils/transforms.py,sha256=PmP5fL_iVguq4GR2aqXhM0TeCsiFVnrPZMZG6zLohrE,6983
57
57
  swcgeom/utils/volumetric_object.py,sha256=DVRGGmQrAL0oaW6hbNtp5TStbic9DfyJdCzsv2FNw2c,15134
58
- swcgeom-0.14.0.dist-info/LICENSE,sha256=JPtohhZ4XURqoKI0ZqnMYb7dobCOoZR_n5EpnaLTp3E,11344
59
- swcgeom-0.14.0.dist-info/METADATA,sha256=4DWijpWXVwo7_Cf8H-EH9DVDfQ31XoA9wEMhshow_kg,2347
60
- swcgeom-0.14.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
61
- swcgeom-0.14.0.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
62
- swcgeom-0.14.0.dist-info/RECORD,,
58
+ swcgeom-0.15.0.dist-info/LICENSE,sha256=JPtohhZ4XURqoKI0ZqnMYb7dobCOoZR_n5EpnaLTp3E,11344
59
+ swcgeom-0.15.0.dist-info/METADATA,sha256=VJX8KlMJWpYCZ-bMUYy_kL05sAHXmvHLMqUepQaKNTQ,2347
60
+ swcgeom-0.15.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
61
+ swcgeom-0.15.0.dist-info/top_level.txt,sha256=hmLyUXWS61Gxl07haswFEKKefYPBVJYlUlol8ghNkjY,8
62
+ swcgeom-0.15.0.dist-info/RECORD,,