AsianPlayer/TriDrive / questionnaire /scripts /make_user_study_bars.py
AsianPlayer's picture
download
raw
4.37 kB
#!/usr/bin/env python3
"""User-study result chart for the teaser: appropriateness and timeliness (1-7 Likert, higher = better) for CockpitWM vs stock openpilot,
mean +/- participant-level bootstrap 95% CI, paired difference and Wilcoxon p from results/endpoints.csv; plus the stated preference split.
Palette-only, Times New Roman. Usage: python make_user_study_bars.py --out figures/user_study_bars"""
import argparse, csv, numpy as np, matplotlib, glob
matplotlib.use("Agg"); import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
for f in glob.glob("/usr/share/fonts/truetype/msttcorefonts/[Tt]imes*.ttf"):
try: fm.fontManager.addfont(f)
except Exception: pass
INK="#2B2B2B"; GREY45="#8C8C8C"; LAV,LAVL,GREYL,PEACHL,PEACH,CORAL,BG="#DDD6E5","#DFEDF2","#BFDCE7","#F5D8BF","#D9E4C2","#CAC2D7","#F9EDE0"
R="results"
ap=argparse.ArgumentParser(); ap.add_argument("--out",required=True); a=ap.parse_args()
rows=list(csv.DictReader(open(f"{R}/per_participant.csv"))); ep={r["analysis"]+"|"+r["endpoint"]:r for r in csv.DictReader(open(f"{R}/endpoints.csv"))}; pref={r["choice"]:r for r in csv.DictReader(open(f"{R}/preference.csv"))}
rng=np.random.default_rng(20260922)
def stats(col):
C=np.array([float(r[col+"_C"]) for r in rows if r[col+"_C"] and r[col+"_S"]]); S=np.array([float(r[col+"_S"]) for r in rows if r[col+"_C"] and r[col+"_S"]])
def ci(x): b=[rng.choice(x,len(x)).mean() for _ in range(2000)]; return np.percentile(b,[2.5,97.5])
return C.mean(),ci(C),S.mean(),ci(S),len(C)
plt.rcParams.update({"font.family":"Times New Roman","font.size":11,"text.color":INK,"axes.labelcolor":INK,"xtick.color":INK,"ytick.color":INK,"axes.edgecolor":INK})
def draw(bare):
"""bare = the teaser version: two grouped bars + preference, minimal text; non-bare keeps the statistics"""
fig=plt.figure(figsize=(6.4,3.6)); fig.patch.set_alpha(0)
ax=fig.add_axes([0.11,0.20,0.56,0.72]); items=(("EP0_appropriateness","Appropriateness"),("ES1_timeliness","Timeliness"))
for i,(col,lab) in enumerate(items):
mC,cC,mS,cS,n=stats(col); e=ep["primary_complete_pairs|"+col]
ax.bar(i-0.19,mC,0.36,color=LAV,ec=INK,lw=0.8,label="TriDrive" if i==0 else None); ax.bar(i+0.19,mS,0.36,color=GREYL,ec=INK,lw=0.8,label="openpilot" if i==0 else None)
ax.errorbar([i-0.19,i+0.19],[mC,mS],yerr=[[mC-cC[0],mS-cS[0]],[cC[1]-mC,cS[1]-mS]],fmt="none",ecolor=INK,elinewidth=1,capsize=3)
ax.text(i-0.19,cC[1]+0.15,f"{mC:.1f}",ha="center",va="bottom",fontsize=12,color=INK); ax.text(i+0.19,cS[1]+0.15,f"{mS:.1f}",ha="center",va="bottom",fontsize=12,color=INK)
if not bare:
d=float(e["mean_diff"]); lo,hi=float(e["ci95_lo"]),float(e["ci95_hi"]); pv=float(e["wilcoxon_p"])
ax.text(i,7.55,f"Δ = +{d:.2f} [{lo:.2f}, {hi:.2f}]\nn = {n}, p = {pv:.3f}",ha="center",va="bottom",fontsize=8.6,color=GREY45)
ax.set_xticks([0,1]); ax.set_xticklabels([l for _,l in items],fontsize=13); ax.set_ylim(0,8.2 if bare else 9.0); ax.set_yticks(range(1,8)); ax.tick_params(axis="y",labelsize=11); ax.set_ylabel("rating (1–7)",fontsize=12)
ax.spines[["top","right"]].set_visible(False); ax.legend(frameon=False,fontsize=12,loc="upper center",bbox_to_anchor=(0.5,-0.14),ncol=2,handlelength=1.4,columnspacing=1.6)
ax2=fig.add_axes([0.74,0.20,0.22,0.72]); k=[int(pref["prefer C"]["k"]),int(pref["no difference"]["k"]),int(pref["prefer S"]["k"])]; n=int(pref["prefer C"]["n"])
cols=(LAV,BG,GREYL); labs=("TriDrive","tie","openpilot") if bare else ("prefer TriDrive","no difference","prefer openpilot"); y0=0
for kk,c,l in zip(k,cols,labs):
ax2.bar(0,kk,0.7,bottom=y0,color=c,ec=INK,lw=0.8); ax2.text(0,y0+kk/2,f"{kk}\n{l}" if bare else f"{l}\n{kk} / {n}",ha="center",va="center",fontsize=11 if bare else 8.8,color=INK); y0+=kk
ax2.set_xlim(-0.6,0.6); ax2.set_ylim(0,n); ax2.axis("off"); ax2.set_title("preferred",fontsize=12,color=INK) if bare else ax2.set_title("stated preference",fontsize=10.5,color=INK)
if not bare: ax.set_title("14 participants, both systems in their own car",fontsize=10.5,color=INK,loc="left")
return fig
for bare in (False,True):
fig=draw(bare); n=a.out+("_bare" if bare else ""); fig.savefig(n+".png",dpi=250,bbox_inches="tight",transparent=True); fig.savefig(n+".pdf",bbox_inches="tight",transparent=True); plt.close(fig); print("wrote",n)

Xet Storage Details

Size:
4.37 kB
·
Xet hash:
16f2c5361dd4ab04ead3ecce5cec03373f1171583dc470eebf2daa70649ef57d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.