Skip to content

Adopt the scikit-learn fit(X, y, treatment, ...) argument order (v1.0 M1) #980

Description

@jeongyoonlee

Summary

CausalML learners take fit(X, treatment, y, ...), which puts y third. sklearn.pipeline.Pipeline calls the final estimator's fit(X, y) positionally, so today a CausalML learner can't be a Pipeline step without an adapter — the problem reported in #854.

The v1.0 roadmap RFC lists this under M1 — Foundation & stability as the one intentional breaking change: adopt fit(X, y, treatment, ...).

Reordering two required positional arguments can't be done in one step. y and treatment are both same-length arrays, so a straight swap would silently mis-train existing positional callers with no TypeError to catch it. So this is a two-step deprecation, and this epic tracks it end to end.

Step one has landed (#975): positional order is unchanged, but passing treatment/y positionally emits a FutureWarning pointing callers at keyword arguments — fit(X, y=y, treatment=treatment) — which are order-independent and therefore safe across the flip.

The rule

One rule, applied uniformly: X, then y, then treatment, and every other parameter keeps its relative position. It is implemented in causalml/inference/_arg_order.py as v1_order(), derived per method from that method's own signature, so the target order is machine-checkable rather than hand-maintained:

method today v1.0
meta-learner fit (X, treatment, y, p, …) (X, y, treatment, p, …)
predict (X, treatment, y, p, …) (X, y, treatment, p, …)
CausalTreeRegressor.fit (X, treatment, y, sample_weight, …) (X, y, treatment, sample_weight, …)
IVRegressor.fit (X, treatment, y, w) (X, y, treatment, w)
BaseDRIVLearner.fit (X, assignment, treatment, y, …) (X, y, treatment, assignment, …)

predict is included deliberately. Its current order doesn't block Pipeline, since predict(X) already works — but flipping only fit would leave v1.0 with the same two arguments in opposite positional orders on one class. Methods that don't take treatment/y at all, such as BaseRLearner.predict, are untouched.

Why the sequencing matters

A deprecation window is one-shot. Anything whose positional order changes at v1.0 has to warn in the same release as the shim, or it needs a second deprecation cycle of its own. That is why the "close the window" issues below are not optional cleanup — they gate the flip, and they must ship alongside #975 rather than with it.

The flip itself is inherently breaking and cannot be staged: once a signature is reordered, old positional calls break silently. It therefore lands as a single change on the v1.0 line, per-family checklist and all.

Child issues (dependency-ordered)

Close the deprecation window — ships with the warning release:

The flip — v1.0:

Actually deliver #854:

Reordering alone does not make a learner a drop-in Pipeline step: treatment still has to be threaded through sklearn's metadata routing. #854 stays open until that lands.

Open questions

  1. UpliftTreeClassifier.fit takes (X, treatment, y, X_val, treatment_val, y_val, sample_weight, check_input). The rule above moves the first triple but leaves the validation triple as X_val, treatment_val, y_val — internally inconsistent. Reorder the *_val arguments too, or leave them and document the asymmetry?
  2. Sensitivity.get_prediction / .get_ate_ci / .get_potential_outcome_predictions take (X, p, treatment, y) — a third shape, with p second. In scope for the flip, or left alone as evaluation-side helpers?
  3. Window length. How many minor releases between the warning and the flip? Answered: releases are quarterly — 0.18.0 Sep 2026 (ships the warning), 0.19.0 Dec 2026, 0.20.0 Mar 2027, v1.0 Jun 2027 (the flip). Three minor releases, roughly nine months. Published in the migration guide and changelog by Publish the release schedule and the #854 deprecation window #993, so Flip every signature to (X, y, treatment, ...) and delete the deprecation shim #985 is no longer blocked.

Refs #854. Part of the v1.0 M1 milestone (#938).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions