"""Compute exact reported metrics from preserved, genuine prototype predictions.""" from pathlib import Path from collections import Counter import hashlib,json ROOT=Path(__file__).resolve().parent load=lambda name:json.loads((ROOT/name).read_text()) manifest=load('fixture_manifest.json') assert all(hashlib.sha256((ROOT/f).read_bytes()).hexdigest()==s for f,s in manifest['files'].items()), 'Frozen fixture changed' fixtures=load('heldout_classification.json')['issues'] dups=load('heldout_duplicates.json')['issues'] raw=load('raw_predictions.json') labels=['bug','feature','documentation','question','unsure','rejected'] items={item['id']:item for item in raw['classification']['items']} keywords=load('baseline.json')['keywords'] def baseline(issue): text=(issue['title']+' '+issue['body']).lower() scores=sorted([(sum(w in text for w in words),label) for label,words in keywords.items()],reverse=True) return scores[0][1] if scores[0][0]>0 and scores[0][0]>scores[1][0] else 'unsure' def metrics(predictions): confusion={truth:{pred:0 for pred in labels} for truth in labels} for issue,pred in zip(fixtures,predictions):confusion[issue['expected_label']][pred]+=1 n=len(fixtures);correct=sum(confusion[l][l] for l in labels) answered=[i for i,p in enumerate(predictions) if p not in ['unsure','rejected']] per={} for label in labels[:-1]: tp=confusion[label][label];fp=sum(confusion[x][label] for x in labels if x!=label);fn=sum(confusion[label][x] for x in labels if x!=label) precision=tp/(tp+fp) if tp+fp else 0;recall=tp/(tp+fn) if tp+fn else 0 per[label]={'support':sum(confusion[label].values()),'precision':precision,'recall':recall,'f1':2*precision*recall/(precision+recall) if precision+recall else 0} return {'count':n,'correct':correct,'accuracy':correct/n,'confusionMatrix':confusion,'perLabel':per,'macroF1':sum(v['f1'] for v in per.values())/5,'abstentionCount':predictions.count('unsure'),'abstentionRate':predictions.count('unsure')/n,'rejectedCount':predictions.count('rejected'),'coverage':len(answered)/n,'accuracyAmongAnswered':sum(predictions[i]==fixtures[i]['expected_label'] for i in answered)/len(answered) if answered else None} pred=[('unsure' if items[i['number']]['needsReview'] else items[i['number']]['suggestedLabel']) if i['number'] in items else 'rejected' for i in fixtures] rawPred=[items[i['number']]['suggestedLabel'] if i['number'] in items else 'rejected' for i in fixtures] basePred=[baseline(i) for i in fixtures] classFailures=[{'fixtureId':i['id'],'title':i['title'],'expected':i['expected_label'],'observed':p,'rawSuggestedLabel':items.get(i['number'],{}).get('suggestedLabel'),'similarity':items.get(i['number'],{}).get('similarity'),'margin':items.get(i['number'],{}).get('margin')} for i,p in zip(fixtures,pred) if i['expected_label']!=p] byId={i['number']:i for i in dups} truePairs={tuple(sorted((i['number'],j['number']))) for i in dups for j in dups if i['number']