2.2 Feature Engineering & Feature Selection in BigQuery ML
Key Takeaways
- BigQuery ML automatically standardizes numeric features for most model types but not for boosted tree or random forest models.
- Preprocessing in the TRANSFORM clause is saved with the model and reapplied automatically by ML.EVALUATE and ML.PREDICT, which prevents training-serving skew.
- ML analytic preprocessing functions such as ML.QUANTILE_BUCKETIZE and ML.STANDARD_SCALER require an empty OVER() clause.
- ML.FEATURE_IMPORTANCE reports importance_weight, importance_gain, and importance_cover, and it works only for boosted tree and random forest models.
- ML.TRANSFORM returns the preprocessed data from a model's TRANSFORM clause, so you can inspect exactly what the model received.
Feature engineering often matters more for model quality than which algorithm you pick. The exam tests two BigQuery ML skills: knowing what BQML does automatically, and applying custom transformations so the same logic runs at training and prediction.
Automatic Preprocessing
When you run CREATE MODEL without custom preprocessing, BQML handles missing values and applies default transformations:
| Input type | Default transformation |
|---|---|
Numeric (INT64, NUMERIC, BIGNUMERIC, FLOAT64) | Standardization for most models. Exceptions: boosted tree and random forest models (no standardization) and k-means (controlled by STANDARDIZE_FEATURES) |
BOOL, STRING, BYTES, DATE, DATETIME, TIME | One-hot encoding. Tree models use label encoding instead |
Non-numeric ARRAY | Multi-hot encoding |
TIMESTAMP | For linear and logistic regression, split into Unix time (standardized) plus day of month, day of week, month, hour, minute, week, and year (one-hot) |
STRUCT | Expanded into struct_field columns |
Missing values: NULL numeric values are replaced with the column mean from training data. NULL categorical values become their own extra category. Categories that appear for the first time at prediction get zero weight.
The TRANSFORM Clause
The TRANSFORM clause defines preprocessing inside the model. The same logic, including statistics such as means and quantile boundaries computed during training, is reapplied automatically in ML.EVALUATE and ML.PREDICT. Clients send raw columns, so preprocessing done in client code can't drift from what the model learned.
CREATE OR REPLACE MODEL `ops.late_delivery`
TRANSFORM (
ML.IMPUTER(distance_km, 'median') OVER () AS distance_km,
ML.QUANTILE_BUCKETIZE(order_value, 10) OVER () AS value_bucket,
ML.FEATURE_CROSS(STRUCT(carrier, origin_region)) AS carrier_region,
ML.STANDARD_SCALER(items) OVER () AS items_scaled,
EXTRACT(DAYOFWEEK FROM order_ts) AS order_dow,
late
)
OPTIONS (model_type = 'LOGISTIC_REG', input_label_cols = ['late'])
AS SELECT distance_km, order_value, carrier, origin_region, items, order_ts, late
FROM `ops.orders_2025`;
Rules to remember:
- Analytic functions (such as
ML.QUANTILE_BUCKETIZE,ML.STANDARD_SCALER,ML.MIN_MAX_SCALER,ML.IMPUTER) compute statistics across all rows and must use an emptyOVER()clause. - Scalar functions (such as
ML.BUCKETIZEwith fixed boundaries, orML.FEATURE_CROSS) work one row at a time. - Columns you select but don't output from
TRANSFORMaren't used as features. ML.TRANSFORMreturns the transformed rows so you can debug what the model actually sees.- A transform-only model (
MODEL_TYPE = 'TRANSFORM_ONLY') stores preprocessing without training a predictor. Use it to share one feature pipeline across several models or to materialize features for other tools.
Manual Preprocessing Function Families
| Family | Functions | Use when |
|---|---|---|
| General | ML.IMPUTER | Replace missing values with mean, median, or most frequent |
| Numeric | ML.STANDARD_SCALER, ML.MIN_MAX_SCALER, ML.MAX_ABS_SCALER, ML.ROBUST_SCALER, ML.NORMALIZER, ML.BUCKETIZE, ML.QUANTILE_BUCKETIZE, ML.POLYNOMIAL_EXPAND | Scale skewed values, handle outliers (robust scaler), create non-linear features |
| Categorical | ML.ONE_HOT_ENCODER, ML.MULTI_HOT_ENCODER, ML.LABEL_ENCODER, ML.HASH_BUCKETIZE, ML.FEATURE_CROSS | Encode high-cardinality IDs (hash), capture interactions (cross) |
| Text | ML.NGRAMS, ML.BAG_OF_WORDS, ML.TF_IDF | Turn short text into sparse features |
| Image | ML.DECODE_IMAGE, ML.RESIZE_IMAGE, ML.CONVERT_COLOR_SPACE, ML.CONVERT_IMAGE_TYPE | Prepare images from object tables for imported vision models |
Practical feature patterns
- Bucketize a skewed numeric like income or order value so a linear model can learn step effects.
- Cross features like
carrier × regionwhen their combination drives the outcome, for example one carrier being late only in mountain regions. - Hash-bucketize very high-cardinality IDs to cap feature count, accepting some collisions.
- Extract calendar parts (day of week, hour) from timestamps when seasonality matters.
- Point-in-time features:
ML.FEATURES_AT_TIMEreturns feature values as of a cutoff time for each entity. This stops training rows from seeing information recorded after the label event, which is a classic source of leakage.
Feature Selection in SQL
Too many weak features slow training, raise overfitting risk, and make models harder to explain. BQML gives you several selection tools:
| Technique | How | Works with |
|---|---|---|
| Tree feature importance | ML.FEATURE_IMPORTANCE returns importance_weight (split count), importance_gain (average gain), importance_cover (average coverage) | Boosted tree and random forest only |
| Global explanations | Train with ENABLE_GLOBAL_EXPLAIN = TRUE, then query ML.GLOBAL_EXPLAIN | Supervised models that support explainability |
| Per-row attributions | ML.EXPLAIN_PREDICT with TOP_K_FEATURES | Many supervised models |
| Sparse weights | L1_REG pushes small weights to exactly zero | Linear, logistic, and tree models (tunable with HPARAM_RANGE) |
| Weight inspection | ML.WEIGHTS / ML.ADVANCED_WEIGHTS | Linear and logistic regression |
| Dimensionality reduction | PCA model outputs principal components | High-dimensional numeric inputs |
A practical loop: train a boosted tree with ENABLE_GLOBAL_EXPLAIN = TRUE, rank features by importance_gain and global attribution, drop features that add little or that leak the label (for example, "refund_issued" when predicting returns), then retrain and compare ML.EVALUATE metrics.
Worked Example: Tightening a Delivery Model
An operations team's late-delivery classifier has 120 input columns, trains slowly, and is hard to explain to carrier managers. A disciplined BQML workflow:
- Retrain as a boosted tree with
ENABLE_GLOBAL_EXPLAIN = TRUE. - Query
ML.FEATURE_IMPORTANCEandML.GLOBAL_EXPLAIN. Twelve features carry almost all of the gain. Several others turn out to be IDs that the model uses to memorize individual rows. - Check the top features for leakage.
actual_delivery_dateis recorded after the outcome, so remove it. - Move the kept features into a
TRANSFORMclause with bucketized distance and a carrier × region cross. - Compare
ML.EVALUATEon the same holdout. A smaller model with similar AUC is easier to explain, cheaper to run, and less likely to overfit.
Exam Traps
- Skew from duplicated logic: computing a z-score in a SQL view for training and again in application code for serving. Put it in
TRANSFORMor a transform-only model. - Unnecessary scaling for trees: standardizing inputs doesn't improve boosted trees, which is why BQML skips it for them.
- Leaky features: a feature that is only known after the outcome makes offline metrics look great and fails in production.
- Wrong importance tool:
ML.FEATURE_IMPORTANCEreturns an error for linear or DNN models. Use global explanations or weights instead.
An analyst computes normalized features in a SQL view for BigQuery ML training, and the application team re-implements the same normalization in Java for predictions. Accuracy dropped after launch. What BigQuery ML feature best prevents this problem?
Which statement about ML.QUANTILE_BUCKETIZE in a BigQuery ML TRANSFORM clause is correct?
A data scientist wants to rank features by average split gain in a BigQuery ML model to remove weak inputs. Which model and function combination supports this directly?