{"spec_id":"ecdf-basic","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\necdf-basic: Basic ECDF Plot\nLibrary: letsplot 4.10.1 | Python 3.13.14\nQuality: 88/100 | Updated: 2026-06-25\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_hline,\n    geom_text,\n    geom_vline,\n    ggplot,\n    ggsize,\n    labs,\n    layer_tooltips,\n    scale_y_continuous,\n    stat_ecdf,\n    theme,\n    theme_minimal,\n)\nfrom lets_plot.export import ggsave\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\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\"\nGRID = \"#D8D7D0\" if THEME == \"light\" else \"#3A3A36\"\nBRAND = \"#009E73\"\nAMBER = \"#DDCC77\"\n\n# Data — web service response times (ms) with bimodal distribution\nnp.random.seed(42)\nresponse_times = np.concatenate(\n    [np.random.exponential(scale=50, size=150), np.random.normal(loc=200, scale=30, size=50)]\n)\ndf = pd.DataFrame({\"response_time\": response_times})\n\n# Percentile x-values for storytelling annotations\np25_x = np.percentile(response_times, 25)\np50_x = np.percentile(response_times, 50)\np75_x = np.percentile(response_times, 75)\npct_df = pd.DataFrame(\n    {\n        \"x\": [p25_x, p50_x, p75_x],\n        \"y\": [0.25, 0.5, 0.75],\n        \"label\": [f\"P25: {p25_x:.0f} ms\", f\"P50: {p50_x:.0f} ms\", f\"P75: {p75_x:.0f} ms\"],\n    }\n)\n# Inflection annotation data (geom_text instead of annotate)\ninflection_df = pd.DataFrame({\"x\": [45], \"y\": [0.1], \"label\": [\"Bimodal inflection: ~40 ms\"]})\n\n# Title — 3-part format\ntitle = \"ecdf-basic · python · letsplot · anyplot.ai\"\n\n# Plot — ECDF with percentile reference lines, bimodal inflection marker, and text annotations\nplot = (\n    ggplot(df, aes(x=\"response_time\"))\n    # Background reference lines drawn first so ECDF renders on top\n    + geom_hline(yintercept=0.25, color=INK_SOFT, linetype=\"dashed\", size=0.5, alpha=0.5)\n    + geom_hline(yintercept=0.5, color=INK_SOFT, linetype=\"dashed\", size=0.5, alpha=0.5)\n    + geom_hline(yintercept=0.75, color=INK_SOFT, linetype=\"dashed\", size=0.5, alpha=0.5)\n    # Bimodal inflection marker — gap between exponential fast-path and compute cluster\n    + geom_vline(xintercept=40, color=AMBER, linetype=\"dotted\", size=0.7, alpha=0.7)\n    # Main ECDF step line — drawn on top of reference elements\n    + stat_ecdf(\n        geom=\"step\", color=BRAND, size=1.5, tooltips=layer_tooltips().line(\"Response Time: @response_time{,.0f} ms\")\n    )\n    # Percentile x-value labels at each reference line\n    + geom_text(data=pct_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), color=INK_SOFT, size=3.5, hjust=0, vjust=-0.5)\n    # Inflection annotation label\n    + geom_text(data=inflection_df, mapping=aes(x=\"x\", y=\"y\", label=\"label\"), color=AMBER, size=3.5, hjust=0)\n    + labs(x=\"Response Time (ms)\", y=\"Cumulative Proportion\", title=title)\n    + scale_y_continuous(limits=[0, 1], breaks=[0, 0.25, 0.5, 0.75, 1.0])\n    + ggsize(800, 450)\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_border=element_rect(color=GRID, fill=None, size=0.5),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        panel_grid_major=element_line(color=GRID, size=0.5),\n        panel_grid_minor=element_blank(),\n        axis_line=element_line(color=INK_SOFT, size=0.6),\n        axis_ticks=element_line(color=INK_SOFT, size=0.5),\n        axis_text=element_text(size=10, color=INK_SOFT, family=\"monospace\"),\n        axis_title=element_text(size=12, color=INK),\n        plot_title=element_text(size=16, color=INK),\n        plot_margin=[10, 10, 10, 10],\n    )\n)\n\n# Save\nggsave(plot, filename=f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, filename=f\"plot-{THEME}.html\", path=\".\")\n"}