cleverly.interventions.JointPolicy#

class cleverly.interventions.JointPolicy(name: str, components: Mapping[Any, Any], randomizer: Randomizer | None = None, policy_kind: Literal['known', 'estimated'] | None = None, order: tuple[str, ...] | None = None)[source]#

A modified treatment policy on a vector node, declared one component at a time.

Díaz, Williams, Hoffman and Schenck (2023, Section 2) let the treatment of a node be a vector \(A_t = (C_t, D_t)\), with categorical components \(C_t\) and continuous components \(D_t\). A joint policy maps each component. A component left out of components keeps its natural value.

JointPolicy(
    "flag everyone, raise the dose of the flagged",
    components={
        "flag": ModifiedPolicy("flag", apply=flag_everyone, policy_kind="known"),
        "dose": ModifiedPolicy("up if flagged", pieces=(...), policy_kind="known"),
    },
    policy_kind="known",
)

The rules below make the density ratio exist, and the fit checks them before any learner (their Assumption 4, applied on each categorical cell and each value of the earlier doses):

component

policy and what its frame h holds

continuous

Shift, Scale, Piecewise, or a ModifiedPolicy with pieces. h holds the policy frame, the natural values of the node’s categorical components, and the natural values of the doses before it in order, by component name

categorical

a ModifiedPolicy with apply, an arithmetic policy on numeric labels, or a RiskRatioTilt on a component coded 0 and 1. h holds the policy frame and the natural values of every other component of the node

A read of any other component of the node raises DataError, h.get(...) included. With these rules the map of the doses is triangular for each categorical cell, so its Jacobian determinant is the product of the declared derivatives. A dose map that reads a later dose is refused with the order that makes it triangular. Maps that read each other in a cycle are refused: a joint map that is not triangular has no checkable declaration here, because a determinant checked on a grid does not establish that the map is one-to-one. A Randomizer may be declared here, on the joint policy, or on a component’s ModifiedPolicy. A RiskRatioTilt may set a binary component coded 0 and 1, as it sets a binary treatment. The randomizers are independent, so the fit reads the policy as one joint randomizer over the product of their values, with product probabilities (joint_branches()).

Parameters:
namestr

The report label.

componentsmapping

The component column of the node mapped to that component’s policy. With a randomizer, a mapping from each randomizer value to such a mapping.

randomizerRandomizer or None

The known law of the randomizer, or None for a deterministic policy.

policy_kind{“known”, “estimated”} or None

The declaration that the policy is a known function. A fit accepts "known" only.

ordertuple of str or None

The order of the doses: the map of each dose reads the natural values of the doses before it. It lists every continuous component of the node. A categorical component may be listed and its position has no effect, since a categorical map reads every natural value. None takes the node’s column order.

Methods#

Attributes#

JointPolicy.order: tuple[str, ...] | None = None#
JointPolicy.policy_kind: Literal['known', 'estimated'] | None = None#
JointPolicy.randomizer: Randomizer | None = None#
JointPolicy.name: str#
JointPolicy.components: Mapping[Any, Any]#