Skip to content

API: targets

Target dataclass

Target(
    space: str,
    lo: float,
    hi: float,
    bands_spec: tuple[tuple[str, float, float], ...]
    | None = None,
    calibrator: _SupportsIntervalInverse | None = None,
    buffer_logit: float = 0.0,
)

Closed interval target, expressed in raw-score, probability, or calibrated space.

Construct through one of the classmethods (raw, probability, calibrated, bands) rather than the constructor directly — they validate their space's bounds and normalize the range/op/value shorthand into lo/hi. Target.bands builds a named ladder of intervals (rating grades); Explainer.explain then returns one result per band instead of a single Counterfactual/Infeasible. See Targets and, for calibrated, Calibration.

ATTRIBUTE DESCRIPTION
space

"raw", "probability", or "calibrated" — which space lo/hi are expressed in.

TYPE: str

lo

Lower bound of the target interval, in space.

TYPE: float

hi

Upper bound of the target interval, in space.

TYPE: float

bands_spec

(name, lo, hi) per band, set only by Target.bands; None for a plain single-interval target. When set, lo/ hi/space describe the first band only — use band_intervals to resolve every band.

TYPE: tuple[tuple[str, float, float], ...] | None

calibrator

The fitted calibrator, set only by Target.calibrated (or Target.bands(space="calibrated", ...)); None otherwise.

TYPE: _SupportsIntervalInverse | None

buffer_logit

Logit-space shrinkage applied before inverting a calibrated interval; 0.0 (no shrinkage) unless Target.calibrated was given a positive value.

TYPE: float

raw classmethod

raw(
    range: tuple[float, float] | None = None,
    op: str | None = None,
    value: float | None = None,
) -> Target

Target on the model's raw output (pre-link margin for a sigmoid model).

Specify exactly one of range=(lo, hi) or op=/value= (op="<=" or op=">=" with value, giving a half-open bound against -inf/+inf).

PARAMETER DESCRIPTION
range

Explicit (lo, hi) bound, lo < hi required.

TYPE: tuple[float, float] | None DEFAULT: None

op

"<=" or ">=", paired with value; mutually exclusive with range.

TYPE: str | None DEFAULT: None

value

The threshold paired with op.

TYPE: float | None DEFAULT: None

RETURNS DESCRIPTION
A ``Target`` with ``space="raw"``.
RAISES DESCRIPTION
TargetError

If neither or both of range and op/value are given, if op is not "<="/">=", or if the resulting interval is empty.

Source code in src/treecf/targets.py
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@classmethod
def raw(
    cls,
    range: tuple[float, float] | None = None,
    op: str | None = None,
    value: float | None = None,
) -> Target:
    """Target on the model's raw output (pre-link margin for a sigmoid model).

    Specify exactly one of ``range=(lo, hi)`` or ``op=``/``value=``
    (``op="<="`` or ``op=">="`` with ``value``, giving a half-open bound
    against ``-inf``/``+inf``).

    Parameters
    ----------
    range
        Explicit ``(lo, hi)`` bound, ``lo < hi`` required.
    op
        ``"<="`` or ``">="``, paired with ``value``; mutually
        exclusive with ``range``.
    value
        The threshold paired with ``op``.

    Returns
    -------
    A ``Target`` with ``space="raw"``.

    Raises
    ------
    TargetError
        If neither or both of ``range`` and ``op``/``value``
        are given, if ``op`` is not ``"<="``/``">="``, or if the
        resulting interval is empty.
    """
    lo, hi = _interval_from(range, op, value, lo_limit=-math.inf, hi_limit=math.inf)
    return cls(space="raw", lo=lo, hi=hi)

probability classmethod

probability(
    range: tuple[float, float] | None = None,
    op: str | None = None,
    value: float | None = None,
) -> Target

Target on the model's own probability output.

If model outputs are post-hoc calibrated downstream, this constructor targets the uncalibrated model probability; use Target.calibrated with your calibrator instead. Requires a SIGMOID-link model — resolved lazily, at raw_interval time, not here. Specify exactly one of range=(lo, hi) or op=/value=, as in Target.raw.

PARAMETER DESCRIPTION
range

Explicit (lo, hi) bound within [0, 1].

TYPE: tuple[float, float] | None DEFAULT: None

op

"<=" or ">=", paired with value; mutually exclusive with range.

TYPE: str | None DEFAULT: None

value

The threshold paired with op.

TYPE: float | None DEFAULT: None

RETURNS DESCRIPTION
A ``Target`` with ``space="probability"``.
RAISES DESCRIPTION
TargetError

If neither or both of range and op/value are given, if op is not "<="/">=", or if the resulting interval is empty or falls outside [0, 1].

Source code in src/treecf/targets.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
@classmethod
def probability(
    cls,
    range: tuple[float, float] | None = None,
    op: str | None = None,
    value: float | None = None,
) -> Target:
    """Target on the model's own probability output.

    If model outputs are post-hoc calibrated downstream, this constructor
    targets the *uncalibrated* model probability; use ``Target.calibrated``
    with your calibrator instead. Requires a SIGMOID-link model — resolved
    lazily, at ``raw_interval`` time, not here. Specify exactly one of
    ``range=(lo, hi)`` or ``op=``/``value=``, as in ``Target.raw``.

    Parameters
    ----------
    range
        Explicit ``(lo, hi)`` bound within ``[0, 1]``.
    op
        ``"<="`` or ``">="``, paired with ``value``; mutually
        exclusive with ``range``.
    value
        The threshold paired with ``op``.

    Returns
    -------
    A ``Target`` with ``space="probability"``.

    Raises
    ------
    TargetError
        If neither or both of ``range`` and ``op``/``value``
        are given, if ``op`` is not ``"<="``/``">="``, or if the
        resulting interval is empty or falls outside ``[0, 1]``.
    """
    lo, hi = _interval_from(range, op, value, lo_limit=0.0, hi_limit=1.0)
    if not (0.0 <= lo < hi <= 1.0):
        raise TargetError(f"probability interval [{lo}, {hi}] must lie within [0, 1]")
    return cls(space="probability", lo=lo, hi=hi)

calibrated classmethod

calibrated(
    calibrator: _SupportsIntervalInverse,
    range: tuple[float, float] | None = None,
    op: str | None = None,
    value: float | None = None,
    *,
    buffer_logit: float = 0.0,
) -> Target

Target on the calibrated probability g(model probability).

The interval is inverted through the calibrator's generalized inverse lazily, at raw_interval time; the calibrator is held by reference, so refitting it between construction and explain is the caller's responsibility. buffer_logit shrinks the calibrated interval in logit space before inversion, making the counterfactual robust to future recalibration or central-tendency drift of that magnitude. Specify exactly one of range=(lo, hi) or op=/value=, as in Target.raw. See Calibration.

PARAMETER DESCRIPTION
calibrator

Object exposing is_monotone_: bool (must be True) and interval_inverse(lo, hi, *, space, buffer_logit), returning generalized-inverse bounds on the logit of the model probability.

TYPE: _SupportsIntervalInverse

range

Explicit (lo, hi) bound within [0, 1], in the calibrated probability's own space.

TYPE: tuple[float, float] | None DEFAULT: None

op

"<=" or ">=", paired with value; mutually exclusive with range.

TYPE: str | None DEFAULT: None

value

The threshold paired with op.

TYPE: float | None DEFAULT: None

buffer_logit

Logit-space shrinkage applied before inversion; must be >= 0.0. Defaults to 0.0 (no shrinkage).

TYPE: float DEFAULT: 0.0

RETURNS DESCRIPTION
A ``Target`` with ``space="calibrated"``.
RAISES DESCRIPTION
TargetError

If calibrator does not expose interval_inverse/is_monotone_ or is not monotone, if buffer_logit < 0.0, if neither or both of range and op/value are given, if op is not "<="/">=", or if the resulting interval is empty or falls outside [0, 1].

Source code in src/treecf/targets.py
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
@classmethod
def calibrated(
    cls,
    calibrator: _SupportsIntervalInverse,
    range: tuple[float, float] | None = None,
    op: str | None = None,
    value: float | None = None,
    *,
    buffer_logit: float = 0.0,
) -> Target:
    """Target on the *calibrated* probability ``g(model probability)``.

    The interval is inverted through the calibrator's generalized inverse
    lazily, at ``raw_interval`` time; the calibrator is held by reference,
    so refitting it between construction and ``explain`` is the caller's
    responsibility. ``buffer_logit`` shrinks the calibrated interval in
    logit space before inversion, making the counterfactual robust to
    future recalibration or central-tendency drift of that magnitude.
    Specify exactly one of ``range=(lo, hi)`` or ``op=``/``value=``, as in
    ``Target.raw``. See [Calibration](../concepts/calibration.md).

    Parameters
    ----------
    calibrator
        Object exposing ``is_monotone_: bool`` (must be
        ``True``) and ``interval_inverse(lo, hi, *, space,
        buffer_logit)``, returning generalized-inverse bounds on the
        logit of the model probability.
    range
        Explicit ``(lo, hi)`` bound within ``[0, 1]``, in the
        calibrated probability's own space.
    op
        ``"<="`` or ``">="``, paired with ``value``; mutually
        exclusive with ``range``.
    value
        The threshold paired with ``op``.
    buffer_logit
        Logit-space shrinkage applied before inversion;
        must be ``>= 0.0``. Defaults to ``0.0`` (no shrinkage).

    Returns
    -------
    A ``Target`` with ``space="calibrated"``.

    Raises
    ------
    TargetError
        If ``calibrator`` does not expose
        ``interval_inverse``/``is_monotone_`` or is not monotone, if
        ``buffer_logit < 0.0``, if neither or both of ``range`` and
        ``op``/``value`` are given, if ``op`` is not ``"<="``/``">="``,
        or if the resulting interval is empty or falls outside
        ``[0, 1]``.
    """
    _validate_calibrator(calibrator, buffer_logit)
    lo, hi = _interval_from(range, op, value, lo_limit=0.0, hi_limit=1.0)
    if not (0.0 <= lo < hi <= 1.0):
        raise TargetError(f"calibrated interval [{lo}, {hi}] must lie within [0, 1]")
    return cls(
        space="calibrated", lo=lo, hi=hi, calibrator=calibrator, buffer_logit=buffer_logit
    )

bands classmethod

bands(
    bands: dict[str, tuple[float, float]],
    space: str = "probability",
    *,
    calibrator: _SupportsIntervalInverse | None = None,
    buffer_logit: float = 0.0,
) -> Target

A named ladder of intervals — rating grades solved in one explain call.

Explainer.explain(x, target=Target.bands(...)) returns a {band_name: Counterfactual | Infeasible} dict instead of a single result, one entry per band in bands' insertion order.

PARAMETER DESCRIPTION
bands

{name: (lo, hi)} per band, in space; at least one required.

TYPE: dict[str, tuple[float, float]]

space

"raw", "probability" (the default), or "calibrated" — the space every band's (lo, hi) is expressed in.

TYPE: str DEFAULT: 'probability'

calibrator

Required when space="calibrated"; see Target.calibrated. Ignored otherwise.

TYPE: _SupportsIntervalInverse | None DEFAULT: None

buffer_logit

Logit-space shrinkage applied to every band when space="calibrated"; see Target.calibrated. Defaults to 0.0.

TYPE: float DEFAULT: 0.0

RETURNS DESCRIPTION
A ``Target`` with ``bands_spec`` set to one ``(name, lo, hi)`` per
band; ``lo``/``hi``/``space`` mirror the first band for callers
that only look at the plain-interval fields.
RAISES DESCRIPTION
TargetError

If space is not "raw"/"probability"/ "calibrated", if bands is empty, if any band's interval is empty or (for "probability"/"calibrated") falls outside [0, 1], or if space="calibrated" and calibrator fails the same validation as Target.calibrated.

Source code in src/treecf/targets.py
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
@classmethod
def bands(
    cls,
    bands: dict[str, tuple[float, float]],
    space: str = "probability",
    *,
    calibrator: _SupportsIntervalInverse | None = None,
    buffer_logit: float = 0.0,
) -> Target:
    """A named ladder of intervals — rating grades solved in one ``explain`` call.

    ``Explainer.explain(x, target=Target.bands(...))`` returns a
    ``{band_name: Counterfactual | Infeasible}`` dict instead of a single
    result, one entry per band in ``bands``' insertion order.

    Parameters
    ----------
    bands
        ``{name: (lo, hi)}`` per band, in ``space``; at least one
        required.
    space
        ``"raw"``, ``"probability"`` (the default), or
        ``"calibrated"`` — the space every band's ``(lo, hi)`` is
        expressed in.
    calibrator
        Required when ``space="calibrated"``; see
        ``Target.calibrated``. Ignored otherwise.
    buffer_logit
        Logit-space shrinkage applied to every band when
        ``space="calibrated"``; see ``Target.calibrated``. Defaults to
        ``0.0``.

    Returns
    -------
    A ``Target`` with ``bands_spec`` set to one ``(name, lo, hi)`` per
    band; ``lo``/``hi``/``space`` mirror the first band for callers
    that only look at the plain-interval fields.

    Raises
    ------
    TargetError
        If ``space`` is not ``"raw"``/``"probability"``/
        ``"calibrated"``, if ``bands`` is empty, if any band's
        interval is empty or (for ``"probability"``/``"calibrated"``)
        falls outside ``[0, 1]``, or if ``space="calibrated"`` and
        ``calibrator`` fails the same validation as
        ``Target.calibrated``.
    """
    if space not in ("raw", "probability", "calibrated"):
        raise TargetError("bands space must be 'raw', 'probability', or 'calibrated'")
    if space == "calibrated":
        _validate_calibrator(calibrator, buffer_logit)
    if not bands:
        raise TargetError("bands must contain at least one named interval")
    spec = []
    for name, (lo, hi) in bands.items():
        if not lo < hi:
            raise TargetError(f"band {name!r}: empty interval [{lo}, {hi}]")
        if space in ("probability", "calibrated") and not (0.0 <= lo < hi <= 1.0):
            raise TargetError(f"band {name!r} must lie within [0, 1]")
        spec.append((name, float(lo), float(hi)))
    first = spec[0]
    return cls(
        space=space,
        lo=first[1],
        hi=first[2],
        bands_spec=tuple(spec),
        calibrator=calibrator,
        buffer_logit=buffer_logit,
    )

raw_interval

raw_interval(link: Link) -> tuple[float, float]

Resolve this target to an [L, U] interval on the model's raw score.

A space="raw" target returns (lo, hi) unchanged. A space="probability" target inverts through the logistic function. A space="calibrated" target inverts through calibrator's own generalized inverse in logit space, shrunk by buffer_logit first. Both non-raw spaces require link to be Link.SIGMOID — this is where that requirement is actually enforced, not at construction time.

PARAMETER DESCRIPTION
link

The model's link function (Explainer.ir.link).

TYPE: Link

RETURNS DESCRIPTION
``(lo, hi)`` on the raw score, ``lo <= hi``.
RAISES DESCRIPTION
TargetError

If space is "probability" or "calibrated" and link is not Link.SIGMOID, or if calibrator raises while inverting the interval.

Source code in src/treecf/targets.py
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
def raw_interval(self, link: Link) -> tuple[float, float]:
    """Resolve this target to an ``[L, U]`` interval on the model's raw score.

    A ``space="raw"`` target returns ``(lo, hi)`` unchanged. A
    ``space="probability"`` target inverts through the logistic function.
    A ``space="calibrated"`` target inverts through ``calibrator``'s own
    generalized inverse in logit space, shrunk by ``buffer_logit`` first.
    Both non-raw spaces require ``link`` to be ``Link.SIGMOID`` — this is
    where that requirement is actually enforced, not at construction time.

    Parameters
    ----------
    link
        The model's link function (``Explainer.ir.link``).

    Returns
    -------
    ``(lo, hi)`` on the raw score, ``lo <= hi``.

    Raises
    ------
    TargetError
        If ``space`` is ``"probability"`` or ``"calibrated"``
        and ``link`` is not ``Link.SIGMOID``, or if ``calibrator``
        raises while inverting the interval.
    """
    if self.space == "raw":
        return self.lo, self.hi
    if self.space == "calibrated":
        if link is not Link.SIGMOID:
            raise TargetError(
                "calibrated target requires a SIGMOID-link model; "
                "use Target.raw for identity-link outputs"
            )
        assert self.calibrator is not None
        try:
            return self.calibrator.interval_inverse(
                self.lo, self.hi, space="logit", buffer_logit=self.buffer_logit
            )
        except TargetError:
            raise
        except Exception as exc:
            raise TargetError(
                f"calibrator could not invert [{self.lo}, {self.hi}]: {exc}"
            ) from exc
    if link is not Link.SIGMOID:
        raise TargetError(
            "probability target requires a SIGMOID-link model; "
            "use Target.raw for identity-link outputs"
        )
    return _logit(self.lo), _logit(self.hi)

band_intervals

band_intervals(
    link: Link,
) -> dict[str, tuple[float, float]]

Resolve every band of a Target.bands ladder to a raw-score interval.

Applies raw_interval band by band, in bands_spec's order, each against a copy of this target's own space/calibrator/ buffer_logit.

PARAMETER DESCRIPTION
link

The model's link function (Explainer.ir.link).

TYPE: Link

RETURNS DESCRIPTION
``{band_name: (lo, hi)}`` on the raw score, one entry per band.
RAISES DESCRIPTION
TargetError

Under the same conditions as raw_interval, for any band.

Source code in src/treecf/targets.py
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
def band_intervals(self, link: Link) -> dict[str, tuple[float, float]]:
    """Resolve every band of a ``Target.bands`` ladder to a raw-score interval.

    Applies ``raw_interval`` band by band, in ``bands_spec``'s order,
    each against a copy of this target's own ``space``/``calibrator``/
    ``buffer_logit``.

    Parameters
    ----------
    link
        The model's link function (``Explainer.ir.link``).

    Returns
    -------
    ``{band_name: (lo, hi)}`` on the raw score, one entry per band.

    Raises
    ------
    TargetError
        Under the same conditions as ``raw_interval``, for
        any band.
    """
    assert self.bands_spec is not None
    out: dict[str, tuple[float, float]] = {}
    for name, lo, hi in self.bands_spec:
        single = Target(
            space=self.space,
            lo=lo,
            hi=hi,
            calibrator=self.calibrator,
            buffer_logit=self.buffer_logit,
        )
        out[name] = single.raw_interval(link)
    return out