You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
# After creating the heatmap and annotations as before:fori, seginenumerate(pivot.index):
forj, intvinenumerate(pivot.columns):
ifdf[(df['Segment']==seg) & (df['Intervention']==intv)]['Significant'].values[0]:
# Position marker slightly to the right of the textax.scatter(j+0.9, i+0.5, s=90, facecolors='white',
edgecolors='black', linewidths=0.8)
# Add a custom legend for significancefrommatplotlib.linesimportLine2Dlegend_elements= [Line2D([0], [0], marker='o', color='w', label='Significant (95% CI)',
markerfacecolor='white', markeredgecolor='black', markersize=10)]
ax.legend(handles=legend_elements, loc='upper right', frameon=True)
# Python 3.x# Dependencies: pandas, numpy, seaborn, matplotlibimportmatplotlib.pyplotaspltimportseabornassnsimportnumpyasnpimportpandasaspdfrommatplotlib.linesimportLine2D# -------------------------------# 1) Simulated CATE inputs# (Δ weekly Copilot actions per user)# -------------------------------segments= [
"Managers",
"Individual Contributors",
"New Hires (≤6 mo)",
"Tenured (>2 yrs)",
"Customer-facing",
"Internal Platform",
"EMEA",
"AMER",
"APAC",
]
interventions= ["Leadership Digest", "Peer Nudges", "Training Email"]
# Fabricated CATE matrix (rows = segments, cols = interventions)CATE= np.array([
[0.060, 0.050, 0.020], # Managers
[0.020, 0.030, 0.010], # ICs
[0.050, 0.070, 0.030], # New Hires
[0.020, 0.020, 0.000], # Tenured
[0.050, 0.060, 0.020], # Customer-facing
[0.010, 0.015, 0.000], # Internal Platform
[0.030, 0.040, 0.015], # EMEA
[0.040, 0.050, 0.020], # AMER
[0.025, 0.035, 0.010], # APAC
])
# Significance mask (True if 95% CI excludes 0) — simulatedsig= np.array([
[ True, True, False],
[False, True, False],
[ True, True, True],
[False, False, False],
[ True, True, False],
[False, False, False],
[False, True, False],
[ True, True, False],
[False, True, False],
])
# -------------------------------# 2) Build plotting DataFrame# -------------------------------rows= []
fori, segin enumerate(segments):forj, intvin enumerate(interventions):
rows.append({
"Segment":seg,
"Intervention":intv,
"CATE":CATE[i, j],
"Significant":sig[i, j],
})
df= pd.DataFrame(rows)
# Matrix for heatmap, and annotation stringspivot= df.pivot(index="Segment", columns="Intervention", values="CATE")
annot= np.array([[f"{v:+.3f}"forvinrow] forrowinpivot.values])
# -------------------------------# 3) Plot — 16×9; marker at right edge# -------------------------------
plt.figure(figsize=(16, 9))
ax= sns.heatmap(
pivot,
annot=annot, fmt="",
cmap=sns.diverging_palette(220, 20, as_cmap=True),
center=0,
linewidths=0.5, linecolor="white",
cbar_kws={"label":"CATE (Δ weekly Copilot actions per user)"},
)
# Move significance markers to the RIGHT side of each cellfori, segin enumerate(pivot.index):forj, intvin enumerate(pivot.columns):is_sig=df[(df["Segment"] ==seg) & (df["Intervention"] ==intv)]["Significant"].values[0]
ifis_sig:# x: j + 0.90 => right side; y: i + 0.50 => vertical center
ax.scatter(j+0.90, i+0.50,
s=90, facecolors="white", edgecolors="black", linewidths=0.8)
# Legend for significancelegend_elements= [Line2D([0], [0], marker="o", color="w",
label="Significant (95% CI)",
markerfacecolor="white", markeredgecolor="black",
markersize=10)]
ax.legend(handles=legend_elements, loc="upper right", frameon=True)
# Titles & labels
plt.suptitle("Where do interventions work best? (CATE heatmap)", fontsize=20, y=0.95)
plt.title("Estimated heterogeneous treatment effects by segment and intervention\n""Circles mark effects whose 95% CI excludes zero (simulated data)", fontsize=12)
ax.set_xlabel("Intervention")
ax.set_ylabel("Segment")
# Layout & save
plt.tight_layout(rect=[0, 0, 1, 0.93])
plt.savefig("cate_heatmap_16x9_side_marker.png", dpi=200)
plt.show()
# -------------------------------# Notes for real data:# - Replace CATE with your estimates.# - If you have standard errors (se), significance can be set via:# sig = (np.abs(CATE) - 1.96 * se) > 0# - You can push the marker slightly outside with j + 1.02 if you prefer.# -------------------------------