Buckets:
| #!/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.