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

**URL:** <https://community.palantir.com/t/nba-relationship-scouting-in-foundry-modeling-player-combinations-as-first-class-relationships/7189>\
**Category:** Show & Tell\
**Created:** [September 7, 2026, 8:57am UTC](https://community.palantir.com/t/nba-relationship-scouting-in-foundry-modeling-player-combinations-as-first-class-relationships/7189 "2026-09-07T08:57:57Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![subbu1](https://yyz1.discourse-cdn.com/palantir/user_avatar/community.palantir.com/subbu1/32/7821_2.png) [@subbu1](https://community.palantir.com/u/subbu1)\
**Post date:** [September 7, 2026, 8:57am UTC](https://community.palantir.com/t/nba-relationship-scouting-in-foundry-modeling-player-combinations-as-first-class-relationships/7189/1 "2026-09-07T08:57:58Z")

</div>

# 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:

```auto
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:

```auto
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:

```auto
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:

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

```

The second derives explicit membership:

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

```

The third derives larger lineup context:

```auto
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:

```auto
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:

```auto
Giannis Antetokounmpo
Ryan Rollins
AJ Green

Milwaukee
2025-26

```

The important links are:

```auto
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:

```auto
2 Player
3 Player
4 Player
5 Player

```

Each relationship surfaces the core evidence:

```auto
Relationship
Team
GP
MIN
POSS
NET
OFF
DEF

```

Selecting one opens a dedicated Relationship Detail page.

 ![d81cd418-b28e-4f02-9241-ea3a1c896b47](https://canada1.discourse-cdn.com/palantir/original/2X/8/86f863d31b164b197e911fefb3676e3182b9bf06.png)

_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:

```auto
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.

```auto
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.

 ![30092854-f1bb-4d09-a8df-6d7361fe23d2](https://canada1.discourse-cdn.com/palantir/original/2X/0/0f3cb398f85ea1f618a0ca8bc8469aeceb4af0b1.png)

_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:

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

```

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

Conceptually:

```auto
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.

 ![9d7c9b47-7ac0-4575-a215-8da0ae1baae3](https://canada1.discourse-cdn.com/palantir/original/2X/5/58b725e9bc3f7809ff5296d2060988e2d9ecfc80.png)

_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:

```auto
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:

```auto
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.**

 ![5f4eab0f-da93-4f4d-aed6-a36f0785a14f](https://canada1.discourse-cdn.com/palantir/original/2X/b/bf35b07512d916b43dfa4c52ff9a92a464d75861.png)

_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:

```auto
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:

```auto
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:

```auto
status = Requested

```

and can progress through:

```auto
Requested
    ↓
Preparing
    ↓
Ready
    ↓
Reviewed

```

using the Edit Film Review Request Action.

This closes the workflow:

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

```

 ![](https://canada1.discourse-cdn.com/palantir/original/2X/b/be2445d880c88308545ff5b862bcd835bd774def.png)

 ![image](https://canada1.discourse-cdn.com/palantir/original/2X/4/4521af54a5969b33fa6ae51f5bcf6aca4a83ae95.png)

_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:

```auto
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:

```auto
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:

```auto
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:

```auto
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:

```auto
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:

```auto
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.
