Bind the source identifier; load inside the graph; mark X / y at
the split. When declaring a new pipeline, the root
skrub.var(name, value=...) binds a source identifier — a
file path, URL, table name, query — and the loader is the first
.skb.apply_func. Inside the graph, derive X and y, then apply
.skb.mark_as_X() / .skb.mark_as_y() at the split.
path = skrub.var("path", "data/train.parquet")
data = path.skb.apply_func(load_parquet)
X = data.drop(["id", "target"], axis=1).skb.mark_as_X()
y = data["target"].skb.mark_as_y()
X = X.skb.apply_func(feature_engineering_step)
predictions = X.skb.apply(predictor, y=y)
The env-dict at fit / cross-validate time is then one binding per
source (learner.fit({"path": "data/test.parquet"})); swapping
data sources is one string change.
Note on the downstream evaluation contract. A SkrubLearner
does not implement sklearn's fit(X, y) signature — it takes a
single environment dict. Pair it with
skore.evaluate(learner, data={"path": ..., ...}, splitter=...)
(or data={"X": X, "y": y} for a materialized binding) — never
with skore.evaluate(learner, X, y, ...), which raises. The
data= argument is the env-dict-style equivalent of X / y on
skore.evaluate, CrossValidationReport, and the
train_data= / test_data= pair on EstimatorReport. See
evaluate-ml-pipeline and skore-api/reports.md § "skrub
interop".
Cross-validation metadata at the X marker. If the data has
group structure (subjects, sessions, customer IDs, repeated
measures) or temporal ordering, attach the relevant column at
the X marker via .skb.mark_as_X(split_kwargs={...}). The keys
in split_kwargs map directly to the keyword arguments of the
future cross-validator's split(X, y, **split_kwargs) (e.g.
groups). See skrub.DataOp.skb.mark_as_X.
X = data.drop([...]).skb.mark_as_X(
split_kwargs={"groups": data["customer_id"]},
)
Ask the user when you can't tell from the data alone whether
such structure exists — name the suspect columns (anything ending
in _id, columns called subject / session / region, or
any date / timestamp for temporal ordering) and ask whether
to wire them. Do not silently leave split_kwargs empty when
group structure is plausible — that produces optimistic
evaluations downstream. Choosing the splitter itself is owned by
evaluate-ml-pipeline; this skill only ensures the metadata is
wired in.
The skrub.X(...) / skrub.y(...) shortcuts are not acceptable
roots: per skrub-api → data_ops.md they are literally
var("X", value).skb.mark_as_X() and var("y", value).skb.mark_as_y(),
which bakes in the variable name and the marker at the root and
forces a pre-loaded, pre-split binding.
When editing an existing pipeline that already binds materialized
data (or uses the shortcuts), do not auto-rewrite. Surface the
source-bound alternative and ask whether to refactor.
Full catalogue (encouraged / discouraged / OK-but-offer-an-upgrade):
references/source-binding.md.