codex/conditioning-sum-dependent-binary-outcomes.py

conditioning-sum-dependent-binary-outcomes.py
"""Restrict independent binary outcomes to pairs with exactly one success.

All masses are derived exactly from a finite stipulated model, not observations.
Run in the output directory; versions are documented in pyproject.toml.
"""

from fractions import Fraction
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def model():
    joint = {(x, y): Fraction(1, 4) for x in (0, 1) for y in (0, 1)}
    selected_mass = sum(p for (x, y), p in joint.items() if x + y == 1)
    conditional = {xy: p / selected_mass if sum(xy) == 1 else Fraction(0)
                   for xy, p in joint.items()}
    ex = sum(x * p for (x, y), p in conditional.items())
    ey = sum(y * p for (x, y), p in conditional.items())
    exy = sum(x * y * p for (x, y), p in conditional.items())
    assert selected_mass == Fraction(1, 2)
    assert ex == ey == Fraction(1, 2)
    assert exy - ex * ey == -Fraction(1, 4)
    assert conditional[1, 1] == 0 and ex * ey == Fraction(1, 4)
    return joint, conditional


def main():
    fig, axes = plt.subplots(1, 2, figsize=(7.5, 4.0), dpi=130, layout="constrained")
    fig.set_facecolor("white")
    for ax, law, title in zip(axes, model(), ("All pairs: independent", r"Selected pairs: $X+Y=1$")):
        data = np.array([[float(law[x, y]) for y in (0, 1)] for x in (0, 1)])
        ax.imshow(data, cmap="Blues", vmin=0, vmax=0.6)
        for x in (0, 1):
            for y in (0, 1):
                ax.text(y, x, f"{data[x, y]:g}", ha="center", va="center", fontsize=20,
                        color="white" if data[x, y] > 0.35 else "#111111")
        ax.set_xticks((0, 1), labels=("0", "1"))
        ax.set_yticks((0, 1), labels=("0", "1"))
        ax.set(xlabel=r"$Y$", ylabel=r"$X$", title=title)
        ax.tick_params(length=0)
    fig.suptitle("Conditioning can create dependence", fontsize=14)
    fig.savefig(Path(__file__).with_suffix(".png").name, facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()