czty's picture
Add files using upload-large-folder tool
a071401 verified
Raw
History Blame Contribute Delete
12.1 kB
#!/usr/bin/env python3
from __future__ import annotations
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.ticker import FuncFormatter
# ============================================================
# Load Excel
# ============================================================
SCRIPT_DIR = Path(__file__).resolve().parent
xlsx_path = SCRIPT_DIR / "Context_Consuming.xlsx"
# BioAgentBench table
bio_df = pd.read_excel(
xlsx_path,
sheet_name=0,
usecols="A:E",
header=1,
)
bio_df.columns = [
"scale",
"task",
"prompt",
"completion",
"total",
]
bio_df["scale"] = bio_df["scale"].ffill()
bio_df["task"] = (
bio_df["task"]
.astype(str)
.str.replace("\ufeff", "", regex=False)
.str.strip()
)
scale_map = {
0: "No MCP",
100: "Biomni-100",
500: "Biomni-500",
1000: "Biomni-1k",
2000: "Biomni-2k",
}
bio_df["method"] = bio_df["scale"].map(scale_map)
# Last 10 rows are BioManus in your table
bio_last = bio_df.tail(10).copy()
bio_last["method"] = "BioManus"
bio_df = pd.concat([bio_df.iloc[:-10], bio_last], ignore_index=True)
bio_df = bio_df[bio_df["method"].notna()].copy()
# Closing Scenarios table
closing_df = pd.read_excel(
xlsx_path,
sheet_name=0,
usecols="I:L",
header=1,
)
closing_df.columns = [
"model",
"prompt",
"completion",
"total",
]
closing_df["model"] = closing_df["model"].astype(str).str.strip()
closing_name_map = {
"Biomni": "Biomni",
"Biomni with 100 tool scale": "Biomni-100",
"Biomni with 500 tool scale": "Biomni-500",
"Biomni with 1000 tool scale": "Biomni-1k",
"Biomni with 2000 tool scale": "Biomni-2k",
"Biomanus": "BioManus",
}
closing_df["method"] = closing_df["model"].map(closing_name_map)
closing_df = closing_df[closing_df["method"].notna()].copy()
# ============================================================
# Orders and labels
# ============================================================
method_order = [
"No MCP",
"Biomni-100",
"Biomni-500",
"Biomni-1k",
"Biomni-2k",
"BioManus",
]
closing_order = [
"Biomni",
"Biomni-100",
"Biomni-500",
"Biomni-1k",
"Biomni-2k",
"BioManus",
]
task_order = (
bio_df[bio_df["method"] == "No MCP"]["task"]
.drop_duplicates()
.tolist()
)
# Optional: reorder tasks by average context cost from low to high
task_score = (
bio_df.groupby("task")["prompt"]
.mean()
.sort_values()
)
task_order = [t for t in task_score.index.tolist() if t in task_order]
def wrap_task_label(task: str) -> str:
mapping = {
"alzheimer-mouse": "Alzheimer\nmouse",
"comparative-genomics": "Comparative\ngenomics",
"cystic-fibrosis": "Cystic\nfibrosis",
"deseq": "DESeq",
"evolution": "Evolution",
"giab": "GIAB",
"metagenomics": "Metagenomics",
"single-cell": "Single-cell",
"transcript-quant": "Transcript\nquant",
"viral-metagenomics": "Viral\nmetagenomics",
}
return mapping.get(task, task.replace("-", "\n"))
# ============================================================
# Style
# ============================================================
colors = {
"No MCP": "#8A8F98",
"Biomni": "#8A8F98",
"Biomni-100": "#C9853B",
"Biomni-500": "#B8742A",
"Biomni-1k": "#9C5C1A",
"Biomni-2k": "#6F3B0D",
"BioManus": "#C62828",
}
fills = {
"No MCP": "#ECEFF3",
"Biomni": "#ECEFF3",
"Biomni-100": "#F1D9BA",
"Biomni-500": "#E8C390",
"Biomni-1k": "#D8A96E",
"Biomni-2k": "#C69054",
"BioManus": "#F1C9C9",
}
plt.rcParams.update(
{
"font.family": "DejaVu Sans",
"font.size": 10.3,
"axes.titlesize": 12.3,
"axes.labelsize": 10.3,
"xtick.labelsize": 8.8,
"ytick.labelsize": 9.0,
"legend.fontsize": 8.6,
"pdf.fonttype": 42,
"ps.fonttype": 42,
}
)
# ============================================================
# Helpers
# ============================================================
def token_formatter(value: float, _pos=None):
if value >= 1_000_000:
return f"{value / 1_000_000:.1f}M"
if value >= 1_000:
return f"{value / 1_000:.0f}K"
return f"{value:.0f}"
def compact_token_formatter(value: float) -> str:
if value >= 1_000_000:
return f"{value / 1_000_000:.1f}M"
if value >= 1_000:
return f"{value / 1_000:.0f}K"
return f"{value:.0f}"
def percent_reduction(base: float, target: float) -> float:
return 100.0 * (base - target) / base
# ============================================================
# Prepare Panel A matrix
# ============================================================
pivot = bio_df.pivot_table(
index="method",
columns="task",
values="prompt",
aggfunc="mean",
)
available_methods = [m for m in method_order if m in pivot.index]
task_order = [
t for t in task_order
if t in pivot.columns and pivot.loc[available_methods, t].notna().all()
]
pivot = pivot.loc[available_methods, task_order]
log_pivot = np.log10(pivot.astype(float))
# Radar angles
n_tasks = len(task_order)
angles = np.linspace(0, 2 * np.pi, n_tasks, endpoint=False)
angles_closed = np.concatenate([angles, [angles[0]]])
# ============================================================
# Figure
# ============================================================
fig = plt.figure(figsize=(12.2, 5.65))
gs = fig.add_gridspec(
1,
2,
width_ratios=[1.25, 1.0],
wspace=0.42,
)
ax_a = fig.add_subplot(gs[0, 0], projection="polar")
ax_b = fig.add_subplot(gs[0, 1])
# ============================================================
# Panel A: Radar chart
# ============================================================
ax_a.set_theta_offset(np.pi / 2)
ax_a.set_theta_direction(-1)
r_min = 5.0
r_data_max = max(7.3, float(np.nanmax(log_pivot.values)) + 0.15)
r_max = r_data_max + 0.18
ax_a.set_ylim(r_min, r_max)
ax_a.set_xticks(angles)
ax_a.set_xticklabels([])
radial_ticks = [5, 6, 7]
radial_ticks = [t for t in radial_ticks if r_min <= t <= r_max]
ax_a.set_yticks(radial_ticks)
ax_a.set_yticklabels([rf"$10^{t}$" for t in radial_ticks], fontsize=8.4, color="#4B5563")
ax_a.set_rlabel_position(88)
ax_a.grid(True, color="#CBD5E1", linewidth=0.65, alpha=0.75)
ax_a.spines["polar"].set_color("#94A3B8")
ax_a.spines["polar"].set_linewidth(0.8)
label_radius = r_max + 0.34
for angle, task in zip(angles, task_order):
display_angle = np.pi / 2 - angle
x = np.cos(display_angle)
y_pos = np.sin(display_angle)
ha = "center"
if x > 0.22:
ha = "left"
elif x < -0.22:
ha = "right"
va = "center"
if y_pos > 0.78:
va = "bottom"
elif y_pos < -0.78:
va = "top"
ax_a.text(
angle,
label_radius,
wrap_task_label(task),
ha=ha,
va=va,
fontsize=8.2,
color="#374151",
clip_on=False,
bbox={
"boxstyle": "round,pad=0.12",
"facecolor": "white",
"edgecolor": "none",
"alpha": 0.82,
},
)
for method in available_methods:
values = log_pivot.loc[method, task_order].values.astype(float)
values_closed = np.concatenate([values, [values[0]]])
lw = 2.5 if method == "BioManus" else 1.45
alpha = 1.0 if method == "BioManus" else 0.82
ax_a.plot(
angles_closed,
values_closed,
color=colors[method],
linewidth=lw,
alpha=alpha,
marker="o",
markersize=4.8 if method == "BioManus" else 3.2,
markeredgecolor="white",
markeredgewidth=0.55,
label=method,
)
if method == "BioManus":
ax_a.fill(
angles_closed,
values_closed,
color=colors[method],
alpha=0.06,
)
raw_values = pivot.loc[method, task_order].values.astype(float)
for angle, radius, raw_value in zip(angles, values, raw_values):
display_angle = np.pi / 2 - angle
horizontal = np.cos(display_angle)
vertical = np.sin(display_angle)
text_radius = min(radius + 0.13, r_max - 0.04)
ha = "left" if horizontal > 0.18 else "right" if horizontal < -0.18 else "center"
va = "bottom" if vertical > 0.18 else "top" if vertical < -0.18 else "center"
ax_a.text(
angle,
text_radius,
compact_token_formatter(raw_value),
ha=ha,
va=va,
fontsize=6.9,
color=colors[method],
fontweight="bold",
clip_on=False,
bbox={
"boxstyle": "round,pad=0.10",
"facecolor": "white",
"edgecolor": "none",
"alpha": 0.72,
},
)
ax_a.set_title(
"",
)
ax_a.legend(
loc="upper center",
bbox_to_anchor=(0.5, -0.20),
ncol=3,
frameon=False,
handlelength=2.2,
)
# ============================================================
# Panel B: Closing Scenarios aggregate comparison
# ============================================================
closing_values = np.asarray([
closing_df[closing_df["method"] == m]["prompt"].iloc[0]
for m in closing_order
])
y = np.arange(len(closing_order))
for i, method in enumerate(closing_order):
ax_b.barh(
y[i],
closing_values[i],
height=0.48,
color=fills[method],
edgecolor=colors[method],
linewidth=1.25,
zorder=3,
)
ax_b.scatter(
closing_values[i],
y[i],
s=32,
color=colors[method],
edgecolor="white",
linewidth=0.6,
zorder=4,
)
ax_b.text(
closing_values[i] + 45_000,
y[i],
token_formatter(closing_values[i]),
va="center",
ha="left",
fontsize=8.8,
color=colors[method],
fontweight="bold" if method == "BioManus" else "normal",
)
ax_b.set_yticks(y)
ax_b.set_yticklabels(closing_order)
ax_b.invert_yaxis()
ax_b.xaxis.set_major_formatter(FuncFormatter(token_formatter))
ax_b.set_xlabel("Average prompt tokens per case")
ax_b.set_title(
"",
)
ax_b.grid(axis="x", linestyle="-", linewidth=0.6, alpha=0.22)
ax_b.spines["top"].set_visible(False)
ax_b.spines["right"].set_visible(False)
ax_b.spines["left"].set_color("#CBD5E1")
ax_b.spines["bottom"].set_color("#CBD5E1")
ax_b.set_xlim(0, max(closing_values) * 1.22)
bio = closing_df[closing_df["method"] == "BioManus"]["prompt"].iloc[0]
biomni = closing_df[closing_df["method"] == "Biomni"]["prompt"].iloc[0]
biomni2k = closing_df[closing_df["method"] == "Biomni-2k"]["prompt"].iloc[0]
ax_b.text(
0.03,
1.04,
f"BioManus reduces context\n"
f"{percent_reduction(biomni, bio):.1f}% vs. Biomni\n"
f"{percent_reduction(biomni2k, bio):.1f}% vs. Biomni-2k",
transform=ax_b.transAxes,
fontsize=8.8,
color="#374151",
va="bottom",
bbox={
"boxstyle": "round,pad=0.35",
"facecolor": "white",
"edgecolor": "#E5E7EB",
"alpha": 0.95,
},
clip_on=False,
)
fig.text(
0.028,
0.948,
"(a) BioAgentBench task-level context profile",
ha="left",
va="top",
fontsize=12.3,
fontweight="bold",
)
fig.text(
0.635,
0.948,
"(b) Closing Scenarios aggregate context",
ha="left",
va="top",
fontsize=12.3,
fontweight="bold",
)
# ============================================================
# Save
# ============================================================
fig.subplots_adjust(
left=0.075,
right=0.985,
top=0.76,
bottom=0.24,
wspace=0.42,
)
out = SCRIPT_DIR / "context_efficiency_radar_bar"
fig.savefig(f"{out}.pdf", bbox_inches="tight")
fig.savefig(f"{out}.svg", bbox_inches="tight")
fig.savefig(f"{out}.png", dpi=450, bbox_inches="tight")
print("Saved:")
print(f" {out}.pdf")
print(f" {out}.svg")
print(f" {out}.png")