codex/covariance-and-dependence.py

covariance-and-dependence.py
"""Plot exact finite distributions, contrasting covariance and dependence."""

from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np


def covariance(x, y):
    return np.mean((x - np.mean(x)) * (y - np.mean(y)))


def main():
    grid = np.array([-1.0, 0.0, 1.0])
    x = np.repeat(grid, 3)
    z = np.tile(grid, 3)
    y = 2 * x + z
    assert np.isclose(np.var(x), 2 / 3)
    assert np.isclose(covariance(x, y), 4 / 3)
    assert np.isclose(np.var(y), 10 / 3)
    assert np.isclose(np.var(x + y), 20 / 3)
    assert np.isclose(np.var(x - y), 4 / 3)
    assert np.isclose(np.corrcoef(x, y)[0, 1], 2 / np.sqrt(5))
    assert np.isclose(covariance(grid, grid**2), 0)
    assert np.isclose(np.var(grid**2), 2 / 9)
    assert np.isclose(np.corrcoef([1, 2, 3, 4], [2, 1, 4, 3])[0, 1], 0.6)
    plt.rcParams.update({"font.size": 12, "axes.titlesize": 14})
    fig, axes = plt.subplots(2, 2, figsize=(9.6, 8.3), dpi=120, facecolor="white")
    cases = [(x, y, r"$Y=2X+Z$", "Positive linear association", "#206a9d", (-3.5, 3.5)),
             (x, -2 * x + z, r"$Y=-2X+Z$", "Negative linear association", "#be7521", (-3.5, 3.5)),
             (x, z, r"$Y=Z$", "Independent variables", "#2f8a68", (-1.5, 1.5)),
             (grid, grid**2, r"$Y=X^2$", "Dependent, but uncorrelated", "#8255a2", (-0.2, 1.5))]
    for ax, (xs, ys, formula, title, color, ylim) in zip(axes.flat, cases):
        cov = covariance(xs, ys)
        corr = np.corrcoef(xs, ys)[0, 1]
        ax.axvline(np.mean(xs), color="#b4bcc2", lw=1)
        ax.axhline(np.mean(ys), color="#b4bcc2", lw=1)
        if len(xs) == 3:
            curve = np.linspace(-1.12, 1.12, 300)
            ax.plot(curve, curve**2, color=color, lw=1.5, alpha=0.45)
        ax.scatter(xs, ys, s=85, color=color, edgecolor="white", linewidth=1, zorder=4)
        ax.set(xlim=(-1.4, 1.4), ylim=ylim, xlabel="X", ylabel="Y",
               title=title + "\n" + formula, xticks=[-1, 0, 1])
        ax.text(0.5, -0.29, f"Cov(X, Y) = {cov:.3f};  correlation = {corr:.3f}",
                transform=ax.transAxes, fontsize=11, ha="center")
        ax.grid(color="#e7eaed")
        ax.spines[["top", "right"]].set_visible(False)
    fig.suptitle("Exact distributions: shared variation, independence, and a nonlinear rule", fontsize=15)
    fig.subplots_adjust(left=0.09, right=0.98, top=0.865, bottom=0.12, wspace=0.28, hspace=0.9)
    fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name,
                facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()