{"spec_id":"lightcurve-transit","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nlightcurve-transit: Astronomical Light Curve\nLibrary: seaborn 0.13.2 | Python 3.13.14\nQuality: 87/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sns\n\n\n# Theme tokens — Imprint palette, theme-adaptive chrome\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\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\"\n\n# Imprint palette — canonical order, position 1 always first series\nDATA_COLOR = \"#009E73\"  # position 1, brand green — observed flux\nMODEL_COLOR = \"#C475FD\"  # position 2, lavender — transit model\n\nsns.set_theme(\n    style=\"ticks\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Data — wider transit (0.032), 400 pts, u1=0.5/u2=0.3 limb darkening for visual independence\nnp.random.seed(42)\n\nn_points = 400\nphase = np.sort(np.random.uniform(0.0, 1.0, n_points))\n\ntransit_center = 0.5\ntransit_width = 0.032\ntransit_depth = 0.012\nu1, u2 = 0.5, 0.3\n\nz = np.abs(phase - transit_center) / transit_width\ndip = np.where(z < 1.0, np.sqrt(np.clip(1.0 - z**2, 0, None)), 0.0)\nlimb = 1.0 - u1 * (1 - dip) - u2 * (1 - dip) ** 2\nmodel_flux = 1.0 - transit_depth * dip * limb\n\nflux_err = np.random.uniform(0.0008, 0.0015, n_points)\nflux = model_flux + np.random.normal(0, 1, n_points) * flux_err\nresiduals = flux - model_flux\n\nphase_model = np.linspace(0.0, 1.0, 2000)\nz_model = np.abs(phase_model - transit_center) / transit_width\ndip_model = np.where(z_model < 1.0, np.sqrt(np.clip(1.0 - z_model**2, 0, None)), 0.0)\nlimb_model = 1.0 - u1 * (1 - dip_model) - u2 * (1 - dip_model) ** 2\nmodel_smooth = 1.0 - transit_depth * dip_model * limb_model\n\n# Phase binning — 40 bins × ~10 pts each for seaborn statistical aggregation\nn_bins = 40\nphase_edges = np.linspace(0.0, 1.0, n_bins + 1)\nphase_centers = 0.5 * (phase_edges[:-1] + phase_edges[1:])\nbin_idx = np.clip(np.digitize(phase, phase_edges) - 1, 0, n_bins - 1)\n\ndf = pd.DataFrame(\n    {\"phase\": phase, \"phase_bin\": phase_centers[bin_idx], \"flux\": flux, \"flux_err\": flux_err, \"residuals\": residuals}\n)\ndf_model = pd.DataFrame({\"phase\": phase_model, \"flux\": model_smooth})\n\n# Plot — landscape canvas: figsize=(8, 4.5) × dpi=400 → 3200×1800 px\nfig, (ax_main, ax_resid) = plt.subplots(\n    2, 1, figsize=(8, 4.5), dpi=400, height_ratios=[3, 1], sharex=True, gridspec_kw={\"hspace\": 0.05}, facecolor=PAGE_BG\n)\n\n# Main panel: error bars (visible precision layer behind scatter)\nax_main.errorbar(\n    df[\"phase\"],\n    df[\"flux\"],\n    yerr=df[\"flux_err\"],\n    fmt=\"none\",\n    ecolor=DATA_COLOR,\n    elinewidth=0.7,\n    alpha=0.42,\n    capsize=0,\n    zorder=1,\n)\n\n# Main panel: individual scatter (faint texture showing raw data density)\nsns.scatterplot(\n    data=df,\n    x=\"phase\",\n    y=\"flux\",\n    color=DATA_COLOR,\n    s=12,\n    alpha=0.22,\n    edgecolor=\"none\",\n    ax=ax_main,\n    zorder=2,\n    legend=False,\n)\n\n# Main panel: phase-binned mean with 95% CI band — seaborn statistical aggregation\nsns.lineplot(\n    data=df,\n    x=\"phase_bin\",\n    y=\"flux\",\n    color=DATA_COLOR,\n    estimator=\"mean\",\n    errorbar=(\"ci\", 95),\n    linewidth=1.5,\n    ax=ax_main,\n    zorder=3,\n    label=\"Observed (binned ± 95% CI)\",\n)\n\n# Main panel: transit model curve\nsns.lineplot(\n    data=df_model, x=\"phase\", y=\"flux\", color=MODEL_COLOR, linewidth=2.5, ax=ax_main, zorder=4, label=\"Transit model\"\n)\n\n# Residuals panel: scatter points\nsns.scatterplot(\n    data=df, x=\"phase\", y=\"residuals\", color=DATA_COLOR, s=20, alpha=0.40, edgecolor=\"none\", ax=ax_resid, legend=False\n)\n\n# Residuals panel: phase-binned mean ± SD band — shows absence of systematic residual structure\nsns.lineplot(\n    data=df,\n    x=\"phase_bin\",\n    y=\"residuals\",\n    color=DATA_COLOR,\n    estimator=\"mean\",\n    errorbar=\"sd\",\n    linewidth=0.8,\n    alpha=0.70,\n    ax=ax_resid,\n    legend=False,\n)\nax_resid.axhline(0, color=MODEL_COLOR, linewidth=1.5, linestyle=\"--\", alpha=0.7, zorder=3)\n\n# Transit depth annotation — highlights key scientific measurement\ntransit_min = model_smooth.min()\nax_main.annotate(\n    f\"Transit depth {transit_depth * 100:.1f}%\",\n    xy=(transit_center, transit_min),\n    xytext=(transit_center + 0.14, transit_min - 0.0012),\n    fontsize=8,\n    color=MODEL_COLOR,\n    fontweight=\"medium\",\n    arrowprops={\"arrowstyle\": \"->\", \"color\": MODEL_COLOR, \"lw\": 1.2},\n    ha=\"left\",\n    va=\"top\",\n    zorder=5,\n)\n\n# Style — main panel\nax_main.set_facecolor(PAGE_BG)\nax_main.set_ylabel(\"Relative Flux\", fontsize=10, color=INK)\nax_main.set_title(\n    \"lightcurve-transit · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\", color=INK, pad=8\n)\nax_main.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\nax_main.tick_params(axis=\"x\", labelbottom=False)\nax_main.yaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK)\nax_main.legend(fontsize=8, frameon=True, loc=\"lower left\")\n\n# Style — residuals panel\nax_resid.set_facecolor(PAGE_BG)\nax_resid.set_xlabel(\"Orbital Phase\", fontsize=10, color=INK)\nax_resid.set_ylabel(\"Residuals\", fontsize=10, color=INK)\nax_resid.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\nax_resid.yaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK)\nax_resid.set_xlim(0.0, 1.0)\n\n# Despine both panels\nsns.despine(ax=ax_main)\nsns.despine(ax=ax_resid)\n\nfig.subplots_adjust(left=0.09, right=0.97, top=0.92, bottom=0.12)\n\n# Save — no bbox_inches='tight' (seaborn canvas contract: figsize × dpi = exact pixel target)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}