placecell.maze_helper#

Maze behavior processing for 1D arm analysis.

Functions

assign_traversal_direction(trajectory, arm_order)

Split arm traversals into forward/reverse directional segments.

compute_arm_lengths(polylines, mm_per_pixel)

Compute polyline lengths for each zone.

compute_speed_1d(trajectory, *, ...)

Compute 1D speed along the arm axis using a centered window.

filter_complete_traversals(trajectory, ...)

Remove traversals where the animal did not cross from one room to another.

load_graph_polylines(graph_path)

Load behavior graph YAML and return raw polyline waypoints.

serialize_arm_position(trajectory, arm_order)

Convert zone + arm_position to a concatenated 1D position.

placecell.maze_helper.load_graph_polylines(graph_path: Path) dict[str, list[list[float]]]#

Load behavior graph YAML and return raw polyline waypoints.

Parameters:

graph_path (Path) – Path to YAML file in combined zone format (zones key with type, points, and optionally connections per zone).

Returns:

Maps zone name to list of [x, y] waypoints in pixel coords.

Return type:

dict[str, list[list[float]]]

placecell.maze_helper.compute_arm_lengths(polylines: dict[str, list[list[float]]], mm_per_pixel: float) dict[str, float]#

Compute polyline lengths for each zone.

Parameters:
  • polylines (dict[str, list[list[float]]]) – Maps zone name to list of [x, y] waypoints in pixel coords.

  • mm_per_pixel (float) – Scale factor for converting pixel distances to mm.

Returns:

Maps zone name to physical length in mm.

Return type:

dict[str, float]

placecell.maze_helper.serialize_arm_position(trajectory: DataFrame, arm_order: list[str], zone_column: str = 'zone', arm_position_column: str = 'arm_position', arm_lengths: dict[str, float] | None = None) DataFrame#

Convert zone + arm_position to a concatenated 1D position.

Filters to arm-only frames and maps each arm’s [0,1] position onto a concatenated 1D axis. When arm_lengths is provided the axis is in physical units (mm) so that each arm spans its real length; otherwise each arm spans exactly 1 unit.

Parameters:
  • trajectory (DataFrame) – DataFrame with zone, arm_position, and standard columns.

  • arm_order (list[str]) – Ordered list of arm zone names.

  • zone_column (str) – Column containing zone labels.

  • arm_position_column (str) – Column containing within-arm position (0-1).

  • arm_lengths (dict[str, float] | None) – Optional dict mapping zone name to physical length. When provided, pos_1d is in physical units.

Returns:

Filtered to arm frames only, with new columns pos_1d and arm_index.

Return type:

pd.DataFrame

placecell.maze_helper.assign_traversal_direction(trajectory: DataFrame, arm_order: list[str], zone_column: str = 'zone', arm_position_column: str = 'arm_position', direction_threshold: float = 0.5, arm_lengths: dict[str, float] | None = None) tuple[DataFrame, list[str]]#

Split arm traversals into forward/reverse directional segments.

Each contiguous run of frames within the same arm is a “traversal”. Direction is determined by the first arm_position value: < threshold → forward, >= threshold → reverse.

Parameters:
  • trajectory (DataFrame) – DataFrame from serialize_arm_position with columns zone_column, arm_position_column, arm_index, pos_1d, frame_index.

  • arm_order (list[str]) – Original arm order (e.g. [“Arm_1”, “Arm_2”, …]).

  • zone_column (str) – Column with zone labels.

  • arm_position_column (str) – Column with within-arm position (0-1).

  • direction_threshold (float) – Position threshold for direction classification.

  • arm_lengths (dict[str, float] | None) – Optional dict mapping zone name to physical length. When provided, pos_1d is recomputed in physical units.

Returns:

effective_arm_order list e.g. [“Arm_1_fwd”, “Arm_1_rev”, …])

Return type:

tuple of (DataFrame with direction columns and recomputed pos_1d,

placecell.maze_helper.filter_complete_traversals(trajectory: DataFrame, full_trajectory: DataFrame, arm_order: list[str], zone_column: str = 'zone') DataFrame#

Remove traversals where the animal did not cross from one room to another.

For each traversal, the zone immediately before entering and after leaving the arm is looked up in full_trajectory. A traversal is “complete” when both zones are rooms (not arms) and they differ (i.e. the animal went Room A → Arm → Room B, not Room A → Arm → Room A).

If traversal_id is not yet present (e.g. split_by_direction is False), traversal boundaries are detected automatically.

Parameters:
  • trajectory (DataFrame) – Arm-only DataFrame with frame_index column (and optionally traversal_id from assign_traversal_direction).

  • full_trajectory (DataFrame) – Complete trajectory including room frames, with frame_index and zone_column.

  • arm_order (list[str]) – List of arm zone names (anything else is treated as a room).

  • zone_column (str) – Column containing zone labels in full_trajectory.

Returns:

Filtered to only complete (room-to-room) traversals.

Return type:

pd.DataFrame

placecell.maze_helper.compute_speed_1d(trajectory: DataFrame, *, window_seconds: float, sample_rate_hz: float) DataFrame#

Compute 1D speed along the arm axis using a centered window.

Speed is computed as |delta(pos_1d)| / delta(time) over a symmetric window of actual frame indices, not entry offsets. Because room frames are absent from the arm-only trajectory, consecutive entries can have large frame_index gaps (room visits). Using entry offsets would compute distance/time over artificially long intervals, systematically underestimating speed in frequently-exited arms.

Parameters:
  • trajectory (DataFrame) – DataFrame with pos_1d, arm_index, unix_time, frame_index columns. Must be arm-only (output of serialize_arm_position).

  • window_seconds (float) – Centered window span (seconds).

  • sample_rate_hz (float) – Trajectory sampling rate, used to convert window_seconds to a frame count.

Return type:

pd.DataFrame with speed_1d column added.