codex/recombination-fraction-map-distance.py

recombination-fraction-map-distance.py
"""Original Haldane model curve; save an opaque white basename PNG to cwd.

Tested with Python 3.14.4, Matplotlib 3.10.7 (+dfsg1), NumPy 2.3.5.
d is the mean Poisson exchange count on the transmitted chromatid, in morgans.
Run from _media: MPLCONFIGDIR=/tmp/codex-wiki-matplotlib python ../recombination-fraction-map-distance.py
"""

from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np


def main():
    d = np.linspace(0, 1.5, 601)
    r = -np.expm1(-2 * d) / 2
    fig, ax = plt.subplots(figsize=(8.6, 5.6), facecolor="white")
    ax.set_facecolor("white")
    ax.plot(d, d, "--", color="#c06923", lw=2, label="Short-interval approximation: r = d")
    ax.plot(d, r, color="#246c94", lw=2.6, label="Haldane: r = (1 − exp(−2d)) / 2")
    ax.axhline(0.5, color="#555555", ls=":", lw=1.8, label="Model ceiling: r = 0.5")
    d_inverse = -np.log1p(-0.4) / 2
    r_at_point = -np.expm1(-0.4) / 2
    ax.scatter((0.2, d_inverse), (r_at_point, 0.2), color="#246c94", s=35, zorder=5)
    ax.annotate("d = 0.2 M → r = 0.164840", xy=(0.2, r_at_point), xytext=(0.47, 0.085),
                fontsize=11, arrowprops={"arrowstyle": "->", "color": "#246c94"})
    ax.annotate("r = 0.2 → d = 0.255413 M\n                         = 25.5413 cM",
                xy=(d_inverse, 0.2), xytext=(0.68, 0.27), fontsize=11,
                arrowprops={"arrowstyle": "->", "color": "#246c94"})
    ax.text(0.81, 0.69, "The straight line is useful near zero;\nit is not a long-interval probability model.",
            fontsize=11, color="#804414")
    ax.set_xlim(0, 1.5)
    ax.set_ylim(0, 1.56)
    ax.set_xlabel("Map distance d (morgans; 1 M = 100 cM)", fontsize=12)
    ax.set_ylabel("Recombination fraction r", fontsize=12)
    ax.set_title("Map distance counts exchanges; recombination fraction scores parity", fontsize=14, pad=14)
    ax.legend(loc="upper left", fontsize=10, frameon=True, facecolor="white", framealpha=1)
    ax.grid(color="#e2e2e2", lw=0.7)
    ax.spines[["top", "right"]].set_visible(False)
    fig.text(0.5, 0.035, "Toy model: Poisson(d) exchanges along the transmitted chromatid; independent disjoint counts.",
             ha="center", fontsize=10, color="#444444")
    fig.subplots_adjust(left=0.095, right=0.97, bottom=0.16, top=0.87)
    fig.savefig(Path(__file__).with_suffix(".png").name, dpi=120, facecolor="white", transparent=False)
    plt.close(fig)


if __name__ == "__main__":
    main()