{"spec_id":"bar-pareto","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nbar-pareto: Pareto Chart with Cumulative Line\nLibrary: letsplot 4.10.1 | Python 3.13.14\nQuality: 91/100 | Updated: 2026-06-20\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\nfrom lets_plot import ggsave\n\n\nLetsPlot.setup_html()\n\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nGRID_COLOR = \"#E0DFD8\" if THEME == \"light\" else \"#2A2A27\"\n\n# Imprint palette — canonical order\nIMPRINT_PALETTE = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\nBRAND = IMPRINT_PALETTE[0]  # brand green — bars contributing to 80%\nCUMLINE_COLOR = IMPRINT_PALETTE[1]  # lavender — cumulative percentage line\nTHRESHOLD_COLOR = \"#DDCC77\"  # amber — warning/caution semantic anchor\n\n# Data — manufacturing defect types sorted by frequency (descending)\ncategories = [\"Scratches\", \"Dents\", \"Misalignment\", \"Cracks\", \"Discoloration\", \"Burrs\", \"Warping\", \"Contamination\"]\ncounts = [186, 145, 98, 72, 54, 38, 27, 16]\n\ndf = pd.DataFrame({\"category\": categories, \"count\": counts})\n\n# Cumulative percentage\ntotal = sum(counts)\ncumulative_pct = np.cumsum(counts) / total * 100\n\n# Scale cumulative percentages to share the primary y-axis\nmax_count = max(counts)\ny_max = total\nscale_factor = y_max / 100\n\ncumulative_scaled = cumulative_pct * scale_factor\nthreshold_80_scaled = 80 * scale_factor\n\n# Semantic bar colors: brand green for ≤80% threshold, muted for the tail\nbar_colors = [BRAND if cumulative_pct[i] <= 80 else INK_MUTED for i in range(len(categories))]\ndf[\"bar_color\"] = bar_colors\n\n# Segments to draw cumulative line across discrete x-axis\nseg_df = pd.DataFrame(\n    {\n        \"x\": categories[:-1],\n        \"xend\": categories[1:],\n        \"y\": cumulative_scaled[:-1].tolist(),\n        \"yend\": cumulative_scaled[1:].tolist(),\n    }\n)\n\n# Points for cumulative line markers\ndf_points = pd.DataFrame(\n    {\n        \"category\": categories,\n        \"cumulative_scaled\": cumulative_scaled.tolist(),\n        \"cumulative_pct\": [f\"{p:.0f}%\" for p in cumulative_pct],\n    }\n)\n\n# Simulated secondary y-axis tick labels (right of last bar)\nsec_ticks = [20, 40, 60, 80, 100]\nsec_labels_df = pd.DataFrame(\n    {\n        \"category\": [categories[-1]] * len(sec_ticks),\n        \"y\": [t * scale_factor for t in sec_ticks],\n        \"label\": [f\"{t}%\" for t in sec_ticks],\n    }\n)\n\n# Title with dynamic font size (16px baseline for ~67-char title)\ntitle = \"bar-pareto · python · letsplot · anyplot.ai\"\nn = len(title)\ntitle_size = round(16 * 67 / n) if n > 67 else 16\ntitle_size = max(title_size, 11)\n\n# Plot\nplot = (\n    ggplot(df, aes(x=\"category\", y=\"count\"))\n    + geom_bar(\n        aes(fill=\"bar_color\"),\n        stat=\"identity\",\n        width=0.72,\n        tooltips=layer_tooltips()\n        .title(\"@category\")\n        .line(\"Count|@count\")\n        .format(\"count\", \"d\"),\n        show_legend=False,\n    )\n    + scale_fill_identity()\n    # Cumulative percentage line (segments span discrete axis positions)\n    + geom_segment(\n        data=seg_df,\n        mapping=aes(x=\"x\", y=\"y\", xend=\"xend\", yend=\"yend\"),\n        color=CUMLINE_COLOR,\n        size=2.0,\n        inherit_aes=False,\n    )\n    # Cumulative line markers\n    + geom_point(\n        data=df_points,\n        mapping=aes(x=\"category\", y=\"cumulative_scaled\"),\n        color=CUMLINE_COLOR,\n        fill=PAGE_BG,\n        size=5,\n        shape=21,\n        stroke=2.0,\n        inherit_aes=False,\n        tooltips=layer_tooltips().line(\"Cumulative|@cumulative_pct\"),\n    )\n    # 80% threshold reference line (amber warning anchor)\n    + geom_hline(yintercept=threshold_80_scaled, color=THRESHOLD_COLOR, size=1.0, linetype=\"dashed\")\n    + geom_text(\n        data=pd.DataFrame({\"category\": [categories[0]], \"y\": [threshold_80_scaled], \"label\": [\"80%\"]}),\n        mapping=aes(x=\"category\", y=\"y\", label=\"label\"),\n        color=THRESHOLD_COLOR,\n        size=9,\n        hjust=0.0,\n        vjust=-0.7,\n        fontface=\"bold\",\n        inherit_aes=False,\n    )\n    # Simulated secondary y-axis labels (right of last category)\n    + geom_text(\n        data=sec_labels_df[sec_labels_df[\"label\"] != \"100%\"],\n        mapping=aes(x=\"category\", y=\"y\", label=\"label\"),\n        color=CUMLINE_COLOR,\n        size=6,\n        hjust=-1.6,\n        fontface=\"bold\",\n        inherit_aes=False,\n    )\n    + geom_text(\n        data=sec_labels_df[sec_labels_df[\"label\"] == \"100%\"],\n        mapping=aes(x=\"category\", y=\"y\", label=\"label\"),\n        color=CUMLINE_COLOR,\n        size=6,\n        hjust=-1.6,\n        vjust=1.8,\n        fontface=\"bold\",\n        inherit_aes=False,\n    )\n    # Cumulative percentage annotations on first 3 points\n    + geom_text(\n        data=df_points.iloc[:3],\n        mapping=aes(x=\"category\", y=\"cumulative_scaled\", label=\"cumulative_pct\"),\n        color=CUMLINE_COLOR,\n        size=9,\n        vjust=-1.5,\n        fontface=\"bold\",\n        inherit_aes=False,\n    )\n    + scale_x_discrete(limits=categories)\n    + scale_y_continuous(limits=[0, y_max], expand=[0, 0, 0.05, 0])\n    + labs(\n        x=\"Defect Type\", y=\"Frequency (Count)\", title=title, caption=\"Line: cumulative %  ·  Dashed: 80% threshold\"\n    )\n    + theme_minimal()\n    + theme(\n        axis_text_x=element_text(angle=45, hjust=1, size=16, color=INK_SOFT),\n        axis_text_y=element_text(size=16, color=INK_SOFT),\n        axis_title=element_text(size=20, color=INK),\n        plot_title=element_text(size=title_size, hjust=0.5, face=\"bold\", color=INK),\n        plot_caption=element_text(size=14, color=INK_SOFT, hjust=0.5),\n        panel_grid_major_x=element_blank(),\n        panel_grid_minor=element_blank(),\n        panel_grid_major_y=element_line(color=GRID_COLOR, size=0.3),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT),\n        plot_margin=[20, 90, 10, 10],\n    )\n    + ggsize(800, 450)\n)\n\n# Save — scale=4 yields 3200×1800 px from the 800×450 base\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=4)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}