Skip to content

Core Classes

The core classes provide the foundational architecture for neuromorphological data management in ossify.

Cell

Cell

Cell(name: Optional[Union[int, str]] = None, morphsync: Optional[MorphSync] = None, meta: Optional[dict] = None, annotation_layers: Optional[list] = None)

Methods:

  • add_layer –

    Add a initialized layer to the MorphSync.

  • add_mesh –

    Add a mesh layer to the MorphSync.

  • add_skeleton –

    Add a skeleton layer to the MorphSync.

  • add_graph –

    Add the core graph layer to a MeshWork object.

  • add_point_annotations –

    Add point annotations to the MeshWork object. This is intended for annotations which are typically sparse and represent specific features, unlike general point clouds that represent the morphology of the cell.

  • add_point_layer –

    Add point layer to the MeshWork object. This is intended for general point clouds that represent the morphology of the cell, unlike annotations which are typically sparse and represent specific features.

  • apply_mask –

    Create a new Cell with vertices masked out.

  • copy –

    Create a deep copy of the Cell.

  • transform –

    Apply a spatial transformation to all spatial layers in the Cell.

  • get_features –

    Map feature columns from various sources to a target layer.

  • mask_context –

    Create a masked version of the MeshWork object in a context state.

  • remove_layer –

    Remove a morphological layer from the Cell.

  • remove_annotation –

    Remove an annotation layer from the Cell.

  • describe –

    Generate a hierarchical summary description of the cell and its layers.

Attributes:

name property

name: str

Get the name of the cell (typically a segment id)

meta property

meta: dict

Get the metadata associated with the cell.

layers property

layers: LayerManager

Get the non-annotation layers of the cell.

skeleton property

Skeleton layer for the cell, if present. Otherwise, None.

graph property

Graph layer for the cell, if present. Otherwise, None.

mesh property

Mesh layer for the cell, if present. Otherwise, None.

annotations property

annotations: LayerManager

Annotation Manager for the cell, holding all annotation layers.

s property

Alias for skeleton.

g property

Alias for graph.

m property

Alias for mesh.

a property

Alias for annotations.

l property

Alias for layers.

features property

features: DataFrame

Return a DataFrame listing all feature columns across all layers. Each feature is a row, with the layer name and feature name as columns.

add_layer

Add a initialized layer to the MorphSync.

Parameters:

Raises:

  • ValueError –

    If the layer already exists or if the layer type is incorrect.

add_mesh

add_mesh(vertices: Union[ndarray, DataFrame, MeshLayer], faces: Optional[Union[ndarray, DataFrame]] = None, features: Optional[Union[dict, DataFrame]] = None, *, vertex_index: Optional[Union[str, ndarray]] = None, faces_as_positional: Optional[bool] = None, linkage: Optional[Link] = None, spatial_columns: Optional[list] = None) -> Self

Add a mesh layer to the MorphSync.

Parameters:

  • vertices (Union[ndarray, DataFrame, MeshLayer]) –

    The vertices of the mesh, or a MeshLayer object.

  • faces (Union[ndarray, DataFrame], default: None ) –

    The triangular faces of the mesh. If vertex_index is given these are positional indices into vertices and are remapped onto the vertex index for you; otherwise they are vertex indices already -- the index of vertices when it is a DataFrame, or positional when it is a plain array. Mesh formats such as OBJ and PLY give positional faces, so vertex_index is usually what you want when labelling mesh vertices with ids.

  • features (Optional[Union[dict, DataFrame]], default: None ) –

    Additional features for the mesh. If passed as dictionary, the key is the feature name and the values are an array of feature values.

  • vertex_index (Optional[Union[str, ndarray]], default: None ) –

    Vertex indices to label the mesh's vertices by: either the name of a column in vertices, or an array with one value per vertex. Supplying this changes how faces are read -- see above.

  • faces_as_positional (Optional[bool], default: None ) –

    Whether faces are positional indices into vertices. The default, None, infers it as described above: positional exactly when vertex_index is supplied. Pass it explicitly when the two spaces overlap -- a vertex_index whose values are also valid positions cannot be told apart any other way.

  • linkage (Optional[Link], default: None ) –

    The linkage information for the mesh.

  • spatial_columns (Optional[list], default: None ) –

    The spatial columns for the mesh, if vertices are a dataframe.

Returns:

add_skeleton

add_skeleton(vertices: Union[ndarray, DataFrame, SkeletonLayer], edges: Optional[Union[ndarray, DataFrame]] = None, features: Optional[Union[dict, DataFrame]] = None, root: Optional[int] = None, *, vertex_index: Optional[Union[str, ndarray]] = None, edges_as_positional: Optional[bool] = None, root_as_positional: bool = False, linkage: Optional[Link] = None, spatial_columns: Optional[list] = None, inherited_properties: Optional[dict] = None) -> Self

Add a skeleton layer to the MorphSync.

Parameters:

  • vertices (Union[ndarray, DataFrame, SkeletonLayer]) –

    The vertices of the skeleton, or a SkeletonLayer object.

  • edges (Union[ndarray, DataFrame], default: None ) –

    The edges of the skeleton, as pairs of vertices. If vertex_index is given these are positional indices into vertices and are remapped onto the vertex index for you; otherwise they are vertex indices already -- the index of vertices when it is a DataFrame, or positional when it is a plain array.

  • features (Optional[Union[dict, DataFrame]], default: None ) –

    The features for the skeleton.

  • root (Optional[int], default: None ) –

    The root of the skeleton, as a vertex index -- note this is the opposite convention to edges, which are positional when vertex_index is given. Required if the edges are not already consistent with a single root.

  • root_as_positional (bool, default: False ) –

    Whether root is a positional index into vertices rather than a vertex index. Default False. This is the escape hatch for the asymmetry noted above: edges and root keep different defaults, but either can be stated explicitly.

  • vertex_index (Optional[Union[str, ndarray]], default: None ) –

    Vertex indices to label the skeleton's vertices by: either the name of a column in vertices, or an array with one value per vertex. Supplying this changes how edges are read -- see above.

  • edges_as_positional (Optional[bool], default: None ) –

    Whether edges are positional indices into vertices. The default, None, infers it as described above: positional exactly when vertex_index is supplied. Pass it explicitly when the two spaces overlap -- a vertex_index whose values are also valid positions cannot be told apart any other way.

  • linkage (Optional[Link], default: None ) –

    The linkage information for the skeleton. Typically, you will define the source vertices for the skeleton if using a graph-to-skeleton mapping.

  • spatial_columns (Optional[list], default: None ) –

    The spatial columns for the skeleton, if vertices are a dataframe.

Returns:

add_graph

add_graph(vertices: Union[ndarray, DataFrame, SkeletonLayer], edges: Optional[Union[ndarray, DataFrame]] = None, features: Optional[Union[dict, DataFrame]] = None, *, vertex_index: Optional[Union[str, ndarray]] = None, edges_as_positional: Optional[bool] = None, spatial_columns: Optional[list] = None, linkage: Optional[Link] = None) -> Self

Add the core graph layer to a MeshWork object. Additional graph layers can be used, but they must be added separately and with unique names.

Parameters:

  • vertices (Union[ndarray, DataFrame, SkeletonLayer]) –

    The vertices of the graph.

  • edges (Union[ndarray, DataFrame], default: None ) –

    The edges of the graph, as pairs of vertices. If vertex_index is given these are positional indices into vertices and are remapped onto the vertex index for you; otherwise they are vertex indices already -- the index of vertices when it is a DataFrame, or positional when it is a plain array.

  • features (Optional[Union[dict, DataFrame]], default: None ) –

    The features for the graph.

  • vertex_index (Optional[Union[str, ndarray]], default: None ) –

    Vertex indices to label the graph's vertices by: either the name of a column in vertices, or an array with one value per vertex. Supplying this changes how edges are read -- see above.

  • edges_as_positional (Optional[bool], default: None ) –

    Whether edges are positional indices into vertices. The default, None, infers it as described above: positional exactly when vertex_index is supplied. Pass it explicitly when the two spaces overlap -- a vertex_index whose values are also valid positions cannot be told apart any other way.

  • spatial_columns (Optional[list], default: None ) –

    The spatial columns for the graph, if vertices are a dataframe.

Returns:

add_point_annotations

add_point_annotations(name: str, vertices: Optional[Union[ndarray, DataFrame]] = None, spatial_columns: Optional[list] = None, *, vertex_index: Optional[Union[str, ndarray]] = None, features: Optional[Union[dict, DataFrame]] = None, linkage: Optional[Link] = None, vertices_from_linkage: bool = False) -> Self

Add point annotations to the MeshWork object. This is intended for annotations which are typically sparse and represent specific features, unlike general point clouds that represent the morphology of the cell.

Parameters:

  • name (str) –

    The name of the annotation layer.

  • vertices (Union[ndarray, DataFrame], default: None ) –

    The vertices of the annotation layer.

  • spatial_columns (Optional[list], default: None ) –

    The spatial columns for the annotation layer.

  • vertex_index (Optional[Union[str, ndarray]], default: None ) –

    The vertex index for the annotation layer.

  • features (Optional[Union[dict, DataFrame]], default: None ) –

    The features for the annotation layer.

  • linkage (Optional[Link], default: None ) –

    The linkage information for the annotation layer. Typically, you will define the target vertices for annotations.

  • vertices_from_linkage (bool, default: False ) –

    If True, the vertices will be inferred from the linkage mapping rather than the provided vertices. This is useful if you want to create an annotation layer that directly maps to another layer without providing separate vertex coordinates.

Returns:

add_point_layer

add_point_layer(name: str, vertices: Union[ndarray, DataFrame], spatial_columns: Optional[list] = None, *, vertex_index: Optional[Union[str, ndarray]] = None, features: Optional[Union[dict, DataFrame]] = None, linkage: Optional[Link] = None) -> Self

Add point layer to the MeshWork object. This is intended for general point clouds that represent the morphology of the cell, unlike annotations which are typically sparse and represent specific features.

Parameters:

  • name (str) –

    The name of the point layer.

  • vertices (Union[ndarray, DataFrame]) –

    The vertices of the point layer.

  • spatial_columns (Optional[list], default: None ) –

    The spatial columns for the point layer.

  • vertex_index (Optional[Union[str, ndarray]], default: None ) –

    The vertex index for the annotation layer.

  • features (Optional[Union[dict, DataFrame]], default: None ) –

    The features for the annotation layer.

  • linkage (Optional[Link], default: None ) –

    The linkage information for the annotation layer. Typically, you will define the target vertices for annotations.

Returns:

apply_mask

apply_mask(layer: str, mask: ndarray, as_positional: bool = False) -> Self

Create a new Cell with vertices masked out.

Parameters:

  • layer (str) –

    The layer name that the mask is based on.

  • mask (ndarray) –

    The mask to apply. Values that are True are preserved, while values that are False are discarded. Can be a boolean array or an array of vertices.

  • as_positional (bool, default: False ) –

    If mask is an array of vertices, this sets whether indices are in dataframe indices or as_positional indices.

Returns:

copy

copy() -> Self

Create a deep copy of the Cell.

transform

transform(transform: Union[ndarray, callable], inplace: bool = False) -> Self

Apply a spatial transformation to all spatial layers in the Cell.

Parameters:

  • transform (Union[ndarray, callable]) –

    If an array, must be the same shape as the vertices of the layer(s). If a callable, must take in a (N, 3) array and return a (N, 3) array.

  • inplace (bool, default: False ) –

    If True, modify the current Cell. If False, return a new Cell.

Returns:

  • Self –

    The transformed Cell.

get_features

get_features(features: Union[str, list], target_layer: str, source_layers: Optional[Union[str, list]] = None, agg: Union[str, dict] = 'median') -> DataFrame

Map feature columns from various sources to a target layer.

Parameters:

  • features (Union[str, list]) –

    The features to map from the source layer.

  • target_layer (str) –

    The target layer to map all features to.

  • source_layers (Optional[Union[str, list]], default: None ) –

    The source layers to map the features from. Unnecessary if features are unique.

  • agg (Union[str, dict], default: 'median' ) –

    The aggregation method to use when mapping the features. Anything pandas groupby.agg takes, as well as "majority" which will is a majority vote across the mapped indices via the stats.mode function.

Returns:

  • DataFrame –

    The mapped features for the target layer.

mask_context

mask_context(layer: str, mask: ndarray) -> Generator[Self, None, None]

Create a masked version of the MeshWork object in a context state.

The masked Cell is a temporary scoped to the with block: it is torn down when the block exits (including on error), so do not retain it or any of its layers past the context.

Parameters:

  • layer (str) –

    The name of the layer to which the mask applies.

  • mask (array or None) –

    A boolean array with the same number of elements as mesh vertices. True elements are kept, False are masked out.

Example

with mesh.mask_context("layer_name", mask) as masked_mesh: result = my_favorite_function(masked_mesh)

remove_layer

remove_layer(name: str) -> Self

Remove a morphological layer from the Cell.

Parameters:

  • name (str) –

    The name of the layer to remove

Returns:

  • Self –

    The Cell object for method chaining

Raises:

  • ValueError –

    If the layer does not exist or is a core layer that cannot be removed

remove_annotation

remove_annotation(name: str) -> Self

Remove an annotation layer from the Cell.

Parameters:

  • name (str) –

    The name of the annotation to remove

Returns:

  • Self –

    The Cell object for method chaining

Raises:

describe

describe(html: bool = False, coverage: bool = True) -> None

Generate a hierarchical summary description of the cell and its layers.

Provides a tree-like overview including: - Cell name and basic info - Layers section with expandable details - Annotations section with expandable details

Parameters:

  • html (bool, default: False ) –

    If True, create expandable HTML widgets in Jupyter. If False, print formatted string (default).

  • coverage (bool, default: True ) –

    Annotate links whose mapping is incomplete with how much of each side is covered. Default True. This walks the link tables, so it costs O(vertices) per link -- negligible on a normal cell, a couple of seconds on one with millions of mesh vertices. Pass False to skip it.

Returns:

  • None –

    Always returns None (prints text or displays HTML widgets)

Examples:

>>> cell.describe()  # Prints formatted string
>>> cell.describe(html=True)  # Shows HTML widgets in Jupyter
>>> cell.describe(coverage=False)  # Skip the mapping-coverage walk

Link(mapping: Union[list[int], str], source: Optional[str] = None, target: Optional[str] = None, map_value_is_index: bool = True)

Represents the linkage mapping information.

Parameters:

  • mapping (Union[list[int], str]) –

    The mapping information between the source and target layers. The mapping will have the same length as the source vertices and the values will be the corresponding target vertex indices. If map_value_is_index is True, the mapping values should the dataframe index for target vertices (i.e. df.index), if False they are the positional indices (0-N). If a string, the mapping is the column name in the target's vertex dataframe.

  • source (Optional[str], default: None ) –

    The name of the source layer, typically the one with more vertices than the target. E.g. a graph or mesh to a skeleton, or a graph or skeleton to point annotations. If not provided, will be set to the layer name of the object this is being added to. Either source or target can be undefined, but not both.

  • target (Optional[str], default: None ) –

    The name of the target layer, typically the one with fewer vertices. E.g. a skeleton from a graph or mesh, or point annotations from a skeleton. If not provided, will be set to the layer name of the object this is being added to. Either source or target can be undefined, but not both.

  • map_value_is_index (bool, default: True ) –

    If True, assumes the values in the list or the mapping are a non-positional dataframe index.

Methods:

mapping_to_index

mapping_to_index(vertex_data: DataFrame) -> ndarray

Map positional values to index values


Manager Classes

These classes are used internally by Cell to organize and access different types of data layers. Users should not need to interact with these directly.

LayerManager

LayerManager

LayerManager(managed_layers: Optional[dict] = None, validation: Union[str, Callable] = 'any', context: str = 'layer', initial_layers: Optional[list] = None)

Unified manager for both morphological layers and annotations with flexible validation.

Initialize the unified layer manager.

Parameters:

  • managed_layers (dict, default: None ) –

    Dictionary to store layers in. If None, creates new dict.

  • validation (str or callable, default: 'any' ) –

    Type validation mode: - 'any': Accept any layer type - 'point_cloud_only': Only accept PointCloudLayer - callable: Custom validation function

  • context (str, default: 'layer' ) –

    Context name for error messages ('layer', 'annotation', etc.)

  • initial_layers (list, default: None ) –

    Initial layers to add (with validation)

Methods:

  • get –

    Get a layer by name with optional default.

  • describe –

    Generate a detailed description of all managed layers.

Attributes:

  • names (list) –

    Return a list of managed layer names.

names property
names: list

Return a list of managed layer names.

get
get(name: str, default: Any = None)

Get a layer by name with optional default.

describe
describe() -> None

Generate a detailed description of all managed layers.

Shows each layer with its metrics, features, and links - same level of detail as individual layer describe() methods.

Returns:

  • None –

    Always returns None (prints formatted text)

AnnotationManager

AnnotationManager module-attribute

AnnotationManager = partial(LayerManager, validation='point_cloud_only', context='annotation')

Usage Examples

Creating and Working with Cells

import ossify
import numpy as np

# Create an empty cell
cell = ossify.Cell(name="example_neuron")

# Add a skeleton layer
vertices = np.random.rand(100, 3) * 1000  # Random 3D points
edges = np.column_stack([np.arange(99), np.arange(1, 100)])  # Linear chain
cell.add_skeleton(vertices=vertices, edges=edges, root=0)

# Add a mesh layer  
mesh_vertices = np.random.rand(200, 3) * 1000
faces = np.random.randint(0, 200, (150, 3))  # Random triangular faces
cell.add_mesh(vertices=mesh_vertices, faces=faces)

# Add point annotations
annotation_points = np.random.rand(50, 3) * 1000
cell.add_point_annotations(
    name="synapses", 
    vertices=annotation_points
)

Layer Access and Management

# Access layers by name
skeleton = cell.layers["skeleton"]  
mesh = cell.layers["mesh"]

# Access via properties
skeleton = cell.skeleton
mesh = cell.mesh

# Access annotations
synapses = cell.annotations["synapses"]
synapse_locations = cell.annotations.synapses  # Alternative syntax

# List all available layers
print("Morphological layers:", cell.layers.names)
print("Annotation layers:", cell.annotations.names)
print("All features across layers:")
print(cell.features)

Data Linking and Mapping

# Create a link between skeleton and annotations
from ossify import Link

# Map synapses to nearest skeleton vertices
link = Link(
    mapping=skeleton_vertex_ids,  # Array mapping synapses to skeleton vertices
    source="synapses",
    target="skeleton"
)

cell.add_point_annotations(
    name="linked_synapses",
    vertices=annotation_points,
    linkage=link
)

# Map features between layers
compartment_features = cell.get_features(
    features=["compartment"], 
    target_layer="skeleton",
    source_layers=["mesh"],
    agg="majority"  # Use majority vote for mapping
)

Masking and Filtering

# Apply a mask to create a subset
axon_mask = skeleton.get_feature("is_axon") == True
axon_cell = cell.apply_mask("skeleton", axon_mask)

# Temporary masking with context manager
with cell.mask_context("skeleton", axon_mask) as masked_cell:
    # Work with axon-only data
    axon_length = masked_cell.skeleton.cable_length()
    print(f"Axon length: {axon_length:.2f}")

# The original cell is unchanged after exiting context

Spatial Transformations

# Apply a transformation matrix
transform_matrix = np.array([
    [1, 0, 0],    # Scale x by 1
    [0, 1, 0],    # Scale y by 1  
    [0, 0, 2]     # Scale z by 2
])

# Transform all spatial layers
transformed_cell = cell.transform(transform_matrix)

# Or apply in-place
cell.transform(transform_matrix, inplace=True)

# Apply custom transformation function
def rotate_90_degrees(vertices):
    \"\"\"Rotate 90 degrees around z-axis\"\"\"
    rotation = np.array([
        [0, -1, 0],
        [1,  0, 0], 
        [0,  0, 1]
    ])
    return vertices @ rotation.T

rotated_cell = cell.transform(rotate_90_degrees)

Key Design Principles

Consistent Interface: All layer types share common methods for transformations, masking, and feature management.

Method Chaining: Most methods return self to enable fluent interfaces: cell.add_skeleton(...).add_mesh(...)

Non-destructive Operations: Operations like apply_mask() return new objects, preserving the original data.

Flexible Indexing: Support both DataFrame indices (arbitrary integers) and positional indices (0-based arrays).

Performance Tips

  • Use mask_context() for temporary operations: it creates a masked copy scoped to the with block and releases it automatically on exit, so short-lived masked cells don't accumulate in memory
  • Leverage the LinkageSystem for efficient cross-layer operations
  • Cache expensive computations using layer properties like csgraph