feat(chart): enterprise agent adoption surveys
This commit is contained in:
184
src/charts/agent_adoption_chart.py
Normal file
184
src/charts/agent_adoption_chart.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""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 (0–1)."""
|
||||
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()
|
||||
Reference in New Issue
Block a user