Files
ai-bubble-research/src/charts/agent_adoption_chart.py
2026-06-04 19:05:16 -05:00

185 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Agent Adoption Survey Comparison Chart
Grouped horizontal bar chart comparing key enterprise AI adoption metrics
across three major 2025 surveys: LangChain, McKinsey, and PwC.
"""
import matplotlib
matplotlib.use("Agg")
# Patch matplotlib Path.__deepcopy__ to break Python 3.14 recursion loop
try:
from matplotlib.path import Path
_original_path_deepcopy = Path.__deepcopy__
def _safe_path_deepcopy(self, memo):
if id(self) in memo:
return memo[id(self)]
memo[id(self)] = self
return self
Path.__deepcopy__ = _safe_path_deepcopy
except Exception:
pass
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import matplotlib.patches as mpatches
from src.data.agent_adoption import agent_survey_data
from src.utils.styling import (
get_theme, EXPORT_DPI, AGENT_GROWTH, GRAY_DARK, BLACK, WHITE, GRAY_LIGHT,
)
def _shade(base_hex: str, factor: float) -> str:
"""Lighten or darken a hex color by a given factor (01)."""
r, g, b = mcolors.to_rgb(base_hex)
# Blend toward white to lighten
r2 = r + (1.0 - r) * factor
g2 = g + (1.0 - g) * factor
b2 = b + (1.0 - b) * factor
return mcolors.to_hex((r2, g2, b2))
def plot_agent_adoption() -> str:
"""Generate grouped horizontal bar chart of survey comparisons."""
plt.rcParams.update(get_theme())
fig, ax = plt.subplots(figsize=(14, 8))
# ------------------------------------------------------------------
# Data
# ------------------------------------------------------------------
lc = agent_survey_data["langchain_2025"]
mc = agent_survey_data["mckinsey_2025"]
pc = agent_survey_data["pwc_2025"]
# Each row is a comparable category; values are [LangChain, McKinsey, PwC]
# Where a survey has no direct comparable metric, we use None.
categories = [
"Production\nDeployment",
"Overall\nAI Adoption",
"Budget\nIncrease",
"Scaling\nAgentic AI",
"Productivity\nValue",
]
# Values mapped to closest comparable metrics
values = [
# Production / Deployment
[lc["production"], None, pc["ai_agents_already_adopted"]],
# Overall AI Adoption / Maturity
[lc["observability_implemented"], mc["overall_ai_adoption"], None],
# Budget / Investment Intent
[lc["multi_model_deployments"], None, pc["plan_increase_ai_budgets"]],
# Scaling / Experimentation
[None, mc["agentic_ai_scaling"], None],
# Measurable Value / Productivity
[None, None, pc["measurable_productivity_value"]],
]
# Survey identifiers
surveys = [
"LangChain\n(n=1,340)",
"McKinsey\n(n=1,993)",
"PwC\n(n=308)",
]
# Colors: base AGENT_GROWTH with increasing lightness
colors = [
AGENT_GROWTH, # LangChain — full purple
_shade(AGENT_GROWTH, 0.25), # McKinsey — lighter
_shade(AGENT_GROWTH, 0.50), # PwC — lightest
]
# ------------------------------------------------------------------
# Plotting
# ------------------------------------------------------------------
n_cats = len(categories)
bar_height = 0.22
x_positions = [0, 1, 2] # offset within each group
y_positions = []
for i in range(n_cats):
base_y = i * 3 # three bars per category
y_positions.append([base_y + off for off in [0.0, 0.22, 0.44]])
# Plot bars
for row_idx, (cat, row_vals) in enumerate(zip(categories, values)):
for col_idx, val in enumerate(row_vals):
if val is None:
continue
y = y_positions[row_idx][col_idx]
ax.barh(y, val, height=bar_height,
color=colors[col_idx],
edgecolor=WHITE, linewidth=0.8,
label=surveys[col_idx] if row_idx == 0 else None)
# Value label on bar
ax.text(val + 1.0, y, f"{val:.1f}%",
va="center", fontsize=9, color=GRAY_DARK,
fontweight="bold")
# Y-axis: category labels centered on each group
group_centers = [i * 3 + 0.22 for i in range(n_cats)]
ax.set_yticks(group_centers)
ax.set_yticklabels(categories, fontsize=11, fontweight="bold")
# Inset legend-like labels inside each group
legend_y_offset = 0.55
for col_idx in range(3):
ax.text(-0.5, group_centers[0] + legend_y_offset - col_idx * 0.22,
surveys[col_idx], fontsize=8, color=colors[col_idx],
ha="left", va="center", fontweight="bold")
# Axis config
ax.set_xlim(0, 105)
ax.set_xlabel("Percentage (%)", fontsize=11, color=GRAY_DARK)
ax.set_xticks(range(0, 106, 10))
ax.tick_params(axis="x", labelsize=9)
# Grid
ax.xaxis.grid(True, alpha=0.3, color=GRAY_LIGHT)
ax.yaxis.grid(False)
# Spine cleanup
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_color("#cccccc")
ax.spines["bottom"].set_color("#cccccc")
# Title
ax.set_title(
"Enterprise Agent Adoption — Survey Comparison",
fontsize=18, fontweight="bold", pad=16, color=BLACK,
)
ax.text(
0.5, -0.18,
"LangChain (n=1,340) | McKinsey (n=1,993) | PwC (n=308)",
transform=ax.transAxes,
fontsize=11, color=GRAY_DARK, ha="center",
)
# Legend
handles = []
for col_idx in range(3):
handles.append(
mpatches.Rectangle((0, 0), 1, 1, color=colors[col_idx], alpha=1)
)
ax.legend(handles, surveys, loc="lower right", fontsize=9,
frameon=True, edgecolor="#cccccc")
# Adjust layout
fig.subplots_adjust(left=0.28, right=0.95, top=0.85, bottom=0.10)
# Save
out_path = "output/charts/10_agent_adoption.png"
fig.savefig(out_path, dpi=EXPORT_DPI,
facecolor=fig.get_facecolor(), edgecolor="none")
plt.close(fig)
return out_path
def main():
path = plot_agent_adoption()
print(f"Chart saved: {path}")
if __name__ == "__main__":
main()