ML data engineering
Point-in-Time Correctness and Historical Retrieval
Design as-of joins, feature cutoffs, temporal tests, and backfill safeguards for historical examples.
By the end you can
- Distinguish event time, availability time, and historical state
- Build as-of joins that respect feature and relationship cutoffs
- Detect future leakage in ordinary business fields and backfills
- Write temporal boundary tests for historical retrieval
Visual
One fact can carry several times
One fact can carry five clocks, and temporal correctness means picking the one the learning protocol needs. Event time is when the underlying action or measurement occurred in the domain. Valid time is the interval during which a state or attribute is considered true in the domain. Availability time is when the feature value became accessible to the prediction system. System time is when the data platform stored or revised the record. Prediction time is the cutoff that separates allowed evidence from future information.
Two of those clocks are not a local convention. They are in a published ISO/IEC standard. SQL:2011 carries valid time as application-time period tables and transaction time as system-versioned tables. Kulkarni and Michels, the standard's own editors, state the separation flatly: “For any given row, its transaction time may arbitrarily differ from its valid time.” The standard allows at most one application-time period and one system-time period per table. It uses closed-open period semantics. And it adds periods as table metadata over a pair of ordinary DATE or timestamp columns, rather than as a new period data type.
That design is implemented, not merely specified. MariaDB Server documents that “System-versioned tables store the history of all changes, not only data which is currently applicable”, and that “System-versioned tables were first introduced in the SQL:2011 standard”. You query them through the FOR SYSTEM_TIME AS OF form. When a lesson says a fact has two dates, it is describing a table a reader can go and create.
- 01
Event time
When the underlying action or measurement occurred in the domain.
- 02
Valid time
The interval during which a state or attribute is considered true in the domain.
- 03
Availability time
When the feature value became accessible to the prediction system.
- 04
System time
When the data platform stored or revised the record.
- 05
Prediction time
The cutoff that separates allowed evidence from future information.
Point-in-time retrieval uses the prediction cutoff together with the feature’s availability and validity semantics.
Comparison
Current-state joins and as-of joins answer different questions
A normal equality join can be structurally correct and still temporally impossible. Three joins over the same two tables answer three different questions.
A current-state join attaches the latest known dimension row to every historical event. It is simple, and useful for present-state reporting. It also rewrites past context after later changes, and it can leak corrections and future classifications: an old transaction receives today's merchant risk tier. A valid-time as-of join attaches the dimension version valid when the event occurred. It preserves changing domain state and requires non-overlapping validity intervals. It may still use values published after the prediction — contract terms active on the purchase date were true then, but the record of them may not have existed yet. An availability-aware join attaches the latest value available before the prediction cutoff. That matches the model's historical information set, handles delayed computation and publication, and needs feature-generation timestamps or snapshots. It is the only one of the three that can establish that a risk score was completed before authorization.
The third is not a hand-rolled pattern. It is a named relational primitive with defined semantics in independent engines. DuckDB spells it ASOF JOIN. Each left row is matched against the nearest preceding right row through an inequality on the ordering column, and the name comes from the question it answers: “Give me the value of the property as of this time”. Richard Wesley, writing on the DuckDB blog in 2023, gives the property that matters when the left table is a training spine: “Because AsOf produces at most one match from the right hand side, the left side table will not grow as a result of the join.” pandas.merge_asof implements the same rule. Its API reference says “This is similar to a left-join except that we match on nearest key rather than equal keys”, and the default direction, 'backward', “selects the last row in the right DataFrame whose 'on' key is less than or equal to the left's key”. One row in, at most one row out, taken from before the cutoff. That is the whole contract.
Current-state join
Attach the latest known dimension row to every historical event.
- Simple and useful for present-state reporting
- Rewrites past context after later changes
- Can leak corrections and future classifications
- Example: old transaction receives today’s merchant risk tier
Valid-time as-of join
Attach the dimension version valid when the event occurred.
- Preserves changing domain state
- Requires non-overlapping validity intervals
- Still may use values published after prediction
- Example: contract terms active on purchase date
Availability-aware join
Attach the latest value available before the prediction cutoff.
- Matches the model’s historical information set
- Handles delayed computation and publication
- Needs feature-generation timestamps or snapshots
- Example: risk score completed before authorization
Key idea
Backfills can create features that never existed online
A new transformation can be run over years of raw history, producing a complete feature for every old example. That does not prove the feature could have been computed with the latency and source availability of the original system. Historical raw data may also include corrections or retention that live serving lacked, so training on the backfill can overstate both coverage and quality. Distinguish event-derived reproducibility from online availability. When necessary, simulate production delay, missingness, feature computation schedules, and source outages in the historical build.
A historically computable feature is not automatically a historically available feature.
Steps
Build a point-in-time feature join
The process should run for millions of historical prediction times without changing semantics.
Start with the prediction timestamps. The example spine is the authoritative cutoff for every row, and nothing downstream is allowed to widen it. Record the feature timestamps — event, computation, availability, and revision time, as needed. One column cannot answer both when a value was true and when it could be read. Select the eligible history by filtering to values available before the cutoff and valid for the intended entity state. Aggregate within bounded windows, computing counts, recency, and statistics that never cross the cutoff. Then test against synthetic timelines: construct examples where a future value is numerically tempting, and verify that it is excluded.
1. Start with prediction timestamps
Use the example spine as the authoritative cutoff for every row.
2. Record feature timestamps
Preserve event, computation, availability, and revision time as needed.
3. Select the eligible history
Filter to values available before the cutoff and valid for the intended entity state.
4. Aggregate within bounded windows
Compute counts, recency, and statistics without crossing the cutoff.
5. Test against synthetic timelines
Create examples where future values are tempting and verify they are excluded.
Point-in-time logic should be tested with adversarial timelines, not trusted because the SQL contains a timestamp filter.
Analogy
Pausing the tape at the moment of the decision
Evaluating a coach's decision means pausing the recording at that moment and using only the score, player state, and information visible then. The final result and later injury reports may explain the outcome, but they were not available to the coach. Adding them to the decision record makes the coach appear unrealistically informed.
The video is still there to pause. Data warehouses overwrite old state, and without revision history the system may no longer possess the exact information that was visible at the original moment.
The requirement being violated has a name and a published statement of it. “The prevailing example for this type of leakage is what we call the no-time-machine requirement”, write Kaufman and colleagues in their 2011 paper on leakage in data mining. Their remedy is procedural rather than statistical: time-stamp every observation as a legitimacy tag, then cut on a “learn-predict separation”. They also name the obstacle: “Interestingly enough, this common case does not sit well with the equally common way databases are organized.” The tape is not missing because anyone decided to discard it. It is missing because a table that keeps only the current row is the normal way to build one.
That is precisely the gap system-versioned temporal tables close. Microsoft Learn documents that temporal tables “provide built-in support for information about data stored in the table at any point in time, rather than only the current data”, and that FOR SYSTEM_TIME AS OF returns “the values that were current at the specified point in time in the past”. Pausing the tape is a schema decision made before the first training run. It is not a query written afterwards.
Offline training should replay the information state, not merely attach facts that are now known about the past.
Temporal tests should target the boundary directly
For each feature, assert that its maximum availability timestamp does not exceed the example cutoff. Test validity intervals for overlap and gaps.
Create rows with future values that are numerically extreme. If those values change the feature, the join crossed the boundary.
Compare offline feature retrieval with logged online values for the same historical requests. Differences reveal transformation skew, clock mismatch, late data, or missing source state.
Finally, review features with unusually high predictive power. Leakage often produces dramatic gains, especially when the field is something the target itself later causes.
The KDD Cup 2008 mammography data shows how loud that signal can be. Patient IDs fell into bins with very different prevalence: 36% malignant below 20,000, and 1.7% above 4,000,000. The authors then recovered two of those bins from the 117 supplied features alone, with AUCs of 0.86 and 0.75. So the identifier was not neutral. Neither were the pixels.
Temporal correctness deserves explicit invariants, adversarial fixtures, and offline–online comparison.
Example
Future leakage hides in ordinary business fields
The dangerous feature may look plausible because it genuinely correlates with the outcome. When the correlation is impossible, the scoreboard usually says so before anyone reads the schema.
The INFORMS 2010 Data Mining Contest, run on Kaggle, asked entrants to predict whether a stock's price would rise or fall over the next 60 minutes. 894 participants from 27 countries submitted 147 solutions. About 30 groups exceeded 0.9 AUC, and the top entry scored 0.99. An hour of stock direction is not a 0.99 AUC problem. Two things had gone wrong: explanatory variables carried values from after the prediction horizon, and the target stock could be identified from public price data. The organisers ended up publishing a second, separate “Not using future information ranking”, whose winners were entirely different people. Even with the rule stated, Kaufman and colleagues record, “it was still possible to build models that rely on data from the future”, and the organisers “had to admit that verifying future information was not used was impossible”. A rule the dataset cannot enforce is not a rule.
Nothing in that story needs a competition. The same shapes sit in ordinary business tables:
- Chargeback status: a transaction receives the final dispute result that arrived sixty days after authorization.
- Customer lifetime value: historical examples use a value recalculated from purchases that occurred after the prediction date.
- Diagnosis code: an admission-risk model uses the final discharge diagnosis entered at the end of the stay.
- Inventory state: a demand model uses a corrected stock level posted after late warehouse reconciliation.
- Support escalation: a routing model uses the final case priority instead of the priority visible when the first response was assigned.
Position
Leakage is a defect in the dataset, not a mistake the model made
Blame lands on the model, because the model is what produced the suspicious number. The KDD Cup 2008 mammography data shows how misplaced that can be. Patient identifiers fell into bins with very different prevalence: 36% malignant among the 254 patients below 20,000, and 1.7% among the 1,044 above 4,000,000. A classifier that leaned on the identifier was doing exactly what it had been asked to do. The “Patient ID” feature had “tremendous and unexpected predictive power”, report Kaufman and colleagues, who attribute it to “assigning consecutive patient IDs for data from each source”. The defect sat upstream, in how the dataset had been assembled.
Dropping the offending column is the obvious repair, and this case is a warning against trusting it. The winners' own report found that the two extreme groups “are easily identified by a logistic model from the 117 provided features with AUCs of 0.86 and 0.75 respectively”. The grouping survived the identifier. That is the practical difference between a modelling mistake and an engineering defect: the first is repaired by changing the model, and the second is not.
Nor is this one unlucky competition. Kapoor and Narayanan, writing in Patterns in 2023, found “17 fields where leakage has been found, collectively affecting 294 papers”, and introduced “a detailed taxonomy of eight types of leakage”. Their own reproduction of civil war prediction found that “complex ML models do not perform substantively better than decades-old LR models” once the errors were corrected.
Regulators put the obligation in the same place. In October 2021 the U.S. FDA, Health Canada and the UK MHRA jointly published 10 Guiding Principles for Good Machine Learning Practice for medical device development. Principle 4 is “Training Data Sets Are Independent of Test Sets”, and its text is written about how data are handled, not about how a score is computed: “Training and test datasets are selected and maintained to be appropriately independent of one another. All potential sources of dependence, including patient, data acquisition, and site factors, are considered and addressed to assure independence.” Principle 8 requires that clinically relevant performance evidence be generated “independently of the training data set”. Selected and maintained are verbs about a pipeline. Ask where a feature came from before asking why it works so well.
Leakage is repaired where the dataset is assembled, not where the score is computed.
Historical truth is not the same as historical availability
A warehouse may know today that a transaction was fraudulent. The model scoring that transaction two weeks earlier did not know that outcome. Using the corrected record as a feature creates future leakage. Point-in-time correctness requires every feature value to be one the system could legitimately retrieve at the prediction timestamp. The event having happened is not enough if the value was published later. Mutable dimensions create a similar problem. Joining an old order to the customer's current segment, or to a machine's current configuration, rewrites the context of the past.
Macroeconomic data show how wide that gap can be, because the publisher measures it. A quarterly GDP figure is not one series but a sequence of vintages. “The advance estimates for a quarter are released about 1 month after the quarter ends,” write Fixler and colleagues in the Bureau of Economic Analysis's own revision study in 2024. That first published number then moves. For 1999–2022 the mean absolute revision from each of the three current quarterly vintages to the latest estimate ran 1.18 to 1.29 percentage points, for real and nominal GDP alike. From the advance to the second estimate of nominal GDP alone it was 0.47 percentage point, and from the second to the third 0.34 percentage point. A model trained on today's stored series is trained on a number nobody could have read at the time, off by roughly a percentage point. The Federal Reserve Bank of Philadelphia built an archive for exactly this reason. Croushore and Stark described it plainly in 1999: “The data set consists of vintages, or snapshots, of the major macroeconomic data available at quarterly intervals in real time”.
The fix is temporal data modeling. Preserve timestamps, validity intervals, revisions, and snapshots, so historical examples can reconstruct the information state that existed then.
For every feature, ask both “when was it true?” and “when could the model know it?”
Key takeaways
- Point-in-time correctness means training rows contain only evidence available before each prediction cutoff — what Kaufman and colleagues call the no-time-machine requirement.
- Event time, valid time, availability time, system time, and prediction time answer different temporal questions, and SQL:2011 standardises two of them as application-time period tables and system-versioned tables.
- Current-state joins can rewrite historical context and leak later classifications, corrections, or outcomes.
- A feature that can be backfilled from raw history may still have been unavailable under original online latency and coverage — a GDP vintage revised by around a percentage point is the published example.
- Availability-aware as-of joins are an implemented primitive — ASOF JOIN in DuckDB, merge_asof in pandas — and still need explicit timestamps, bounded windows, and preserved revision history.
- Adversarial timelines, cutoff invariants, and offline–online feature comparison are essential temporal tests, because an unverifiable rule is what forced INFORMS 2010 into a second leaderboard.