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):
- Group
featuresbyentity_key; sort each group bytime_keyascending. - For each event: walk the candidate features for that entity; pick the latest feature whose
time_keyis<= event[time_key]. If none qualifies, set the feature value toNone.
Return a new list of event dicts with the matched feature_key attached.
Input
events—list[dict]. Each has at leastentity_keyandtime_key.features—list[dict]. Each hasentity_key,time_key, andfeature_key.entity_key,time_key,feature_key—str.
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
listofdict.
Notes
- Why this is hard. Naive joins ignore time; SQL
LATERAL JOINdoes 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.
▶ 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