{"spec_id":"pp-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\npp-basic: Probability-Probability (P-P) Plot\nLibrary: plotly 6.8.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-06-16\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom scipy import stats\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\n\n# Theme-adaptive chrome\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint palette\nBRAND = \"#009E73\"  # brand green — within-range points (first categorical series)\nDEVIATION = \"#AE3030\"  # matte red — semantic anchor for deviation / poor fit\nNEUTRAL = INK  # totals / baseline / reference line (theme-adaptive)\nMUTED = INK_MUTED  # confidence-band fill (theme-adaptive)\n\n# Data: mixture of normal + exponential simulating process drift\nnp.random.seed(42)\nobserved = np.random.normal(loc=50, scale=10, size=200) + np.random.exponential(scale=2, size=200)\n\nobserved_sorted = np.sort(observed)\nn = len(observed_sorted)\n\n# Fit normal distribution to observed data\nmu, sigma = stats.norm.fit(observed_sorted)\n\n# Empirical CDF using plotting position formula i/(n+1)\nempirical_cdf = np.arange(1, n + 1) / (n + 1)\n\n# Theoretical CDF from fitted normal\ntheoretical_cdf = stats.norm.cdf(observed_sorted, loc=mu, scale=sigma)\n\n# Deviation threshold for storytelling — confine flagged points to the genuine\n# right tail (theoretical CDF > 0.8), where the added exponential mass makes the\n# data depart from the fitted normal. This keeps the red diamonds in the tail\n# region the annotation describes, rather than the distribution body.\ndeviation = np.abs(empirical_cdf - theoretical_cdf)\nin_right_tail = theoretical_cdf > 0.8\nthreshold = np.percentile(deviation, 75)\nis_deviant = in_right_tail & (deviation > threshold)\n\n# 95% confidence band (Kolmogorov-Smirnov)\nks_band = 1.36 / np.sqrt(n)\ndiag = np.linspace(0, 1, 200)\n\n# Plot\nfig = go.Figure()\n\n# Confidence band\nfig.add_trace(\n    go.Scatter(\n        x=np.concatenate([diag, diag[::-1]]),\n        y=np.concatenate([diag + ks_band, (diag - ks_band)[::-1]]),\n        fill=\"toself\",\n        fillcolor=\"rgba(107,106,99,0.12)\" if THEME == \"light\" else \"rgba(168,167,159,0.12)\",\n        line={\"width\": 0},\n        name=\"95% KS confidence band\",\n        hoverinfo=\"skip\",\n    )\n)\n\n# Reference line (perfect fit = structural baseline → neutral)\nfig.add_trace(\n    go.Scatter(\n        x=[0, 1],\n        y=[0, 1],\n        mode=\"lines\",\n        line={\"color\": NEUTRAL, \"width\": 2.5, \"dash\": \"dash\"},\n        name=\"Perfect normal fit\",\n        hoverinfo=\"skip\",\n    )\n)\n\n# Data points: within expected range\nfig.add_trace(\n    go.Scatter(\n        x=theoretical_cdf[~is_deviant],\n        y=empirical_cdf[~is_deviant],\n        mode=\"markers\",\n        marker={\"size\": 6.5, \"color\": BRAND, \"opacity\": 0.6, \"line\": {\"width\": 0.6, \"color\": PAGE_BG}},\n        name=\"Within expected range\",\n        customdata=np.column_stack([observed_sorted[~is_deviant], deviation[~is_deviant]]),\n        hovertemplate=(\n            \"Theoretical: %{x:.3f}<br>\"\n            \"Empirical: %{y:.3f}<br>\"\n            \"Value: %{customdata[0]:.1f}<br>\"\n            \"Deviation: %{customdata[1]:.3f}\"\n            \"<extra></extra>\"\n        ),\n    )\n)\n\n# Data points: tail deviations\nfig.add_trace(\n    go.Scatter(\n        x=theoretical_cdf[is_deviant],\n        y=empirical_cdf[is_deviant],\n        mode=\"markers\",\n        marker={\n            \"size\": 11,\n            \"color\": DEVIATION,\n            \"symbol\": \"diamond\",\n            \"opacity\": 0.9,\n            \"line\": {\"width\": 0.8, \"color\": PAGE_BG},\n        },\n        name=\"Tail deviation\",\n        customdata=np.column_stack([observed_sorted[is_deviant], deviation[is_deviant]]),\n        hovertemplate=(\n            \"Theoretical: %{x:.3f}<br>\"\n            \"Empirical: %{y:.3f}<br>\"\n            \"Value: %{customdata[0]:.1f}<br>\"\n            \"Deviation: %{customdata[1]:.3f}\"\n            \"<extra></extra>\"\n        ),\n    )\n)\n\n# Annotation pointing to right-tail deviation\ntail_idx = np.where(is_deviant & (theoretical_cdf > 0.8))[0]\nif len(tail_idx) > 0:\n    ax_pt = theoretical_cdf[tail_idx[len(tail_idx) // 2]]\n    ay_pt = empirical_cdf[tail_idx[len(tail_idx) // 2]]\n    fig.add_annotation(\n        x=ax_pt,\n        y=ay_pt,\n        ax=-55,\n        ay=-45,\n        text=\"Heavier right tail<br>→ process drift detected\",\n        showarrow=True,\n        arrowhead=2,\n        arrowsize=1.2,\n        arrowwidth=1.6,\n        arrowcolor=DEVIATION,\n        font={\"size\": 11, \"color\": INK_SOFT},\n        bgcolor=ELEVATED_BG,\n        bordercolor=DEVIATION,\n        borderwidth=1.2,\n        borderpad=5,\n    )\n\n# Layout\nfig.update_layout(\n    autosize=False,\n    width=600,\n    height=600,\n    margin={\"l\": 70, \"r\": 40, \"t\": 80, \"b\": 60},\n    title={\n        \"text\": \"pp-basic · python · plotly · anyplot.ai\",\n        \"font\": {\"size\": 16, \"color\": INK},\n        \"x\": 0.5,\n        \"xanchor\": \"center\",\n        \"y\": 0.97,\n    },\n    xaxis={\n        \"title\": {\"text\": \"Theoretical CDF (Normal)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [-0.02, 1.02],\n        \"showgrid\": False,\n        \"showline\": True,\n        \"linecolor\": INK_SOFT,\n        \"linewidth\": 1.2,\n        \"zeroline\": False,\n        \"ticks\": \"outside\",\n        \"tickcolor\": INK_SOFT,\n        \"dtick\": 0.2,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Empirical CDF\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [-0.02, 1.02],\n        \"showgrid\": False,\n        \"showline\": True,\n        \"linecolor\": INK_SOFT,\n        \"linewidth\": 1.2,\n        \"zeroline\": False,\n        \"ticks\": \"outside\",\n        \"tickcolor\": INK_SOFT,\n        \"dtick\": 0.2,\n        \"scaleanchor\": \"x\",\n        \"scaleratio\": 1,\n    },\n    legend={\n        \"x\": 0.02,\n        \"y\": 0.98,\n        \"xanchor\": \"left\",\n        \"yanchor\": \"top\",\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"borderwidth\": 1,\n    },\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n)\n\n# Save — square canvas: 600 × 600 × scale 4 = 2400 × 2400\nfig.write_image(f\"plot-{THEME}.png\", width=600, height=600, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}