Skip to content

Possessions

Possession reconstruction is a finite-state process over canonical events. It combines source possession labels with explicit basketball outcomes.

Source signal and overrides

source_possession_team_id is a strong signal, but it can switch before all companion actions for the previous possession have been emitted. The reconstruction therefore prioritizes semantic terminal events when the two disagree.

Expected source overrides are counted on each possession for auditability.

Terminal reasons

Possessions can end with:

Reason Interpretation
made_field_goal Made two- or three-point field goal
made_final_free_throw Made final non-technical free throw
turnover Explicit turnover action
defensive_rebound Defense controls a missed shot
held_ball Opponent recovers a held-ball jump
period_end Active possession reaches the period boundary
source_change Unexplained source transition retained for diagnosis
feed_end Feed ends during an active period

Made scores and held-ball recoveries remain pending briefly so companion actions can confirm or supersede the boundary.

Important feed sequences

Offensive-foul companions

An offensive foul may carry the next team's source possession label before the turnover companion action. Both events stay with the original offense.

Held ball followed by turnover and steal

Some feeds describe one change of control as a held-ball recovery followed by a turnover and steal at the same clock. The turnover supersedes the pending held-ball terminal, preventing a phantom possession.

Technical free throws

A technical free throw can score for the defensive team under the CDN's active possession label. The point is preserved and marked as opponent_technical_free_throw rather than treated as an accounting failure.

Post-score loose-ball free throws

Newer feeds can label the prospective next offense immediately after a made basket, then award a same-clock loose-ball penalty free throw to the scoring team. The foul and free throw remain continuation context for the scoring possession.

Legacy labels

Older live-data feeds use free-throw labels such as 2of2 and may omit isFieldGoal. Canonical event type and shot result provide the stable terminal signals across these versions.

Possession contract

possessions/{game_id}.parquet contains one row per basketball possession, including:

  • offense and defense team IDs;
  • period-local and game-global indexes;
  • start and end events, clocks, and elapsed times;
  • points by team and offense points;
  • start and terminal reasons;
  • event count, source mismatch count, and validation flags;
  • number of fixed-lineup segments.

Fixed-lineup segments

possession_segments/{game_id}.parquet intersects each possession with atomic substitution boundaries.

A segment stores one home lineup, one away lineup, points, duration, and its position within the parent possession. Segment scores and durations must sum exactly to the possession totals.

Accounting invariant

For every possession, segment points and elapsed duration must sum exactly to the parent row. A mismatch is an audit failure, not a warning.

Allocation-policy incidence

RAPM requires a rule for possessions that cross a substitution boundary. The canonical equal_segments policy divides one possession equally across all of its fixed-lineup segments. Sensitivity tests also assign the possession to its starting lineup, terminal lineup, first and last lineups, or exclude it.

Two related rates quantify how much those choices affect the modeling data:

  • Changed possessions have a policy-specific exposure vector over distinct ten-player lineups that differs from equal_segments.
  • Reassigned or removed possession-equivalents measure how much lineup exposure moves. This is total-variation distance with an additional removed bucket, so excluding one possession counts as one full possession rather than one half.

For example, a two-segment possession has canonical shares (0.5, 0.5). Assigning it to the starting lineup changes one possession but reassigns only 0.5 possession-equivalents. boundary_split leaves it unchanged.

2025-26 regular season

The curated regular season contains 245,772 possessions. Of those, 26,962 (10.970%) cross at least one distinct-lineup boundary and can therefore depend on allocation policy. There are 25,803 two-segment possessions and 1,159 possessions with three or more segments.

Policy Changed possessions Percent of all possessions Reassigned or removed equivalents Percent of all exposure
equal_segments 0 0.000% 0.00 0.000%
starting_lineup 26,962 10.970% 13,665.98 5.560%
terminal_lineup 26,962 10.970% 13,666.52 5.561%
boundary_split 1,158 0.471% 392.67 0.160%
exclude_multi_lineup 26,962 10.970% 26,962.00 10.970%

boundary_split and equal_segments agree for ordinary two-segment possessions: each assigns one half to the first and last lineup. They diverge only for possessions with additional segments or repeated lineups. Starting and terminal assignment affect every cross-lineup possession, but they reassign approximately half as much total exposure as outright exclusion.

Reproduce the summary from the curated possession-segment partition:

import pandas as pd

from nba_lineup_model.modeling.allocation import possession_allocation_summary

segments = pd.read_parquet(
    "data/curated/possession_segments/2025-26/regular"
)
summary = possession_allocation_summary(segments)

Future RAPM diagnostic runs persist the same table as allocation_summary.parquet.