"""G11: descriptive group error audit. No automatic fairness or deployment verdict."""importmathdefbinary_report(y,probabilities,threshold=.5):ifnotyorlen(y)!=len(probabilities)ornot0<=threshold<=1:raiseValueError("nonempty matching vectors and threshold in [0,1] required")ifany(type(t)isnotintortnotin(0,1)fortiny):raiseValueError("strict binary labels required")ifany(notmath.isfinite(p)ornot0<=p<=1forpinprobabilities):raiseValueError("probabilities must be finite in [0,1]")pred=[int(p>=threshold)forpinprobabilities]tp=sum(t==1andv==1fort,vinzip(y,pred))fp=sum(t==0andv==1fort,vinzip(y,pred))fn=sum(t==1andv==0fort,vinzip(y,pred))tn=len(y)-tp-fp-fnratio=lambdaa,b:a/bifbelseNonereturn{"n":len(y),"tp":tp,"fp":fp,"fn":fn,"tn":tn,"precision":ratio(tp,tp+fp),"recall":ratio(tp,tp+fn),"fpr":ratio(fp,fp+tn),"selection":(tp+fp)/len(y),"accuracy":(tp+tn)/len(y),"brier":math.fsum((p-t)**2forp,tinzip(probabilities,y))/len(y)}defaudit(y,probabilities,groups,threshold=.5):whole=binary_report(y,probabilities,threshold)iflen(groups)!=len(y)orany(type(g)isnotstrornotgforgingroups):raiseValueError("one nonempty group label per observation required")reports={}forginsorted(set(groups)):idx=[ifori,vinenumerate(groups)ifv==g]reports[g]=binary_report([y[i]foriinidx],[probabilities[i]foriinidx],threshold)return{"overall":whole,"groups":reports}if__name__=="__main__":print(audit([1,0,1,0],[.9,.6,.4,.2],["A","A","B","B"]))