{"spec_id":"bar-diverging-likert","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nbar-diverging-likert: Likert Scale Diverging Bar Chart\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-06-01\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport matplotlib.ticker as mticker\nimport numpy as np\nimport pandas as pd\nfrom matplotlib.colors import LinearSegmentedColormap\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-derived diverging palette for Likert categories\n# Midpoint is a fixed neutral hex so intermediate colors are theme-stable\nNEUTRAL_GRAY = \"#888880\"\ndiv_cmap = LinearSegmentedColormap.from_list(\"imprint_div\", [\"#AE3030\", NEUTRAL_GRAY, \"#4467A3\"])\ncat_colors = {\n    \"Strongly Disagree\": \"#AE3030\",  # Imprint semantic red\n    \"Disagree\": div_cmap(0.25),  # blended red-neutral\n    \"Neutral\": NEUTRAL_GRAY,  # fixed mid-gray (theme-stable)\n    \"Agree\": div_cmap(0.75),  # blended neutral-blue\n    \"Strongly Agree\": \"#4467A3\",  # Imprint blue (position 3)\n}\n\n# Data — hand-crafted employee engagement survey (10 questions, 5-point Likert)\nquestions = [\n    \"I feel valued at work\",\n    \"Communication is transparent\",\n    \"Leadership inspires confidence\",\n    \"Work-life balance is respected\",\n    \"Career growth opportunities exist\",\n    \"Team collaboration is effective\",\n    \"Resources are adequate\",\n    \"Feedback is constructive\",\n    \"Company culture is positive\",\n    \"Compensation is fair\",\n]\n\ndata = {\n    \"question\": questions,\n    \"Strongly Disagree\": [4, 8, 18, 6, 12, 3, 2, 7, 5, 15],\n    \"Disagree\": [10, 15, 25, 12, 20, 8, 6, 14, 11, 22],\n    \"Neutral\": [16, 20, 18, 18, 22, 14, 12, 20, 15, 20],\n    \"Agree\": [42, 35, 24, 38, 28, 45, 48, 36, 40, 28],\n    \"Strongly Agree\": [28, 22, 15, 26, 18, 30, 32, 23, 29, 15],\n}\n\ndf = pd.DataFrame(data)\n\n# Sort by net agreement (ascending so highest appears at top of chart)\nnet_scores = (df[\"Agree\"] + df[\"Strongly Agree\"]) - (df[\"Disagree\"] + df[\"Strongly Disagree\"])\ndf = df.iloc[net_scores.argsort()].reset_index(drop=True)\n\n# Diverging bar positions — neutral split evenly at center\nhalf_neutral = df[\"Neutral\"] / 2\ncat_order = [\"Strongly Disagree\", \"Disagree\", \"Neutral\", \"Agree\", \"Strongly Agree\"]\nstarts = {\n    \"Strongly Disagree\": -(df[\"Strongly Disagree\"] + df[\"Disagree\"] + half_neutral),\n    \"Disagree\": -(df[\"Disagree\"] + half_neutral),\n    \"Neutral\": -half_neutral,\n    \"Agree\": half_neutral,\n    \"Strongly Agree\": half_neutral + df[\"Agree\"],\n}\n\n# Plot\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\ny_pos = np.arange(len(df))\nbar_height = 0.7\n\nfor label in cat_order:\n    ax.barh(\n        y_pos,\n        df[label],\n        left=starts[label],\n        height=bar_height,\n        color=cat_colors[label],\n        edgecolor=PAGE_BG,\n        linewidth=0.8,\n        label=label,\n        zorder=2,\n    )\n\n# Percentage labels inside segments ≥7%\nfor i in range(len(df)):\n    for label in cat_order:\n        w = df[label].iloc[i]\n        s = starts[label].iloc[i]\n        if w >= 7:\n            cx = s + w / 2\n            text_color = INK if label == \"Neutral\" else \"white\"\n            ax.text(\n                cx, i, f\"{w:.0f}%\", ha=\"center\", va=\"center\", fontsize=7, fontweight=\"bold\", color=text_color, zorder=4\n            )\n\n# Center line\nax.axvline(x=0, color=INK_SOFT, linewidth=1.2, zorder=3)\n\n# X-axis grid\nax.xaxis.grid(True, alpha=0.15, linewidth=0.6, color=INK, zorder=0)\nax.set_axisbelow(True)\n\nax.xaxis.set_major_formatter(mticker.FuncFormatter(lambda x, _: f\"{x:+.0f}%\" if x != 0 else \"0%\"))\nax.xaxis.set_major_locator(mticker.MultipleLocator(20))\n\n# Style\ntitle = \"bar-diverging-likert · python · matplotlib · anyplot.ai\"\ntitle_fontsize = max(8, round(12 * 67 / len(title))) if len(title) > 67 else 12\nax.set_yticks(y_pos)\nax.set_yticklabels(df[\"question\"], fontsize=9, color=INK_SOFT)\nax.set_xlabel(\"Percentage of Responses\", fontsize=10, color=INK)\nax.set_title(title, fontsize=title_fontsize, fontweight=\"medium\", color=INK, pad=12)\nax.tick_params(axis=\"x\", labelsize=8, colors=INK_SOFT)\nax.tick_params(axis=\"y\", length=0)\n\n# Remove all spines — center line provides the zero reference\nfor spine in ax.spines.values():\n    spine.set_visible(False)\n\n# Net agreement column on right\nfor i in range(len(df)):\n    net_val = (df[\"Agree\"].iloc[i] + df[\"Strongly Agree\"].iloc[i]) - (\n        df[\"Disagree\"].iloc[i] + df[\"Strongly Disagree\"].iloc[i]\n    )\n    sign = \"+\" if net_val > 0 else \"\"\n    net_color = \"#4467A3\" if net_val > 0 else \"#AE3030\"\n    ax.annotate(\n        f\"{sign}{net_val}\",\n        xy=(1.02, i),\n        xycoords=(\"axes fraction\", \"data\"),\n        fontsize=7,\n        fontweight=\"bold\",\n        color=net_color,\n        va=\"center\",\n        ha=\"left\",\n        annotation_clip=False,\n    )\n\nax.annotate(\n    \"Net\",\n    xy=(1.02, len(df) - 0.5),\n    xycoords=(\"axes fraction\", \"data\"),\n    fontsize=7,\n    fontweight=\"bold\",\n    color=INK_MUTED,\n    va=\"center\",\n    ha=\"left\",\n    annotation_clip=False,\n)\n\n# Legend below chart\nhandles, legend_labels = ax.get_legend_handles_labels()\nleg = ax.legend(\n    handles,\n    legend_labels,\n    loc=\"upper center\",\n    bbox_to_anchor=(0.5, -0.08),\n    ncol=5,\n    fontsize=8,\n    frameon=False,\n    handlelength=1.5,\n    columnspacing=1.5,\n)\nplt.setp(leg.get_texts(), color=INK_SOFT)\n\nfig.subplots_adjust(left=0.30, right=0.90, top=0.92, bottom=0.16)\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}