#359Point-in-Time Feature JoinMedium

Point-in-Time Feature Join

Background

Training a model with future features silently leaks the future and inflates training accuracy. Point-in-time join matches each event row with the feature value that was available at the event time — not later. The canonical correctness primitive for any tabular ML pipeline with a time axis.

Problem statement

Implement pit_join(events, features, entity_key, time_key, feature_key):

  1. Group features by entity_key; sort each group by time_key ascending.
  2. For each event: walk the candidate features for that entity; pick the latest feature whose time_key is <= event[time_key]. If none qualifies, set the feature value to None.

Return a new list of event dicts with the matched feature_key attached.

Input

  • eventslist[dict]. Each has at least entity_key and time_key.
  • featureslist[dict]. Each has entity_key, time_key, and feature_key.
  • entity_key, time_key, feature_keystr.

Output

Returns list[dict], same length as events. Each output dict is a shallow copy of the input event with feature_key added.

Examples

Example 1 — latest feature at-or-before event time

events   = [{"u": 1, "t": 10}]
features = [{"u": 1, "t": 5, "v": "A"}, {"u": 1, "t": 8, "v": "B"}, {"u": 1, "t": 12, "v": "C"}]
Output: [{"u": 1, "t": 10, "v": "B"}]   (feature at t=12 would be future leak)

Example 2 — no earlier feature → None

events   = [{"u": 1, "t": 5}]
features = [{"u": 1, "t": 10, "v": "X"}]
Output: feature_key value is None

Example 3 — separate entities don't mix

Input:  features for u=1 and u=2 are independent
Output: each event sees only its own entity's history

Constraints

  • Event order is preserved in the output.
  • Inputs are not mutated.
  • Returns Python list of dict.

Notes

  • Why this is hard. Naive joins ignore time; SQL LATERAL JOIN does the right thing but is hard to optimise at scale. Feast / Tecton / Feathr feature stores all implement this primitive natively.
  • Watermarking. Production feature stores add an "availability lag" — features have a known publish delay, so the join's effective time is event_time - lag.
Python
Loading...

▶ Run executes the 3 visible sample tests below in your browser. Submit runs the full suite — including hidden tests — on the server for an official verdict.

  • Example: latest feature at-or-before event time
  • Reference: two entities exclude their own future features
  • Sample: no feature at-or-before yields None