NBA Relationship Scouting in Foundry: Modeling Player Combinations as First-Class Relationships

NBA Relationship Scouting: League-Wide Relationship Discovery and Film Review in Foundry

NBA scouts and basketball operations analysts often have to jump between separate lineup-stat tables to understand how players perform together across different contexts. I built a Foundry-native basketball operations application that treats observed player combinations as first-class relationships rather than isolated rows in lineup-stat tables.

For any selected NBA player, the application lets an analyst:

  • Explore their observed 2-, 3-, 4-, and 5-player relationships

  • Traverse a relationship into the complete five-man lineups that contain it

  • Search the league for statistically parallel relationship profiles

  • Use grounded AIP to identify what deserves further investigation

  • Turn that investigation into a tracked Film Review Request

Stack: Pipeline Builder → Ontology → OSDK React → deterministic analytics → AIP Logic → Actions
Primary grain: one observed player group for one team and season
Relationship universe: 27,903 observed 2-, 3-, 4-, and 5-player groups
Current coverage: 2025–26 season

The application is deliberately scoped as a relationship scouting and discovery system. It does not estimate chemistry, simulate unseen lineups, recommend trades, or claim that statistically parallel groups contain interchangeable players.


Why this problem

NBA lineup statistics are readily available, but they are commonly consumed as separate tables.

A player can appear across dozens of pairs, trios, four-player units, and complete five-player lineups. The statistics describe each combination, but moving between those levels of context quickly becomes cumbersome.

The question I wanted to support was broader:

How does a player perform across different teammate relationships, what larger lineups contain those relationships, and where else in the league do similar relationship profiles appear?

The useful layer is therefore not another NET Rating table. It is the connection between:

Player
  ↓
Observed relationship
  ↓
Larger lineup context
  ↓
League-wide parallel relationship
  ↓
Film investigation

That led to the central modeling decision:

Make the observed player combination itself an Ontology object.


Data

The build uses NBA shared-court advanced statistics for observed player groups.

The extracted relationship universe contains:

2-player groups:  5,403
3-player groups:  7,500
4-player groups:  7,500
5-player groups:  7,500
────────────────────────
Total:           27,903

Each group contains season-level shared-court evidence including:

  • GP / MIN / POSS

  • OFF Rating

  • DEF Rating

  • NET Rating

  • AST%

  • REB%

  • eFG%

  • TS%

  • Pace

The source group identifier also contains the constituent NBA player IDs. That allowed player membership to be derived directly instead of inferred through name matching.

The primary serving dataset is:

observed_player_groups_base

One row represents one observed player combination for one team and season.


Architecture

The build has three connected paths.

The first creates the canonical relationship layer:

2 / 3 / 4 / 5-player group data
        ↓
Pipeline Builder
        ↓
observed_player_groups_base
        ↓
ObservedPlayerGroup
        ↓
OSDK application

The second derives explicit membership:

observed_player_groups_base
        ↓
parse constituent player IDs
        ↓
group_membership
        ↓
Player ↔ ObservedPlayerGroup

The third derives larger lineup context:

2 / 3 / 4-player membership
        +
5-player membership
        ↓
team + season + constituent-player matching
        ↓
group_supporting_lineups
        ↓
smaller group ↔ supporting five-man group

ObservedPlayerGroup stores the shared-court profile. group_membership resolves who belongs to that relationship. group_supporting_lineups resolves which complete observed five-man lineups contain the smaller group.

The OSDK application consumes these Ontology relationships directly rather than reconstructing the graph in frontend code.


Ontology model

The active Ontology contains four object types:

Player
Team
ObservedPlayerGroup
FilmReviewRequest

The central analytical object is ObservedPlayerGroup.

One object represents:

one observed combination of players, for one team, during one season

For example:

Giannis Antetokounmpo
Ryan Rollins
AJ Green

Milwaukee
2025-26

The important links are:

Player ↔ ObservedPlayerGroup

Team ↔ ObservedPlayerGroup

ObservedPlayerGroup
        ↕
Supporting 5-Man ObservedPlayerGroup

FilmReviewRequest
        ↔
ObservedPlayerGroup

The grain is the important part.

A Player object tells me who an athlete is. A Team provides organizational context. Neither represents the shared-court behavior of several players together.

The relationship itself is the analytical record.


Relationship Explorer

The application begins with a league-wide Player search.

Selecting a player resolves that player’s linked ObservedPlayerGroup objects and separates them into:

2 Player
3 Player
4 Player
5 Player

Each relationship surfaces the core evidence:

Relationship
Team
GP
MIN
POSS
NET
OFF
DEF

Selecting one opens a dedicated Relationship Detail page.

A Player becomes the entry point into every observed relationship containing that player.


Relationship detail and supporting context

The Relationship Detail page expands one observed group into its statistical and Ontology context.

The analyst sees:

  • constituent players

  • team and season

  • performance metrics

  • GP / MIN / POSS evidence volume

  • supporting five-man lineups

For a smaller relationship, the supporting-lineup link answers:

What complete observed lineups contained these players together?

For example:

Westbrook + DeRozan + Achiuwa
              ↓
Westbrook + DeRozan + Achiuwa + Murray + Raynaud

Westbrook + DeRozan + Achiuwa + LaVine + Raynaud

Each supporting lineup is itself another ObservedPlayerGroup, so the analyst can open it and continue investigating.

3-player relationship
        ↓
supporting 5-man relationship
        ↓
full relationship profile

This turns what would normally be a static lineup table into a navigable relationship graph.

Smaller player relationships can be expanded into the complete observed five-man contexts that contain them.


Parallel Relationships

The primary analytical extension is Parallel Relationship discovery.

Once an analyst identifies an interesting relationship, the application searches the same-size league-wide relationship universe for statistically nearby profiles.

A two-player relationship is compared only against other two-player relationships. A three-player relationship is compared only against other three-player relationships, and so on.

The comparison uses:

NET Rating
OFF Rating
DEF Rating
AST%
REB%
eFG%
TS%
Pace

Each dimension is standardized across the same-size universe before calculating distance.

Conceptually:

D(g,h) = sqrt((1/k) * Σ(z_g,i - z_h,i)^2)

This keeps metrics with different numerical scales from dominating the comparison.

The application then shows the reference and selected parallel relationship side-by-side, including:

  • raw metric values

  • standardized gap by dimension

  • overall distance

GP, MIN, and POSS are intentionally not similarity dimensions.

They are displayed separately as evidence-volume context.

Two relationships can therefore have similar observed profiles without being treated as equally well-supported samples.

The methodology is intentionally transparent. The analyst can see exactly why one relationship was retrieved as a parallel to another.

Parallel Relationships retrieves same-size observed groups with nearby multivariate shared-court profiles while keeping evidence volume separate from similarity.


Why “parallel relationships”?

The comparison is deliberately between relationship profiles, not between individual players.

A nearby result supports the statement:

These observed shared-court relationships have similar profiles across the selected dimensions.

It does not establish that:

  • the constituent players are interchangeable

  • the players perform equivalent roles

  • the groups have the same chemistry

  • the same basketball mechanisms created the results

Parallel retrieval is therefore a discovery mechanism. It gives the analyst another relationship worth investigating.


AIP Scouting Copilot

The AIP design went through an important iteration.

The first version was correctly grounded but mostly repeated values already visible in the application.

If the interface already shows:

NET 9.2
OFF 120.7
POSS 1,266

an LLM repeating those numbers adds very little.

The more useful question became:

Given what the deterministic system already found, what deserves the analyst’s attention next?

That changed the division of labor.

Before the Copilot runs, the application calculates additional discovery signals deterministically.

For the selected relationship, it derives same-size standing across:

  • NET Rating

  • OFF Rating

  • DEF Rating

  • AST%

  • REB%

  • eFG%

  • TS%

  • Pace

This includes percentile and standardized standing.

For supporting five-man contexts, the application derives:

  • possession-weighted context statistics

  • contextual dispersion

  • highest-volume context

  • volume-aware high and low contexts

  • largest meaningful contextual differences

For a selected parallel relationship it also provides:

  • selected rank and distance

  • closest comparison dimensions

  • largest standardized gaps

  • evidence-volume comparison

The LLM does not calculate or override these values.

It receives only the current investigation context:

selected relationship
constituent players
team / season
raw metrics
GP / MIN / POSS
loaded supporting five-man contexts
derived discovery signals
selected parallel, when applicable
deterministic comparison output

The full league-wide relationship universe is never sent to AIP.

The Copilot then answers a much narrower set of questions.

What Stands Out

Which grounded patterns deserve attention?

What to Investigate Next

Which contexts or differences warrant deeper review?

Film Review Question

What specific qualitative question could film help answer?

Evidence Limitations

What caveats should temper the investigation?

The deterministic layer finds the pattern.

AIP prioritizes the investigation.

The Copilot consumes deterministic discovery signals and turns quantitative patterns into concrete investigation priorities rather than recomputing the underlying analytics.


From discovery to Action

Aggregate statistics can reveal an interesting relationship.

They cannot establish the basketball mechanism producing it.

That creates a natural handoff:

statistical discovery
        ↓
parallel comparison
        ↓
AIP investigation guidance
        ↓
film-review question
        ↓
FilmReviewRequest

AIP can draft the Film Review Question, but it does not execute the Action.

The analyst still controls:

Priority
Film Scope
Review Question
Notes

and explicitly submits the existing Create Film Review Request Action.

FilmReviewRequest is linked directly to the ObservedPlayerGroup being investigated.

New requests begin with:

status = Requested

and can progress through:

Requested
    ↓
Preparing
    ↓
Ready
    ↓
Reviewed

using the Edit Film Review Request Action.

This closes the workflow:

discover
   ↓
compare
   ↓
identify what deserves investigation
   ↓
request film
   ↓
track operational state

Statistical discovery becomes a persistent operational workflow through FilmReviewRequest objects linked back to the relationship under investigation.


Validation and iteration

A few issues only became obvious once the application was used against live Ontology data.

Search relevance

The original Player search used fuzzy matching but alphabetically ordered the returned candidates.

That meant obvious searches could rank below weaker fuzzy matches.

The final ranking is:

exact full name
→ exact first / last name
→ prefix
→ substring
→ fuzzy fallback

Relationship-size filtering

The original 2 / 3 / 4 / 5-player interface relied primarily on asynchronous query filtering.

During transitions, cached results from the previous group size could briefly remain visible.

The final application enforces:

displayed group_quantity
=
selected group_quantity

at the render boundary and clears incompatible selections.

AIP usefulness

The first grounded Copilot was descriptive rather than investigative.

Testing showed that the LLM needed derived discovery signals, not simply more raw statistics.

The final design moved relationship standing, context variation, evidence-volume reasoning, and parallel-profile gaps into deterministic analytics while keeping AIP focused on investigation guidance.

That was the iteration that made the AIP layer useful rather than decorative.


Limitations

Season-aggregate grain

ObservedPlayerGroup currently represents season-level shared-court aggregates.

The system knows which relationship deserves investigation, but it does not yet resolve that relationship into the exact:

games
stints
possessions
timestamps
video clips

that produced the aggregate profile.

A Film Review Request therefore identifies what relationship should be investigated and the desired film scope, rather than automatically constructing the underlying clip package.

Single-season coverage

The current relationship universe covers only the 2025–26 season.

Every relationship profile, supporting context, and parallel comparison therefore reflects one season.

The application cannot yet test whether a relationship pattern persists across:

  • multiple seasons

  • different teammates

  • different teams

  • different coaching environments

  • changing roster structures

A relationship that looks distinctive in one season may be highly dependent on its surrounding context.

Multi-season coverage would make it possible to investigate:

Does this relationship pattern persist when the basketball environment changes?

Supporting-lineup coverage

Supporting five-man contexts represent the observed five-player groups available in the current extraction.

They should therefore be interpreted as available observed contexts containing the smaller relationship, not as a guarantee that every underlying lineup configuration is represented.

Extraction bounds

The 3-, 4-, and 5-player pulls each returned 7,500 rows, corresponding to 250 groups per team in this extraction.

The application therefore operates over the extracted relationship universe, rather than claiming complete coverage of every possible observed group.

Descriptive similarity

Parallel Relationships does not:

  • establish causal chemistry

  • predict future performance

  • project unseen lineups

  • adjust for opponent strength

  • prove player interchangeability

  • identify the underlying basketball mechanism

It is a transparent retrieval method for finding relationships worth comparing.

Client-side parallel serving

The same-size relationship universe is currently loaded into the browser before parallel retrieval.

That is workable at the current scale, but a production implementation would move retrieval to a server-side Function, transform, or precomputed nearest-neighbor layer.


Next iteration

The highest-value extension is to move one grain deeper.

A production version could introduce:

ObservedPlayerGroup
        ↓
GroupGameAppearance
        ↓
Game
        ↓
Shared stint / possession
        ↓
Timestamp
        ↓
Video clip

That would turn:

Review the best-performing games for this relationship

into a directly executable workflow:

relationship
→ relevant games
→ shared possessions
→ time-coded film

Multi-season coverage would add another important analytical dimension:

Does this relationship pattern persist when teammates, teams, and surrounding context change?

That would make it possible to study relationship persistence and portability rather than only single-season similarity.

A production version would also move parallel retrieval out of the browser while preserving the same transparent comparison methodology.


Final result

The core design idea is simple:

Make the observed player relationship the unit of investigation.

Once that relationship exists as a first-class Ontology object, the analyst can move naturally from:

player
→ relationship
→ larger lineup context
→ league-wide parallel
→ AIP-guided investigation
→ film review

The project deliberately favors inspectable relationship structure, transparent analytics, grounded AIP, and human-in-the-loop investigation over hypothetical fit scores or unsupported chemistry scores or hypothetical fit predictions.

Happy to go deeper on the Pipeline Builder relationship logic, Ontology model, OSDK traversal, parallel-profile methodology, AIP grounding, or Action workflow.