swcgeom 0.17.0__py3-none-any.whl → 0.17.1__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.

Files changed (47) hide show
  1. swcgeom/_version.py +2 -2
  2. swcgeom/analysis/feature_extractor.py +13 -12
  3. swcgeom/analysis/features.py +4 -4
  4. swcgeom/analysis/lmeasure.py +5 -5
  5. swcgeom/analysis/sholl.py +4 -4
  6. swcgeom/analysis/trunk.py +12 -11
  7. swcgeom/analysis/visualization.py +9 -9
  8. swcgeom/analysis/visualization3d.py +85 -0
  9. swcgeom/analysis/volume.py +4 -4
  10. swcgeom/core/branch.py +4 -3
  11. swcgeom/core/branch_tree.py +3 -4
  12. swcgeom/core/compartment.py +3 -2
  13. swcgeom/core/node.py +2 -2
  14. swcgeom/core/path.py +3 -2
  15. swcgeom/core/population.py +16 -27
  16. swcgeom/core/swc.py +11 -10
  17. swcgeom/core/swc_utils/base.py +8 -17
  18. swcgeom/core/swc_utils/io.py +7 -6
  19. swcgeom/core/swc_utils/normalizer.py +4 -3
  20. swcgeom/core/swc_utils/subtree.py +2 -2
  21. swcgeom/core/tree.py +22 -34
  22. swcgeom/core/tree_utils.py +11 -10
  23. swcgeom/core/tree_utils_impl.py +3 -3
  24. swcgeom/images/augmentation.py +3 -3
  25. swcgeom/images/folder.py +10 -16
  26. swcgeom/images/io.py +19 -30
  27. swcgeom/transforms/image_stack.py +6 -5
  28. swcgeom/transforms/images.py +2 -3
  29. swcgeom/transforms/neurolucida_asc.py +4 -6
  30. swcgeom/transforms/population.py +1 -3
  31. swcgeom/transforms/tree.py +8 -7
  32. swcgeom/transforms/tree_assembler.py +4 -3
  33. swcgeom/utils/ellipse.py +3 -4
  34. swcgeom/utils/neuromorpho.py +17 -16
  35. swcgeom/utils/plotter_2d.py +12 -6
  36. swcgeom/utils/plotter_3d.py +31 -0
  37. swcgeom/utils/renderer.py +6 -6
  38. swcgeom/utils/sdf.py +2 -2
  39. swcgeom/utils/solid_geometry.py +1 -3
  40. swcgeom/utils/transforms.py +1 -3
  41. swcgeom/utils/volumetric_object.py +8 -10
  42. {swcgeom-0.17.0.dist-info → swcgeom-0.17.1.dist-info}/METADATA +1 -1
  43. swcgeom-0.17.1.dist-info/RECORD +67 -0
  44. swcgeom-0.17.0.dist-info/RECORD +0 -65
  45. {swcgeom-0.17.0.dist-info → swcgeom-0.17.1.dist-info}/LICENSE +0 -0
  46. {swcgeom-0.17.0.dist-info → swcgeom-0.17.1.dist-info}/WHEEL +0 -0
  47. {swcgeom-0.17.0.dist-info → swcgeom-0.17.1.dist-info}/top_level.txt +0 -0
@@ -2,21 +2,10 @@
2
2
 
3
3
  import os
4
4
  import warnings
5
+ from collections.abc import Callable, Iterable, Iterator
5
6
  from concurrent.futures import ProcessPoolExecutor
6
7
  from functools import reduce
7
- from typing import (
8
- Any,
9
- Callable,
10
- Dict,
11
- Iterable,
12
- Iterator,
13
- List,
14
- Optional,
15
- Protocol,
16
- TypeVar,
17
- cast,
18
- overload,
19
- )
8
+ from typing import Any, Optional, Protocol, TypeVar, cast, overload
20
9
 
21
10
  import numpy as np
22
11
  import numpy.typing as npt
@@ -44,16 +33,16 @@ class Trees(Protocol):
44
33
  class LazyLoadingTrees:
45
34
  """Lazy loading trees."""
46
35
 
47
- swcs: List[str]
48
- trees: List[Tree | None]
49
- kwargs: Dict[str, Any]
36
+ swcs: list[str]
37
+ trees: list[Tree | None]
38
+ kwargs: dict[str, Any]
50
39
 
51
40
  def __init__(self, swcs: Iterable[str], **kwargs) -> None:
52
41
  """
53
42
  Paramters
54
43
  ---------
55
44
  swcs : List of str
56
- kwargs : Dict[str, Any]
45
+ kwargs : dict[str, Any]
57
46
  Forwarding to `Tree.from_swc`
58
47
  """
59
48
 
@@ -81,7 +70,7 @@ class LazyLoadingTrees:
81
70
  class ChainTrees:
82
71
  """Chain trees."""
83
72
 
84
- trees: List[Trees]
73
+ trees: list[Trees]
85
74
  cumsum: npt.NDArray[np.int64]
86
75
 
87
76
  def __init__(self, trees: Iterable[Trees]) -> None:
@@ -146,7 +135,7 @@ class Population:
146
135
 
147
136
  # fmt:off
148
137
  @overload
149
- def __getitem__(self, key: slice) -> List[Tree]: ...
138
+ def __getitem__(self, key: slice) -> list[Tree]: ...
150
139
  @overload
151
140
  def __getitem__(self, key: int) -> Tree: ...
152
141
  # fmt:on
@@ -216,9 +205,9 @@ class Population:
216
205
  return cls.from_swc(root, ext, extra_cols=extra_cols, **kwargs)
217
206
 
218
207
  @staticmethod
219
- def find_swcs(root: str, ext: str = ".swc", relpath: bool = False) -> List[str]:
208
+ def find_swcs(root: str, ext: str = ".swc", relpath: bool = False) -> list[str]:
220
209
  """Find all swc files."""
221
- swcs: List[str] = []
210
+ swcs: list[str] = []
222
211
  for r, _, files in os.walk(root):
223
212
  rr = os.path.relpath(r, root) if relpath else r
224
213
  fs = filter(lambda f: os.path.splitext(f)[-1] == ext, files)
@@ -231,8 +220,8 @@ class Populations:
231
220
  """A set of population."""
232
221
 
233
222
  len: int
234
- populations: List[Population]
235
- labels: List[str]
223
+ populations: list[Population]
224
+ labels: list[str]
236
225
 
237
226
  def __init__(
238
227
  self, populations: Iterable[Population], labels: Optional[Iterable[str]] = None
@@ -248,9 +237,9 @@ class Populations:
248
237
 
249
238
  # fmt:off
250
239
  @overload
251
- def __getitem__(self, key: slice) -> List[List[Tree]]: ...
240
+ def __getitem__(self, key: slice) -> list[list[Tree]]: ...
252
241
  @overload
253
- def __getitem__(self, key: int) -> List[Tree]: ...
242
+ def __getitem__(self, key: int) -> list[Tree]: ...
254
243
  # fmt:on
255
244
  def __getitem__(self, key):
256
245
  return [p[key] for p in self.populations]
@@ -259,7 +248,7 @@ class Populations:
259
248
  """Miniumn length of populations."""
260
249
  return self.len
261
250
 
262
- def __iter__(self) -> Iterator[List[Tree]]:
251
+ def __iter__(self) -> Iterator[list[Tree]]:
263
252
  return (self[i] for i in range(self.len))
264
253
 
265
254
  def __repr__(self) -> str:
@@ -288,7 +277,7 @@ class Populations:
288
277
 
289
278
  Parameters
290
279
  ----------
291
- roots : list of str
280
+ roots : List of str
292
281
  intersect : bool, default `True`
293
282
  Take the intersection of these populations.
294
283
  check_same : bool, default `False`
swcgeom/core/swc.py CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  import warnings
4
4
  from abc import ABC, abstractmethod
5
+ from collections.abc import Iterable
5
6
  from copy import deepcopy
6
- from typing import Any, Dict, Iterable, List, Optional, Tuple, TypeVar, overload
7
+ from typing import Any, Optional, TypeVar, overload
7
8
 
8
9
  import numpy as np
9
10
  import numpy.typing as npt
@@ -32,7 +33,7 @@ __all__ = [
32
33
 
33
34
 
34
35
  swc_names_default = get_names()
35
- swc_cols: List[Tuple[str, npt.DTypeLike]] = [
36
+ swc_cols: list[tuple[str, npt.DTypeLike]] = [
36
37
  (swc_names_default.id, np.int32),
37
38
  (swc_names_default.type, np.int32),
38
39
  (swc_names_default.x, np.float32),
@@ -42,7 +43,7 @@ swc_cols: List[Tuple[str, npt.DTypeLike]] = [
42
43
  (swc_names_default.pid, np.int32),
43
44
  ]
44
45
 
45
- eswc_cols: List[Tuple[str, npt.DTypeLike]] = [
46
+ eswc_cols: list[tuple[str, npt.DTypeLike]] = [
46
47
  ("level", np.int32),
47
48
  ("mode", np.int32),
48
49
  ("timestamp", np.int32),
@@ -55,7 +56,7 @@ class SWCLike(ABC):
55
56
  """ABC of SWC."""
56
57
 
57
58
  source: str = ""
58
- comments: List[str] = []
59
+ comments: list[str] = []
59
60
  names: SWCNames
60
61
  types: SWCTypes
61
62
 
@@ -131,15 +132,15 @@ class SWCLike(ABC):
131
132
 
132
133
  # fmt: off
133
134
  @overload
134
- def to_swc(self, fname: str, *, extra_cols: List[str] | None = ..., source: bool | str = ..., id_offset: int = ...) -> None: ...
135
+ def to_swc(self, fname: str, *, extra_cols: list[str] | None = ..., source: bool | str = ..., id_offset: int = ...) -> None: ...
135
136
  @overload
136
- def to_swc(self, *, extra_cols: List[str] | None = ..., source: bool | str = ..., id_offset: int = ...) -> str: ...
137
+ def to_swc(self, *, extra_cols: list[str] | None = ..., source: bool | str = ..., id_offset: int = ...) -> str: ...
137
138
  # fmt: on
138
139
  def to_swc(
139
140
  self,
140
141
  fname: Optional[str] = None,
141
142
  *,
142
- extra_cols: Optional[List[str]] = None,
143
+ extra_cols: Optional[list[str]] = None,
143
144
  source: bool | str = True,
144
145
  comments: bool = True,
145
146
  id_offset: int = 1,
@@ -177,7 +178,7 @@ class SWCLike(ABC):
177
178
  self,
178
179
  fname: Optional[str] = None,
179
180
  swc_path: Optional[str] = None,
180
- extra_cols: Optional[List[str]] = None,
181
+ extra_cols: Optional[list[str]] = None,
181
182
  **kwargs,
182
183
  ) -> str | None:
183
184
  if swc_path is None:
@@ -199,7 +200,7 @@ SWCTypeVar = TypeVar("SWCTypeVar", bound=SWCLike)
199
200
  class DictSWC(SWCLike):
200
201
  """SWC implementation on dict."""
201
202
 
202
- ndata: Dict[str, npt.NDArray]
203
+ ndata: dict[str, npt.NDArray]
203
204
 
204
205
  def __init__(
205
206
  self,
@@ -221,7 +222,7 @@ class DictSWC(SWCLike):
221
222
  def values(self) -> Iterable[npt.NDArray[Any]]:
222
223
  return self.ndata.values()
223
224
 
224
- def items(self) -> Iterable[Tuple[str, npt.NDArray[Any]]]:
225
+ def items(self) -> Iterable[tuple[str, npt.NDArray[Any]]]:
225
226
  return self.ndata.items()
226
227
 
227
228
  def get_ndata(self, key: str) -> npt.NDArray[Any]:
@@ -1,16 +1,7 @@
1
1
  """Base SWC format utils."""
2
2
 
3
- from dataclasses import dataclass
4
- from typing import (
5
- Callable,
6
- List,
7
- Literal,
8
- NamedTuple,
9
- Optional,
10
- Tuple,
11
- TypeVar,
12
- overload,
13
- )
3
+ from collections.abc import Callable
4
+ from typing import Literal, NamedTuple, Optional, TypeVar, overload
14
5
 
15
6
  import numpy as np
16
7
  import numpy.typing as npt
@@ -29,7 +20,7 @@ __all__ = [
29
20
  ]
30
21
 
31
22
  T, K = TypeVar("T"), TypeVar("K")
32
- Topology = Tuple[npt.NDArray[np.int32], npt.NDArray[np.int32]] # (id, pid)
23
+ Topology = tuple[npt.NDArray[np.int32], npt.NDArray[np.int32]] # (id, pid)
33
24
 
34
25
 
35
26
  class SWCNames(NamedTuple):
@@ -43,7 +34,7 @@ class SWCNames(NamedTuple):
43
34
  r: str = "r"
44
35
  pid: str = "pid"
45
36
 
46
- def cols(self) -> List[str]:
37
+ def cols(self) -> list[str]:
47
38
  return [self.id, self.type, self.x, self.y, self.z, self.r, self.pid]
48
39
 
49
40
 
@@ -114,10 +105,10 @@ def get_dsu(
114
105
  @overload
115
106
  def traverse(topology: Topology, *, enter: Callable[[int, T | None], T], root: int | np.integer = ..., mode: Literal["dfs"] = ...) -> None: ...
116
107
  @overload
117
- def traverse(topology: Topology, *, leave: Callable[[int, List[K]], K], root: int | np.integer = ..., mode: Literal["dfs"] = ...) -> K: ...
108
+ def traverse(topology: Topology, *, leave: Callable[[int, list[K]], K], root: int | np.integer = ..., mode: Literal["dfs"] = ...) -> K: ...
118
109
  @overload
119
110
  def traverse(
120
- topology: Topology, *, enter: Callable[[int, T | None], T], leave: Callable[[int, List[K]], K],
111
+ topology: Topology, *, enter: Callable[[int, T | None], T], leave: Callable[[int, list[K]], K],
121
112
  root: int | np.integer = ..., mode: Literal["dfs"] = ...,
122
113
  ) -> K: ...
123
114
  # fmt: on
@@ -130,7 +121,7 @@ def traverse(topology: Topology, *, mode="dfs", **kwargs):
130
121
  The callback when entering node, which accepts two parameters,
131
122
  the current node id and the return value of it parent node. In
132
123
  particular, the root node receives an `None`.
133
- leave : (id: int, children: List[T]) => T, optional
124
+ leave : (id: int, children: list[T]) => T, optional
134
125
  The callback when leaving node. When leaving a node, subtree
135
126
  has already been traversed. Callback accepts two parameters,
136
127
  the current node id and list of the return value of children,
@@ -155,7 +146,7 @@ def _traverse_dfs(topology: Topology, *, enter=None, leave=None, root=0):
155
146
  children_map[pid].append(idx)
156
147
 
157
148
  # manual dfs to avoid stack overflow in long branch
158
- stack: List[Tuple[int, bool]] = [(root, True)] # (idx, is_enter)
149
+ stack: list[tuple[int, bool]] = [(root, True)] # (idx, is_enter)
159
150
  params = {root: None}
160
151
  vals = {}
161
152
 
@@ -2,7 +2,8 @@
2
2
 
3
3
  import re
4
4
  import warnings
5
- from typing import Callable, Iterable, List, Literal, Optional, Tuple
5
+ from collections.abc import Callable, Iterable
6
+ from typing import Literal, Optional
6
7
 
7
8
  import numpy as np
8
9
  import numpy.typing as npt
@@ -30,7 +31,7 @@ def read_swc(
30
31
  *,
31
32
  encoding: Literal["detect"] | str = "utf-8",
32
33
  names: Optional[SWCNames] = None,
33
- ) -> Tuple[pd.DataFrame, List[str]]:
34
+ ) -> tuple[pd.DataFrame, list[str]]:
34
35
  """Read swc file.
35
36
 
36
37
  Parameters
@@ -55,7 +56,7 @@ def read_swc(
55
56
  Returns
56
57
  -------
57
58
  df : ~pandas.DataFrame
58
- comments : list of string
59
+ comments : List of string
59
60
  """
60
61
 
61
62
  names = get_names(names)
@@ -137,14 +138,14 @@ def parse_swc(
137
138
  names: SWCNames,
138
139
  extra_cols: Iterable[str] | None = None,
139
140
  encoding: Literal["detect"] | str = "utf-8",
140
- ) -> Tuple[pd.DataFrame, List[str]]:
141
+ ) -> tuple[pd.DataFrame, list[str]]:
141
142
  """Parse swc file.
142
143
 
143
144
  Parameters
144
145
  ----------
145
146
  fname : PathOrIO
146
147
  names : SWCNames
147
- extra_cols : list of str, optional
148
+ extra_cols : List of str, optional
148
149
  encoding : str | 'detect', default `utf-8`
149
150
  The name of the encoding used to decode the file. If is
150
151
  `detect`, we will try to detect the character encoding.
@@ -152,7 +153,7 @@ def parse_swc(
152
153
  Returns
153
154
  -------
154
155
  df : ~pandas.DataFrame
155
- comments : list of string
156
+ comments : List of string
156
157
  """
157
158
 
158
159
  # pylint: disable=too-many-locals
@@ -3,7 +3,8 @@
3
3
  Methods ending with a underline imply an in-place transformation.
4
4
  """
5
5
 
6
- from typing import Callable, List, Literal, Optional, Tuple
6
+ from collections.abc import Callable
7
+ from typing import Literal, Optional
7
8
 
8
9
  import numpy as np
9
10
  import numpy.typing as npt
@@ -111,7 +112,7 @@ def sort_nodes_(df: pd.DataFrame, *, names: Optional[SWCNames] = None) -> None:
111
112
  df[names.id], df[names.pid] = new_ids, new_pids
112
113
 
113
114
 
114
- def sort_nodes_impl(topology: Topology) -> Tuple[Topology, npt.NDArray[np.int32]]:
115
+ def sort_nodes_impl(topology: Topology) -> tuple[Topology, npt.NDArray[np.int32]]:
115
116
  """Sort the indices of neuron tree.
116
117
 
117
118
  Returns
@@ -127,7 +128,7 @@ def sort_nodes_impl(topology: Topology) -> Tuple[Topology, npt.NDArray[np.int32]
127
128
  new_pids = np.full_like(old_ids, fill_value=-3)
128
129
  new_id = 0
129
130
  first_root = old_ids[(old_pids == -1).argmax()]
130
- s: List[Tuple[npt.NDArray[np.int32], int]] = [(first_root, -1)]
131
+ s: list[tuple[npt.NDArray[np.int32], int]] = [(first_root, -1)]
131
132
  while len(s) != 0:
132
133
  old_id, new_pid = s.pop()
133
134
  id_map[new_id] = old_id
@@ -6,7 +6,7 @@ but in more cases, you can use the high-level methods provided in
6
6
  high-level API.
7
7
  """
8
8
 
9
- from typing import Tuple, cast
9
+ from typing import cast
10
10
 
11
11
  import numpy as np
12
12
  import numpy.typing as npt
@@ -18,7 +18,7 @@ __all__ = ["REMOVAL", "to_sub_topology", "propagate_removal"]
18
18
  REMOVAL = -2 # A marker in utils, place in the ids to mark it removal
19
19
 
20
20
 
21
- def to_sub_topology(sub: Topology) -> Tuple[Topology, npt.NDArray[np.int32]]:
21
+ def to_sub_topology(sub: Topology) -> tuple[Topology, npt.NDArray[np.int32]]:
22
22
  """Create sub tree from origin tree.
23
23
 
24
24
  Mark the node to be removed, then use this method to get a child
swcgeom/core/tree.py CHANGED
@@ -2,20 +2,8 @@
2
2
 
3
3
  import itertools
4
4
  import os
5
- import warnings
6
- from typing import (
7
- Callable,
8
- Dict,
9
- Iterable,
10
- Iterator,
11
- List,
12
- Literal,
13
- Optional,
14
- Tuple,
15
- TypeVar,
16
- Union,
17
- overload,
18
- )
5
+ from collections.abc import Callable, Iterable, Iterator
6
+ from typing import Literal, Optional, TypeVar, Union, overload
19
7
 
20
8
  import numpy as np
21
9
  import numpy.typing as npt
@@ -44,12 +32,12 @@ class Tree(DictSWC):
44
32
  def parent(self) -> Union["Tree.Node", None]:
45
33
  return Tree.Node(self.attach, self.pid) if self.pid != -1 else None
46
34
 
47
- def children(self) -> List["Tree.Node"]:
35
+ def children(self) -> list["Tree.Node"]:
48
36
  children = self.attach.id()[self.attach.pid() == self.id]
49
37
  return [Tree.Node(self.attach, idx) for idx in children]
50
38
 
51
39
  def branch(self) -> "Tree.Branch":
52
- ns: List["Tree.Node"] = [self]
40
+ ns: list["Tree.Node"] = [self]
53
41
  while not ns[-1].is_bifurcation() and (p := ns[-1].parent()) is not None:
54
42
  ns.append(p)
55
43
 
@@ -68,7 +56,7 @@ class Tree(DictSWC):
68
56
 
69
57
  Parameters
70
58
  ----------
71
- out_mapping : List of int or Dict[int, int], optional
59
+ out_mapping : List of int or dict[int, int], optional
72
60
  Map from new id to old id.
73
61
  """
74
62
 
@@ -160,7 +148,7 @@ class Tree(DictSWC):
160
148
 
161
149
  # fmt:off
162
150
  @overload
163
- def __getitem__(self, key: slice) -> List[Node]: ...
151
+ def __getitem__(self, key: slice) -> list[Node]: ...
164
152
  @overload
165
153
  def __getitem__(self, key: int) -> Node: ...
166
154
  @overload
@@ -199,18 +187,18 @@ class Tree(DictSWC):
199
187
  raise ValueError(f"no soma found in: {self.source}")
200
188
  return n
201
189
 
202
- def get_bifurcations(self) -> List[Node]:
190
+ def get_bifurcations(self) -> list[Node]:
203
191
  """Get all node of bifurcations."""
204
- bifurcations: List[int] = []
192
+ bifurcations: list[int] = []
205
193
 
206
- def collect_bifurcations(n: Tree.Node, children: List[None]) -> None:
194
+ def collect_bifurcations(n: Tree.Node, children: list[None]) -> None:
207
195
  if len(children) > 1:
208
196
  bifurcations.append(n.id)
209
197
 
210
198
  self.traverse(leave=collect_bifurcations)
211
199
  return [self.node(i) for i in bifurcations]
212
200
 
213
- def get_tips(self) -> List[Node]:
201
+ def get_tips(self) -> list[Node]:
214
202
  """Get all node of tips."""
215
203
  tip_ids = np.setdiff1d(self.id(), self.pid(), assume_unique=True)
216
204
  return [self.node(i) for i in tip_ids]
@@ -221,16 +209,16 @@ class Tree(DictSWC):
221
209
  def get_segments(self) -> Compartments[Compartment]: # Alias
222
210
  return self.get_compartments()
223
211
 
224
- def get_branches(self) -> List[Branch]:
212
+ def get_branches(self) -> list[Branch]:
225
213
  def collect_branches(
226
- node: "Tree.Node", pre: List[Tuple[List[Tree.Branch], List[int]]]
227
- ) -> Tuple[List[Tree.Branch], List[int]]:
214
+ node: "Tree.Node", pre: list[tuple[list[Tree.Branch], list[int]]]
215
+ ) -> tuple[list[Tree.Branch], list[int]]:
228
216
  if len(pre) == 1:
229
217
  branches, child = pre[0]
230
218
  child.append(node.id)
231
219
  return branches, child
232
220
 
233
- branches: List[Tree.Branch] = []
221
+ branches: list[Tree.Branch] = []
234
222
 
235
223
  for sub_branches, child in pre:
236
224
  child.append(node.id)
@@ -244,19 +232,19 @@ class Tree(DictSWC):
244
232
  branches, _ = self.traverse(leave=collect_branches)
245
233
  return branches
246
234
 
247
- def get_paths(self) -> List[Path]:
235
+ def get_paths(self) -> list[Path]:
248
236
  """Get all path from soma to tips."""
249
- path_dic: Dict[int, List[int]] = {}
237
+ path_dic: dict[int, list[int]] = {}
250
238
 
251
- def assign_path(n: Tree.Node, pre_path: List[int] | None) -> List[int]:
239
+ def assign_path(n: Tree.Node, pre_path: list[int] | None) -> list[int]:
252
240
  path = [] if pre_path is None else pre_path.copy()
253
241
  path.append(n.id)
254
242
  path_dic[n.id] = path
255
243
  return path
256
244
 
257
245
  def collect_path(
258
- n: Tree.Node, children: List[List[List[int]]]
259
- ) -> List[List[int]]:
246
+ n: Tree.Node, children: list[list[list[int]]]
247
+ ) -> list[list[int]]:
260
248
  if len(children) == 0:
261
249
  return [path_dic[n.id]]
262
250
 
@@ -283,7 +271,7 @@ class Tree(DictSWC):
283
271
  @overload
284
272
  def traverse(self, *,
285
273
  enter: Optional[Callable[[Node, T | None], T]] = ...,
286
- leave: Callable[[Node, List[K]], K],
274
+ leave: Callable[[Node, list[K]], K],
287
275
  root: int | np.integer = ..., mode: Literal["dfs"] = ...) -> K: ...
288
276
  # fmt: on
289
277
 
@@ -293,7 +281,7 @@ class Tree(DictSWC):
293
281
  Parameters
294
282
  ----------
295
283
  enter : (n: Node, parent: T | None) => T, optional
296
- leave : (n: Node, children: List[T]) => T, optional
284
+ leave : (n: Node, children: list[T]) => T, optional
297
285
 
298
286
  See Also
299
287
  --------
@@ -355,7 +343,7 @@ class Tree(DictSWC):
355
343
 
356
344
  @classmethod
357
345
  def from_eswc(
358
- cls, swc_file: str, extra_cols: Optional[List[str]] = None, **kwargs
346
+ cls, swc_file: str, extra_cols: Optional[list[str]] = None, **kwargs
359
347
  ) -> "Tree":
360
348
  """Read neuron tree from eswc file.
361
349
 
@@ -1,7 +1,8 @@
1
1
  """SWC util wrapper for tree."""
2
2
 
3
3
  import warnings
4
- from typing import Callable, Dict, Iterable, List, Optional, Tuple, TypeVar, overload
4
+ from collections.abc import Callable, Iterable
5
+ from typing import Optional, TypeVar, overload
5
6
 
6
7
  import numpy as np
7
8
 
@@ -50,9 +51,9 @@ def sort_tree(tree: Tree) -> Tree:
50
51
 
51
52
  # fmt:off
52
53
  @overload
53
- def cut_tree(tree: Tree, *, enter: Callable[[Tree.Node, T | None], Tuple[T, bool]]) -> Tree: ...
54
+ def cut_tree(tree: Tree, *, enter: Callable[[Tree.Node, T | None], tuple[T, bool]]) -> Tree: ...
54
55
  @overload
55
- def cut_tree(tree: Tree, *, leave: Callable[[Tree.Node, List[K]], Tuple[K, bool]]) -> Tree: ...
56
+ def cut_tree(tree: Tree, *, leave: Callable[[Tree.Node, list[K]], tuple[K, bool]]) -> Tree: ...
56
57
  # fmt:on
57
58
  def cut_tree(tree: Tree, *, enter=None, leave=None):
58
59
  """Traverse and cut the tree.
@@ -60,11 +61,11 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
60
61
  Returning a `True` can delete the current node and its children.
61
62
  """
62
63
 
63
- removals: List[int] = []
64
+ removals: list[int] = []
64
65
 
65
66
  if enter:
66
67
 
67
- def _enter(n: Tree.Node, parent: Tuple[T, bool] | None) -> Tuple[T, bool]:
68
+ def _enter(n: Tree.Node, parent: tuple[T, bool] | None) -> tuple[T, bool]:
68
69
  if parent is not None and parent[1]:
69
70
  removals.append(n.id)
70
71
  return parent
@@ -79,7 +80,7 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
79
80
 
80
81
  elif leave:
81
82
 
82
- def _leave(n: Tree.Node, children: List[K]) -> K:
83
+ def _leave(n: Tree.Node, children: list[K]) -> K:
83
84
  res, removal = leave(n, children)
84
85
  if removal:
85
86
  removals.append(n.id)
@@ -94,7 +95,7 @@ def cut_tree(tree: Tree, *, enter=None, leave=None):
94
95
  return to_subtree(tree, removals)
95
96
 
96
97
 
97
- def to_sub_tree(swc_like: SWCLike, sub: Topology) -> Tuple[Tree, Dict[int, int]]:
98
+ def to_sub_tree(swc_like: SWCLike, sub: Topology) -> tuple[Tree, dict[int, int]]:
98
99
  """Create subtree from origin tree.
99
100
 
100
101
  You can directly mark the node for removal, and we will remove it,
@@ -107,7 +108,7 @@ def to_sub_tree(swc_like: SWCLike, sub: Topology) -> Tuple[Tree, Dict[int, int]]
107
108
  Returns
108
109
  -------
109
110
  tree : Tree
110
- id_map : Dict[int, int]
111
+ id_map : dict[int, int]
111
112
  """
112
113
 
113
114
  warnings.warn(
@@ -145,7 +146,7 @@ def to_subtree(
145
146
  swc_like : SWCLike
146
147
  removals : List of int
147
148
  A list of id of nodes to be removed.
148
- out_mapping: List of int or Dict[int, int], optional
149
+ out_mapping: List of int or dict[int, int], optional
149
150
  Map new id to old id.
150
151
  """
151
152
 
@@ -170,7 +171,7 @@ def get_subtree(
170
171
  swc_like : SWCLike
171
172
  n : int
172
173
  Id of the root of the subtree.
173
- out_mapping: List of int or Dict[int, int], optional
174
+ out_mapping: List of int or dict[int, int], optional
174
175
  Map new id to old id.
175
176
  """
176
177
 
@@ -5,7 +5,7 @@ Notes
5
5
  Do not import `Tree` and keep this file minimized.
6
6
  """
7
7
 
8
- from typing import Any, Dict, List, Optional, Tuple
8
+ from typing import Any, Optional
9
9
 
10
10
  import numpy as np
11
11
  import numpy.typing as npt
@@ -15,8 +15,8 @@ from swcgeom.core.swc_utils import Topology, to_sub_topology, traverse
15
15
 
16
16
  __all__ = ["get_subtree_impl", "to_subtree_impl"]
17
17
 
18
- Mapping = Dict[int, int] | List[int]
19
- TreeArgs = Tuple[int, Dict[str, npt.NDArray[Any]], str, SWCNames]
18
+ Mapping = dict[int, int] | list[int]
19
+ TreeArgs = tuple[int, dict[str, npt.NDArray[Any]], str, SWCNames]
20
20
 
21
21
 
22
22
  def get_subtree_impl(
@@ -6,7 +6,7 @@ This is expremental code, and the API is subject to change.
6
6
  """
7
7
 
8
8
  import random
9
- from typing import List, Literal, Optional
9
+ from typing import Literal, Optional
10
10
 
11
11
  import numpy as np
12
12
  import numpy.typing as npt
@@ -54,7 +54,7 @@ class Augmentation:
54
54
 
55
55
  def swapaxes(self, x, mode: Optional[Literal["xy", "xz", "yz"]] = None) -> NDArrf32:
56
56
  if mode is None:
57
- modes: List[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
57
+ modes: list[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
58
58
  mode = modes[self.rand.randint(0, 2)]
59
59
 
60
60
  match mode:
@@ -69,7 +69,7 @@ class Augmentation:
69
69
 
70
70
  def flip(self, x, mode: Optional[Literal["xy", "xz", "yz"]] = None) -> NDArrf32:
71
71
  if mode is None:
72
- modes: List[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
72
+ modes: list[Literal["xy", "xz", "yz"]] = ["xy", "xz", "yz"]
73
73
  mode = modes[random.randint(0, 2)]
74
74
 
75
75
  match mode: