{"spec_id":"count-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ncount-basic: Basic Count Plot\nLibrary: plotly 6.9.0 | Python 3.13.14\nQuality: 94/100 | Updated: 2026-08-11\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\n\n\n# Theme tokens (see prompts/default-style-guide.md \"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\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\nBRAND = \"#009E73\"  # Imprint palette position 1 — ALWAYS first series\nNEUTRAL = INK  # Imprint semantic anchor — cumulative line, theme-adaptive\nAMBER = \"#DDCC77\"  # Imprint semantic anchor — 80% threshold reference\n\n# Data - Product category purchases with heavily skewed distribution\nnp.random.seed(42)\ncategories = [\"Electronics\", \"Clothing\", \"Home & Garden\", \"Sports\", \"Books\", \"Toys\", \"Beauty\"]\n# Generate raw purchase data with heavily right-skewed distribution\n# Electronics dominates, others taper off\nprobabilities = [0.40, 0.25, 0.15, 0.10, 0.05, 0.03, 0.02]\nraw_data = np.random.choice(categories, size=250, p=probabilities)\n\n# Count occurrences\nunique, counts = np.unique(raw_data, return_counts=True)\n# Sort by frequency (descending)\nsort_idx = np.argsort(counts)[::-1]\nsorted_categories = unique[sort_idx]\nsorted_counts = counts[sort_idx]\n\n# Per-category share and running (Pareto) cumulative share\ntotal = sorted_counts.sum()\npercentages = sorted_counts / total * 100\ncumulative_pct = np.cumsum(sorted_counts) / total * 100\n\n# Category index where the cumulative share first reaches/exceeds 80% (\"vital few\")\nvital_few_idx = int(np.searchsorted(cumulative_pct, 80.0))\n\nbar_hover = [\n    f\"{cat}<br>Count: {count}<br>Share: {pct:.1f}%\"\n    for cat, count, pct in zip(sorted_categories, sorted_counts, percentages, strict=True)\n]\nline_hover = [f\"{cat}<br>Cumulative: {pct:.1f}%\" for cat, pct in zip(sorted_categories, cumulative_pct, strict=True)]\n\n# Title fontsize scales linearly with title length off the 67-char baseline,\n# both up (short titles) and down (long titles), clamped to a legible range\ntitle_text = \"count-basic · python · plotly · anyplot.ai\"\ntitle_fontsize = max(11, min(24, round(16 * 67 / len(title_text))))\n\nfig = make_subplots(specs=[[{\"secondary_y\": True}]])\n\nfig.add_trace(\n    go.Bar(\n        name=\"Count\",\n        x=sorted_categories,\n        y=sorted_counts,\n        marker=dict(color=BRAND, opacity=0.9, line=dict(color=INK_SOFT, width=1.5)),\n        text=sorted_counts,\n        textposition=\"outside\",\n        textfont=dict(size=13, color=INK),\n        hovertext=bar_hover,\n        hoverinfo=\"text\",\n    ),\n    secondary_y=False,\n)\n\nfig.add_trace(\n    go.Scatter(\n        name=\"Cumulative %\",\n        x=sorted_categories,\n        y=cumulative_pct,\n        mode=\"lines+markers\",\n        line=dict(color=NEUTRAL, width=2.5, dash=\"dot\"),\n        marker=dict(size=9, color=NEUTRAL, line=dict(width=1.5, color=PAGE_BG)),\n        hovertext=line_hover,\n        hoverinfo=\"text\",\n    ),\n    secondary_y=True,\n)\n\n# 80% Pareto threshold — reference line + \"vital few\" callout on the secondary axis\nfig.add_hline(y=80, line=dict(color=AMBER, width=1.5, dash=\"dash\"), secondary_y=True)\nfig.add_annotation(\n    x=sorted_categories[vital_few_idx],\n    y=80,\n    yref=\"y2\",\n    text=f\"80% reached at {sorted_categories[vital_few_idx]}\",\n    showarrow=True,\n    arrowhead=2,\n    arrowcolor=AMBER,\n    ax=40,\n    ay=-32,\n    font=dict(size=11, color=INK),\n    bgcolor=ELEVATED_BG,\n    bordercolor=AMBER,\n    borderwidth=1,\n    borderpad=4,\n)\n\n# Layout — hard target 3200 x 1800 (see \"Canvas — hard rule\" in prompts/library/plotly.md)\nfig.update_layout(\n    autosize=False,\n    title=dict(text=title_text, font=dict(size=title_fontsize, color=INK), x=0.5, xanchor=\"center\"),\n    xaxis=dict(\n        title=dict(text=\"Product Category\", font=dict(size=13, color=INK)),\n        tickfont=dict(size=11, color=INK_SOFT),\n        showline=True,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    bargap=0.3,\n    margin=dict(l=90, r=90, t=100, b=90),\n    legend=dict(\n        x=0.99,\n        y=0.12,\n        xanchor=\"right\",\n        yanchor=\"bottom\",\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n        font=dict(size=11, color=INK_SOFT),\n    ),\n)\n\nfig.update_yaxes(\n    title=dict(text=\"Count (n)\", font=dict(size=13, color=INK)),\n    tickfont=dict(size=11, color=INK_SOFT),\n    gridcolor=GRID,\n    gridwidth=1,\n    showline=True,\n    linecolor=INK_SOFT,\n    zerolinecolor=INK_SOFT,\n    rangemode=\"tozero\",\n    secondary_y=False,\n)\nfig.update_yaxes(\n    title=dict(text=\"Cumulative Share (%)\", font=dict(size=13, color=INK)),\n    tickfont=dict(size=11, color=INK_SOFT),\n    range=[0, 105],\n    ticksuffix=\"%\",\n    showgrid=False,\n    showline=True,\n    linecolor=INK_SOFT,\n    zeroline=False,\n    secondary_y=True,\n)\n\n# Save as PNG — hard target 3200 x 1800 (landscape)\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\n\n# Save as HTML for interactivity\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}