{"spec_id":"dumbbell-basic","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\ndumbbell-basic: Basic Dumbbell Chart\nLibrary: plotnine 0.15.7 | Python 3.13.14\nQuality: 89/100 | Updated: 2026-06-30\n\"\"\"\n\nimport os\nimport sys\n\n\n# Prevent script directory from shadowing the plotnine package\n_here = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != _here]\n\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_point,\n    geom_segment,\n    geom_text,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    theme,\n    theme_minimal,\n)\n\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\"\n\n# Imprint palette — hybrid-v3 sort order\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\", \"#99B314\"]\n\n# Data: Employee satisfaction scores before and after workplace policy changes\ncategories_raw = [\"Engineering\", \"Marketing\", \"Sales\", \"HR\", \"Finance\", \"Operations\", \"Customer Support\", \"Product\"]\nbefore_scores_raw = [65, 58, 72, 45, 68, 52, 40, 75]\nafter_scores_raw = [82, 71, 78, 73, 75, 68, 62, 88]\n\ndifferences_raw = [a - b for a, b in zip(after_scores_raw, before_scores_raw, strict=True)]\nsorted_data = sorted(\n    zip(categories_raw, before_scores_raw, after_scores_raw, differences_raw, strict=True), key=lambda x: x[3]\n)\ncategories = [d[0] for d in sorted_data]\nbefore_scores = [d[1] for d in sorted_data]\nafter_scores = [d[2] for d in sorted_data]\ndifferences = [d[3] for d in sorted_data]\n\ndf_segments = pd.DataFrame(\n    {\n        \"category\": categories,\n        \"start\": before_scores,\n        \"end\": after_scores,\n        \"label_x\": [(s + e) / 2 for s, e in zip(before_scores, after_scores, strict=True)],\n        \"label\": [f\"+{d}\" for d in differences],\n    }\n)\n\ndf_points = pd.DataFrame(\n    {\n        \"category\": categories * 2,\n        \"value\": before_scores + after_scores,\n        \"period\": [\"Before\"] * len(categories) + [\"After\"] * len(categories),\n    }\n)\n\ndf_segments[\"category\"] = pd.Categorical(df_segments[\"category\"], categories=categories, ordered=True)\ndf_points[\"category\"] = pd.Categorical(df_points[\"category\"], categories=categories, ordered=True)\ndf_points[\"period\"] = pd.Categorical(df_points[\"period\"], categories=[\"Before\", \"After\"], ordered=True)\n\nplot = (\n    ggplot()\n    + geom_segment(\n        aes(x=\"start\", xend=\"end\", y=\"category\", yend=\"category\"), data=df_segments, color=INK_SOFT, size=0.8, alpha=0.5\n    )\n    + geom_point(aes(x=\"value\", y=\"category\", color=\"period\"), data=df_points, size=4.0)\n    + geom_text(aes(x=\"label_x\", y=\"category\", label=\"label\"), data=df_segments, color=INK_MUTED, size=3.0, nudge_y=0.3)\n    + scale_color_manual(values={\"Before\": IMPRINT[0], \"After\": IMPRINT[1]})\n    + scale_x_continuous(limits=(30, 100), breaks=[30, 40, 50, 60, 70, 80, 90, 100])\n    + labs(\n        x=\"Satisfaction Score\",\n        y=\"Department\",\n        title=\"Employee Satisfaction · dumbbell-basic · plotnine · anyplot.ai\",\n        color=\"\",\n    )\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, color=PAGE_BG),\n        panel_border=element_blank(),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n        plot_title=element_text(size=12, color=INK, weight=\"bold\"),\n        axis_title=element_text(size=10, color=INK),\n        axis_text=element_text(size=8, color=INK_SOFT),\n        legend_text=element_text(size=8, color=INK_SOFT),\n        legend_title=element_text(color=INK),\n        legend_background=element_rect(fill=ELEVATED_BG, color=ELEVATED_BG),\n        legend_position=\"right\",\n        panel_grid_major_x=element_line(color=INK, size=0.3, alpha=0.10),\n        panel_grid_minor=element_blank(),\n        panel_grid_major_y=element_blank(),\n    )\n)\n\nplot.save(f\"plot-{THEME}.png\", dpi=400, width=8, height=4.5, units=\"in\", verbose=False)\n"}