When you want to list KPIs and show gaps to targets, a bullet chart is a space-efficient option. The background bands communicate achievement levels at a glance.
import matplotlib.pyplot as plt
import numpy as np
metrics = ["CVR", "Average order value", "Subscription retention", "NPS"]
actual = np.array([0.046, 12_300, 0.78, 32])
target = np.array([0.05, 12_000, 0.8, 35])
thresholds = np.array(
[
[0.02, 0.04, 0.06],
[9000, 11_000, 13_500],
[0.6, 0.75, 0.85],
[10, 25, 40],
]
)
fig, ax = plt.subplots(figsize=(6.2, 4))
for idx, name in enumerate(metrics):
base, good, excellent = thresholds[idx]
ax.barh(idx, excellent, color="#f1f5f9", height=0.8)
ax.barh(idx, good, color="#cbd5f5", height=0.8)
ax.barh(idx, base, color="#94a3b8", height=0.8)
ax.barh(idx, actual[idx], color="#38bdf8", height=0.3)
ax.plot(
[target[idx], target[idx]],
[idx - 0.4, idx + 0.4],
color="#ef4444",
linewidth=2,
)
ax.text(
actual[idx] * 1.02,
idx,
f"{actual[idx]:.2f}" if idx != 1 else f"{actual[idx]:,.0f}",
va="center",
ha="left",
fontsize=9,
)
ax.set_yticks(range(len(metrics)), labels=metrics)
ax.set_xlabel("Metric value")
ax.set_title("Key KPI progress (bullet chart)")
ax.set_xlim(0, max(thresholds[:, -1]) * 1.05)
ax.grid(axis="x", alpha=0.2)
ax.spines[["right", "top"]].set_visible(False)
fig.tight_layout()
plt.show()

Reading tips #
- The background bands represent achievement zones. If the bar reaches the darker band, the KPI exceeds the higher target.
- The red vertical line is the target. A bar that passes it means over-target; a shorter bar means under-target.
- You can fit multiple KPIs on one panel, which is ideal for weekly report summaries.