{"spec_id":"count-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\ncount-basic: Basic Count Plot\nLibrary: plotnine 0.15.7 | Python 3.13.14\nQuality: 94/100 | Updated: 2026-08-11\n\"\"\"\n\nimport os\n\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    after_stat,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_bar,\n    geom_text,\n    ggplot,\n    labs,\n    scale_fill_manual,\n    scale_y_continuous,\n    theme,\n    theme_minimal,\n)\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Imprint palette — brand green marks the modal category, muted grey covers the rest\nBRAND = \"#009E73\"\n\n# Data - Netflix user ratings from a streaming survey\nratings = [\"5 Stars\"] * 285 + [\"4 Stars\"] * 198 + [\"3 Stars\"] * 142 + [\"2 Stars\"] * 89 + [\"1 Star\"] * 56\nrating_order = [\"5 Stars\", \"4 Stars\", \"3 Stars\", \"2 Stars\", \"1 Star\"]\ndf = pd.DataFrame({\"Rating\": pd.Categorical(ratings, categories=rating_order, ordered=True)})\n\n# Highlight the modal category so the count plot makes a point, not just a tally\nmode_rating = df[\"Rating\"].value_counts().idxmax()\ndf[\"Highlight\"] = df[\"Rating\"].apply(lambda r: \"Mode\" if r == mode_rating else \"Other\")\n\n# Plot - native stat='count' drives the bars and the count labels; the percentage\n# labels read plotnine's own `prop` stat variable, forced to share-of-total (its\n# default is share-of-fill-group) via the aes(group=1) override — a\n# grammar-of-graphics idiom that keeps the annotation fully data-driven.\nplot = (\n    ggplot(df, aes(x=\"Rating\", fill=\"Highlight\"))\n    + geom_bar(width=0.62, color=PAGE_BG, size=0.6, show_legend=False)\n    + geom_text(\n        aes(label=after_stat(\"count\")),\n        stat=\"count\",\n        color=INK,\n        size=8,\n        va=\"bottom\",\n        nudge_y=10,\n        fontweight=\"bold\",\n        format_string=\"{:.0f}\",\n    )\n    + geom_text(\n        aes(label=after_stat(\"prop\"), group=1),\n        stat=\"count\",\n        color=INK_MUTED,\n        size=6,\n        va=\"bottom\",\n        nudge_y=27,\n        format_string=\"{:.0%}\",\n    )\n    + scale_fill_manual(values={\"Mode\": BRAND, \"Other\": INK_MUTED})\n    + scale_y_continuous(breaks=[0, 100, 200, 300], expand=(0, 0, 0.16, 0))\n    + labs(x=\"Rating\", y=\"Number of Responses\", title=\"count-basic · python · plotnine · anyplot.ai\")\n    + theme_minimal()\n    + theme(\n        figure_size=(8, 4.5),\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_border=element_blank(),\n        panel_grid_major_x=element_blank(),\n        panel_grid_major_y=element_line(color=INK, size=0.3, alpha=0.15),\n        panel_grid_minor=element_blank(),\n        axis_line=element_line(color=INK_SOFT, size=0.4),\n        axis_title=element_text(size=10, color=INK),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        plot_title=element_text(size=12, color=INK, weight=\"bold\"),\n        text=element_text(size=7),\n    )\n)\n\n# Save\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\")\n"}