Preprocessing#
macroforecast.preprocessing turns a canonical pandas panel from
macroforecast.data into a processed panel plus metadata. The preferred input is
a DataBundle or DataSpec; the output is a PreprocessedData object. The
default reprocess() path follows the public McCracken-Ng FRED-MD Matlab
workflow for FRED-MD/FRED-QD style panels.
For use inside a POOS runner, preprocess_spec stores the preprocessing choices
without executing them. The runner then applies the spec at each origin, refitting
stateful steps (outlier thresholds, EM factors, standardization scale) only on
the estimation-window rows available at that origin. This is the leak-free path.
Key Callables#
mf.preprocessing.reprocess applies the full preprocessing sequence to a panel
immediately (full-sample; for exploration and single-shot use).
mf.preprocessing.preprocess_spec stores preprocessing choices for runner-fitted
execution. Pass the returned PreprocessSpec to forecasting.run or to an
Arm in pipeline_spec.
import macroforecast as mf
# Full-sample preprocessing (for exploration).
processed = mf.preprocessing.reprocess(
data_spec,
transform="official", # apply McCracken-Ng t-codes
outliers="iqr", # IQR-based outlier replacement
impute="em_factor", # EM algorithm factor imputation
standardize="zscore",
)
# Deferred preprocessing spec for runner-fitted execution (leak-free).
prep_spec = mf.preprocessing.preprocess_spec(
transform="official",
outliers="iqr",
impute="em_factor",
standardize="zscore",
)
# Pass prep_spec to Arm(..., preprocessing=prep_spec) or forecasting.run(...).
Executed walkthrough#
Running the full-sample path on the loaded data_spec applies the t-code
transforms, flags outliers, imputes by EM factor, and standardizes:
processed = mf.preprocessing.reprocess(
data_spec,
transform="official", outliers="iqr", impute="em_factor", standardize="zscore",
)
print(type(processed).__name__, processed.panel.shape)
print("NaN before:", int(bundle.panel.isna().sum().sum()),
"| NaN after:", int(processed.panel.isna().sum().sum()))
print(processed.panel.iloc[:3, :4])
PreprocessedData (694, 128)
NaN before: 942 | NaN after: 0
RPI W875RX1 DPCERA3M086SBEA CMRMTSPLx
date
1960-03-01 -0.166574 -0.325648 2.178270 -2.831788
1960-04-01 0.132535 0.205311 2.425007 0.704389
1960-05-01 -0.026582 0.006684 -4.410584 -3.134124
The output panel is stationary and standardized. The row count drops from 708 to
694 because the official transforms difference the early observations away, and
the EM-factor step fills the 942 missing entries, leaving none remaining. This
is the full-sample path for exploration; inside a runner, preprocess_spec
refits these same steps on each origin’s estimation rows only.
Custom Steps And Caches#
Custom preprocessing steps run after the built-in transform/outlier/impute/
standardize/frame sequence. In the normal origin_available runner path, fitted
standardization is applied before custom steps at transform time, matching the
fit-time order, so scale-sensitive custom steps see the same units on train and
test rows.
Disk-backed preprocessing caches use content-derived identities. If a
preprocess_spec(custom_steps=...) contains a custom callable, set a stable
func.__mf_digest__ string before using preprocessing_cache_dir or a
PreprocessorStore; update the digest when the callable behavior changes. Named
custom callables without a digest still run, but disk get/put is skipped and the
runner recomputes instead of risking stale reuse. Anonymous lambda custom steps
without __mf_digest__ are rejected because they collide by qualified name.
Under policy="fit_window", built-in outlier/imputation/standardization state is
fitted on the training window and applied to later rows, but custom steps are
re-executed on the apply window. Keep those custom steps row-local/stateless; a
custom step that computes statistics from all rows it receives can read
post-origin rows and leak future information.
Reference#
Preprocessing reference page — full function list including
plan,report,apply_transform_codes, and individual step callables.