Skip to content

Commit

Permalink
refactor!: rename bifurcation to furcation
Browse files Browse the repository at this point in the history
  • Loading branch information
yzx9 committed Apr 14, 2024
1 parent 1f332f2 commit 26e9dec
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 30 deletions.
15 changes: 12 additions & 3 deletions swcgeom/analysis/feature_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from matplotlib.axes import Axes

from swcgeom.analysis.features import (
BifurcationFeatures,
BranchFeatures,
FurcationFeatures,
NodeFeatures,
PathFeatures,
TipFeatures,
Expand All @@ -40,6 +40,9 @@
"node_count",
"node_radial_distance",
"node_branch_order",
# furcation nodes
"furcation_count",
"furcation_radial_distance",
# bifurcation nodes
"bifurcation_count",
"bifurcation_radial_distance",
Expand All @@ -57,7 +60,7 @@
NDArrayf32 = npt.NDArray[np.float32]
FeatAndKwargs = Feature | tuple[Feature, dict[str, Any]]

Feature1D = set(["length", "volume", "node_count", "bifurcation_count", "tip_count"])
Feature1D = set(["length", "volume", "node_count", "furcation_count", "tip_count"])


class Features:
Expand All @@ -70,7 +73,7 @@ class Features:
@cached_property
def node_features(self) -> NodeFeatures: return NodeFeatures(self.tree)
@cached_property
def bifurcation_features(self) -> BifurcationFeatures: return BifurcationFeatures(self.node_features)
def furcation_features(self) -> FurcationFeatures: return FurcationFeatures(self.node_features)
@cached_property
def tip_features(self) -> TipFeatures: return TipFeatures(self.node_features)
@cached_property
Expand Down Expand Up @@ -214,6 +217,12 @@ def _plot_histogram_impl(
) -> Axes:
raise NotImplementedError()

def get_bifurcation_count(self, **kwargs):
raise DeprecationWarning("Use `furcation_count` instead.")

def get_bifurcation_radial_distance(self, **kwargs):
raise DeprecationWarning("Use `furcation_radial_distance` instead.")


class TreeFeatureExtractor(FeatureExtractor):
"""Extract feature from tree."""
Expand Down
20 changes: 16 additions & 4 deletions swcgeom/analysis/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import numpy as np
import numpy.typing as npt
from typing_extensions import Self
from typing_extensions import Self, deprecated

from swcgeom.core import Branch, BranchTree, Tree

Expand Down Expand Up @@ -121,12 +121,24 @@ def from_tree(cls, tree: Tree) -> Self:
return cls(NodeFeatures(tree))


class BifurcationFeatures(_SubsetNodesFeatures):
"""Evaluate bifurcation node feature of tree."""
class FurcationFeatures(_SubsetNodesFeatures):
"""Evaluate furcation node feature of tree."""

@cached_property
def nodes(self) -> npt.NDArray[np.bool_]:
return np.array([n.is_bifurcation() for n in self._features.tree])
return np.array([n.is_furcation() for n in self._features.tree])


@deprecated("Use FurcationFeatures instead")
class BifurcationFeatures(FurcationFeatures):
"""Evaluate bifurcation node feature of tree.
Notes
-----
Deprecated due to the wrong spelling of furcation. For now, it
is just an alias of `FurcationFeatures` and raise a warning. It
will be change to raise an error in the future.
"""


class TipFeatures(_SubsetNodesFeatures):
Expand Down
8 changes: 4 additions & 4 deletions swcgeom/analysis/lmeasure.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def n_bifs(self, tree: Tree) -> int:
--------
L-Measure: http://cng.gmu.edu:8080/Lm/help/N_bifs.htm
"""
return len(tree.get_bifurcations())
return len(tree.get_furcations())

def n_branch(self, tree: Tree) -> int:
"""Number of branches.
Expand Down Expand Up @@ -339,7 +339,7 @@ def pk_2(self, bif: Tree.Node) -> float:
return (da**rall_power + db**rall_power) / dp**rall_power

def bif_ampl_local(self, bif: Tree.Node) -> float:
"""Bifuraction angle.
"""Bifurcation angle.
Given a bifurcation, this function returns the angle between
the first two compartments (in degree).
Expand All @@ -353,7 +353,7 @@ def bif_ampl_local(self, bif: Tree.Node) -> float:
return np.degrees(angle(v1, v2))

def bif_ampl_remote(self, bif: Tree.Node) -> float:
"""Bifuraction angle.
"""Bifurcation angle.
This function returns the angle between two bifurcation points
or between bifurcation point and terminal point or between two
Expand Down Expand Up @@ -668,7 +668,7 @@ def branch_order(self, node: Tree.Node) -> int:
n = node
order = 0
while n is not None:
if n.is_bifurcation():
if n.is_furcation():
order += 1
n = n.parent()
return order
Expand Down
16 changes: 15 additions & 1 deletion swcgeom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import numpy as np
import numpy.typing as npt
from typing_extensions import deprecated

from swcgeom.core.swc import DictSWC, SWCTypeVar
from swcgeom.core.swc_utils import SWCNames
Expand Down Expand Up @@ -95,9 +96,22 @@ def format_swc(self) -> str:
items = [self.id, self.type, x, y, z, r, self.pid]
return " ".join(map(str, items))

def is_bifurcation(self) -> bool:
def is_furcation(self) -> bool:
"""Is furcation node."""
return np.count_nonzero(self.attach.pid() == self.id) > 1

@deprecated("Use is_furcation instead")
def is_bifurcation(self) -> bool:
"""Is furcation node.
Notes
-----
Deprecated due to the wrong spelling of furcation. For now, it
is just an alias of `is_furcation` and raise a warning. It will
be change to raise an error in the future.
"""
return self.is_furcation()

def is_tip(self) -> bool:
return self.id not in self.attach.pid()

Expand Down
2 changes: 1 addition & 1 deletion swcgeom/core/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class Path(SWCLike, Generic[SWCTypeVar]):
"""Neuron path.
A path is a linear set of points without bifurcations.
A path is a linear set of points without furcations.
"""

attach: SWCTypeVar
Expand Down
31 changes: 22 additions & 9 deletions swcgeom/core/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import numpy as np
import numpy.typing as npt
import pandas as pd
from typing_extensions import deprecated

from swcgeom.core.branch import Branch
from swcgeom.core.compartment import Compartment, Compartments
Expand Down Expand Up @@ -38,11 +39,11 @@ def children(self) -> list["Tree.Node"]:

def branch(self) -> "Tree.Branch":
ns: list["Tree.Node"] = [self]
while not ns[-1].is_bifurcation() and (p := ns[-1].parent()) is not None:
while not ns[-1].is_furcation() and (p := ns[-1].parent()) is not None:
ns.append(p)

ns.reverse()
while not (ns[-1].is_bifurcation() or ns[-1].is_tip()):
while not (ns[-1].is_furcation() or ns[-1].is_tip()):
ns.append(ns[-1].children()[0])

return Tree.Branch(self.attach, [n.id for n in ns])
Expand Down Expand Up @@ -187,16 +188,28 @@ def soma(self, type_check: bool = True) -> Node:
raise ValueError(f"no soma found in: {self.source}")
return n

def get_bifurcations(self) -> list[Node]:
"""Get all node of bifurcations."""
bifurcations: list[int] = []
def get_furcations(self) -> list[Node]:
"""Get all node of furcations."""
furcations: list[int] = []

def collect_bifurcations(n: Tree.Node, children: list[None]) -> None:
def collect_furcations(n: Tree.Node, children: list[None]) -> None:
if len(children) > 1:
bifurcations.append(n.id)
furcations.append(n.id)

self.traverse(leave=collect_furcations)
return [self.node(i) for i in furcations]

self.traverse(leave=collect_bifurcations)
return [self.node(i) for i in bifurcations]
@deprecated("Use `get_furcations` instead")
def get_bifurcations(self) -> list[Node]:
"""Get all node of furcations.
Notes
-----
Deprecated due to the wrong spelling of furcation. For now, it
is just an alias of `get_furcations` and raise a warning. It
will be change to raise an error in the future.
"""
return self.get_furcations()

def get_tips(self) -> list[Node]:
"""Get all node of tips."""
Expand Down
36 changes: 28 additions & 8 deletions swcgeom/transforms/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"CutByType",
"CutAxonTree",
"CutDendriteTree",
"CutByBifurcationOrder",
"CutByFurcationOrder",
"CutShortTipBranch",
]

Expand Down Expand Up @@ -124,28 +124,48 @@ def __init__(self, types: Optional[SWCTypes] = None) -> None:
super().__init__(type=types.basal_dendrite) # TODO: apical dendrite


class CutByBifurcationOrder(Transform[Tree, Tree]):
"""Cut tree by bifurcation order."""
class CutByFurcationOrder(Transform[Tree, Tree]):
"""Cut tree by furcation order."""

max_bifurcation_order: int
max_furcation_order: int

def __init__(self, max_bifurcation_order: int) -> None:
self.max_bifurcation_order = max_bifurcation_order
self.max_furcation_order = max_bifurcation_order

def __call__(self, x: Tree) -> Tree:
return cut_tree(x, enter=self._enter)

def __repr__(self) -> str:
return f"CutByBifurcationOrder-{self.max_bifurcation_order}"
return f"CutByBifurcationOrder-{self.max_furcation_order}"

def _enter(self, n: Tree.Node, parent_level: int | None) -> tuple[int, bool]:
if parent_level is None:
level = 0
elif n.is_bifurcation():
elif n.is_furcation():
level = parent_level + 1
else:
level = parent_level
return (level, level >= self.max_bifurcation_order)
return (level, level >= self.max_furcation_order)


@deprecated("Use CutByFurcationOrder instead")
class CutByBifurcationOrder(CutByFurcationOrder):
"""Cut tree by bifurcation order.
Notes
-----
Deprecated due to the wrong spelling of furcation. For now, it
is just an alias of `CutByFurcationOrder` and raise a warning. It
will be change to raise an error in the future.
"""

max_furcation_order: int

def __init__(self, max_bifurcation_order: int) -> None:
super().__init__(max_bifurcation_order)

def __repr__(self) -> str:
return f"CutByBifurcationOrder-{self.max_furcation_order}"


class CutShortTipBranch(Transform[Tree, Tree]):
Expand Down

0 comments on commit 26e9dec

Please sign in to comment.