Data Preparation, Splitting and Leakage

Cleaning, scaling, class imbalance, training, validation and test splits, time-ordered data, cross-validation and how leakage fools you.

What good data looks like

A model learns whatever is in its data, including the mistakes. The data should be representative (cover the loads, speeds, temperatures, product types and faults the model will meet), correctly labeled, consistent (same units and meaning throughout) and big enough for the model you choose. A model trained on summer data may fail in winter; one trained on a single machine may not transfer to its twin. Collecting and timing the data properly is the subject of Data Logging, and its page on data quality applies directly here.

QualityQuestionTypical problem
CoverageDoes it include every condition of use?Only normal running, no faults and no start-ups
Label qualityAre the answers right and consistent?Two inspectors disagree; labels written weeks later
BalanceAre the categories represented?1 % faults in the data
IndependenceAre the samples different enough?Consecutive samples nearly identical, so 10 000 rows are really 20
Timing and unitsAre channels aligned and in the same units?Clocks out by seconds, a mix of psi and bar
ProvenanceDo you know where each row came from?Data merged from several machines with no machine ID

Cleaning

  • Plot everything first. Most data problems (a stuck sensor, a unit change, a flat line) are obvious in a picture and invisible in a table.
  • Fix units and scaling and keep a record of every conversion.
  • Handle gaps deliberately. Options: drop the rows, fill from the last value, interpolate (short gaps only) or add a “missing” flag as an input. Dropping 4 % of 10 000 rows loses 400 rows, which matters little if they are scattered but a great deal if they are all one fault.
  • Treat sensor faults as faults, not data: a 4–20 mA reading below 3.6 mA is a broken wire, not a low pressure (see the NAMUR note in Sensors, Signal Conditioning and ADCs).
  • Be careful with outliers. Removing the rare extreme values may remove the very events you want to detect; flag them and decide by cause.
  • Remove duplicates and records written twice by a retry, or they will appear on both sides of a split.
  • Align time stamps across channels before building features (see Time Stamps and Clocks).
import pandas as pd
df = pd.read_csv("run.csv", parse_dates=["t"]).set_index("t").sort_index()
df = df[~df.index.duplicated(keep="first")]                  # duplicates
df = df[df["current_ma"] >= 3.6]                              # broken-wire readings are not data
df = df.interpolate(limit=3)                                  # fill gaps of up to 3 samples only
df["missing"] = df["temp_c"].isna().astype(int)               # keep a flag for what is left

Scaling features

Many methods (nearest neighbors, support vector machines, neural networks, regularized regression) treat all inputs alike, so a feature measured in thousands drowns one measured in tenths. Scale them to comparable ranges. Tree-based methods do not need it.

z = (x − μ) ÷ σ scaled = (x − min) ÷ (max − min)

Worked example: scaling five temperatures

  • Training temperatures: 62, 65, 71, 68, 74 °C. Mean μ = 68.0, sample standard deviation σ = 4.74.
  • Z-scores: -1.26, -0.63, 0.63, 0.00, 1.26. They average to zero.
  • A new reading of 80 °C is scaled with the training numbers: z = (80 − 68.0) ÷ 4.74 = 2.53; min-max gives (80 − 62) ÷ 12 = 1.50, outside the 0 to 1 range, which is a useful warning that the input is beyond anything seen in training.
  • Compute the scaling on the training data only, save μ and σ with the model, and apply the same numbers in service. Scaling with statistics from the whole set lets test information leak in.
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
model = make_pipeline(StandardScaler(), LogisticRegression())   # the scaler is fitted on training data only
model.fit(X_train, y_train)

Rare events and class imbalance

Faults are rare, so the “faulty” class is often a tiny fraction of the data. A model can then score very high accuracy by never predicting a fault.

Worked example: 99 % accuracy that catches nothing

A data set has 5 000 windows, of which 50 are faulty. A “model” that always says healthy is right 4 950 times: 99.0 % accuracy, with zero faults found. The imbalance ratio is 99 to 1.

  • Use metrics that see the rare class: precision, recall, F1 and the precision-recall curve (see Classification and Metrics).
  • Stratify the split so that each part has the same fraction of faults (stratify=y).
  • Reweight the classes (class_weight="balanced") so that a fault counts about 99 times as much, or oversample the rare class in the training data only.
  • Treat it as anomaly detection when faults are too rare or too varied to learn (see Trees, Clustering and Anomaly Detection).
  • Get more faults: run-to-failure tests, seeded faults on a test rig, simulation, or data from similar machines. Label them carefully.

Training, validation and test sets

SetPurposeTypical shareRows of 4 200
TrainingFit the parametersAbout 70 %2 940
ValidationChoose the model, the settings and the thresholdAbout 15 %630
TestOne honest final score, used onceAbout 15 %630
  • Split before you look and before you scale. Whatever you learn from the test set tunes the model to it and spoils the score.
  • Keep the test set locked away until the end. If you pick a model because of its test score, the test set has become a validation set.
  • Split by the unit that will be new in service: by machine, by batch, by day, not by row. Rows from the same machine in both training and test make the model look better than it will be on a new machine.
  • Small data: use cross-validation (below) for choosing, and keep a small test set for the final number.
from sklearn.model_selection import train_test_split, GroupShuffleSplit
# Random rows (only when rows are independent), keeping the fault fraction the same
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.15, stratify=y, random_state=0)
# Keep each machine entirely in one part
gss = GroupShuffleSplit(n_splits=1, test_size=0.15, random_state=0)
tr_idx, te_idx = next(gss.split(X, y, groups=machine_id))

Time-ordered data

When rows follow one another in time, shuffling them is wrong: neighbors are nearly identical, so a random split puts a sample in training right next to its twin in the test set, and the future leaks into the past. Split by time: train on the earlier data, validate on the next stretch, test on the newest, which is exactly how the model will be used.

Splitting time-ordered dataoldernewerRandom splitLooks fair, but training samples sit on both sides of every test sample: neighbors leak, and the future teaches the past.ChronologicalTrain on the past, validate on the next stretch, test on the newest. This is how the model will be used.CV fold 1CV fold 2CV fold 3Expanding-window cross-validation: each fold trains on all earlier data and validates on the next block.trainvalidationtestunused
A random split leaks neighbors; a chronological split and an expanding-window cross-validation respect the order of time.

Worked example: windows from a recording

  • A 600 s recording at 1 000 Hz is cut into windows of 0.5 s (500 samples) every 0.25 s: (600 − 0.5) ÷ 0.25 + 1 = 2 399 windows.
  • With 50 % overlap, each window shares half its samples with the next. If these windows are split at random, most test windows have a near-identical training neighbor. Split the recording into whole stretches first, then window each stretch.
  • Leave a gap (a “purge” of at least one window length) between the end of training and the start of the test stretch so that no window straddles the boundary.

Cross-validation

k-fold cross-validation splits the training data into k parts, trains on k − 1 of them and validates on the remaining one, k times over, then averages. It gives a steadier estimate than one split, which matters when data is scarce.

Worked example: five folds

With 4 200 rows and k = 5, each fold holds 840 rows: five models are each trained on 3 360 rows and validated on 840. If the five scores are 0.88, 0.91, 0.90, 0.86 and 0.92, the mean is 0.89; the spread (about ±0.03) is the honest uncertainty, and a model whose advantage over another is smaller than that spread is not clearly better.

  • Use the right variant: stratified k-fold for imbalanced classes, group k-fold when rows belong to machines or batches, and a time-series split (expanding window) for ordered data.
  • Put all preprocessing inside the pipeline so that scaling and feature selection are re-fitted within each fold, not before it.

Data leakage

Leakage is when information about the answer reaches the model through a route that will not exist in service. It produces impressive test scores and useless real-world performance. It is the most common reason a model “works” in the lab and fails in the field.

LeakExampleHow to avoid
Future informationA maintenance-log field written after a failure; a feature computed with a centered rolling window that uses later samplesUse only what was known at the time of prediction; use trailing windows
Duplicates across the splitThe same run or overlapping windows in training and testSplit by run or machine; remove duplicates first
Preprocessing before the splitScaling or choosing features with all the dataFit everything on training only, inside a pipeline
A proxy for the labelA column that is only filled in for faulty cases, a file name or an operator ID that correlates with the outcomeInspect feature importances; remove anything that will not exist in service
Tuning on the test setTrying ten models and reporting the best test scoreChoose on validation; test once

Worked example: a score that was too good

A fault classifier scores 99 % on a random split. Investigation finds 18 % of the test windows overlap training windows from the same run. Re-splitting by run gives 91 %, which is what to expect on a new machine. The drop of 8 points was the leakage. Be suspicious of very high scores, especially on the first try.

How much data is enough?

ModelRough needRemarks
Straight-line fitTens of pointsFewer if the range is wide and the noise small
Linear model with p inputsAbout 10 rows per parameterFor 12 parameters, about 120 independent rows
Random forest or boosted trees on a tableHundreds to thousandsMore if the classes are imbalanced
Neural network on signals or imagesThousands to millionsFewer with a pretrained network or augmentation (see Neural Networks and Deep Learning)

Worked example: how precise is a test score?

A classifier scores 90 % accuracy on 600 test samples. The 95 % margin of error is about 1.96 × √(p (1 − p) ÷ n) = ±2.4 points. On only 100 test samples the same score is ±5.9 points: two models that differ by 3 points cannot be told apart. A test set must be large enough for the difference you care about, and for rare faults it must contain enough faults (not just enough rows).

  • Independent rows count, not total rows: ten thousand windows from one machine on one day are one example of that day.
  • Data augmentation (adding noise, shifting, scaling, small rotations for images) stretches a small set, if the changes mimic real variation.
  • Learning curves (score against training size, see Overfitting, Validation and Tuning) tell you whether more data would help.

Related

FAQ

Why should I split before scaling?

Because the scaling numbers (the mean and standard deviation) are learned from data. If they come from the whole set, information about the test rows has leaked into the training. Fit the scaler on the training part only, then apply it to the rest.

Should I shuffle time series?

No. Shuffling puts near-identical neighbors on both sides of the split and lets the future teach the past. Split by time and, where possible, by machine.

How do I handle missing values?

Understand why they are missing. Drop a few scattered rows, fill short gaps by interpolation, and add a “missing” flag for the rest. Never fill the test set using statistics from itself.

What if I have very few faults?

Use stratified splits and recall, precision and F1; reweight the classes; get more fault data by tests or simulation; or switch to anomaly detection trained on normal data.

References

  • Géron, A., Hands-On Machine Learning with Scikit-Learn, Keras and TensorFlow, O’Reilly (data preparation and validation chapters).
  • scikit-learn user guide: cross-validation, preprocessing and pipelines; Kaufman, S. and others, “Leakage in data mining”, ACM TKDD, 2012.
  • Zinkevich, M., Rules of Machine Learning, Google (free online).

Last reviewed: October 2026. Counts and scores in the worked examples are illustrative: compute your own.