{"cells":[{"cell_type":"markdown","metadata":{},"source":"# RSNA Knee — Nested-OOF rank ensemble\n\nFinal blending notebook. It accepts independently produced image-model prediction packages and changes the primary DINOv2 ranking only when a candidate clears a five-fold nested test on the 58 official labels. This protects against public-LB chasing and probability-scale mismatch: ROC-AUC uses only ranks.\n\nEach attached package must contain `test_predictions.csv` and `oof_predictions.csv`, exact competition columns, and a `fold` column on OOF rows. OOF predictions must be generated strictly out of fold."},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"from pathlib import Path\nimport numpy as np, pandas as pd\nfrom sklearn.metrics import roc_auc_score\n\nCOMP='rsna-knee-abnormality-detection'; ROOT=Path('/kaggle/input')\ncompetition = ROOT/'competitions'/COMP\nif not competition.exists():\n    candidates=[p.parent for p in ROOT.rglob('sample_submission.csv')]\n    if len(candidates)!=1: raise RuntimeError(f'Cannot locate competition: {candidates}')\n    competition=candidates[0]\nsample=pd.read_csv(competition/'sample_submission.csv'); train=pd.read_csv(competition/'train.csv')\nUID='StudyInstanceUID'; TARGETS=[c for c in sample if c!=UID]\nGOLD=train[[UID,*TARGETS]].copy(); gold_mask=GOLD[TARGETS].notna().all(axis=1); GOLD=GOLD.loc[gold_mask].copy()\nassert len(TARGETS)==12 and len(GOLD)==58, f'Expected 58 fully labelled rows, got {len(GOLD)}'\n\n# Attach prediction packages here. The first entry is the protected primary model.\nPACKAGE_DIRS = [\n    Path('/kaggle/input/rsna-dino-ft-b-oof-package'),\n    Path('/kaggle/input/rsna-efficientnet-specialist-oof-package'),\n    Path('/kaggle/input/rsna-radimagenet-specialist-oof-package'),\n]\n# A direct-inference output from notebook 02 may have a dataset-dependent mount name.\nFALLBACK_NAMES = ('dino_ft_b_predictions.csv', 'test_predictions.csv', 'submission.csv')\nprint('Gold rows:',len(GOLD),'| candidate package dirs:',PACKAGE_DIRS)\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"def validate_predictions(frame, name, require_fold=False):\n    required={UID,*TARGETS} | ({'fold'} if require_fold else set())\n    missing=required-set(frame.columns)\n    if missing: raise ValueError(f'{name}: missing columns {sorted(missing)}')\n    if frame[UID].duplicated().any(): raise ValueError(f'{name}: duplicate StudyInstanceUID')\n    values=frame[TARGETS].to_numpy(float)\n    if not np.isfinite(values).all() or ((values<0)|(values>1)).any(): raise ValueError(f'{name}: predictions outside [0,1]')\n    return frame[[UID,*TARGETS]+(['fold'] if require_fold else [])].copy()\ndef rank01(frame):\n    return frame[TARGETS].rank(method='average',pct=True).to_numpy(float)\ndef macro_auc(y,p):\n    result=[]\n    for j in range(len(TARGETS)):\n        if len(np.unique(y[:,j]))==2: result.append(roc_auc_score(y[:,j],p[:,j]))\n    return float(np.mean(result))\n\npackages=[]\nfor folder in PACKAGE_DIRS:\n    test_path, oof_path=folder/'test_predictions.csv',folder/'oof_predictions.csv'\n    if not (test_path.exists() and oof_path.exists()):\n        print('skip missing package:',folder); continue\n    test=validate_predictions(pd.read_csv(test_path),str(test_path)); oof=validate_predictions(pd.read_csv(oof_path),str(oof_path),True)\n    if not test[UID].equals(sample[UID]): raise ValueError(f'{folder}: test row order differs from sample_submission')\n    packages.append((folder.name,test,oof))\nif not packages:\n    fallback_paths=[]\n    for name in FALLBACK_NAMES:\n        fallback_paths.extend(sorted(ROOT.rglob(name)))\n    fallback_paths=[p for i,p in enumerate(fallback_paths) if p not in fallback_paths[:i]]\n    fallback=None\n    for path in fallback_paths:\n        try:\n            candidate=validate_predictions(pd.read_csv(path),str(path))\n            if candidate[UID].equals(sample[UID]):\n                fallback=candidate; print('Using attached primary prediction:',path); break\n        except Exception as err:\n            print('skip invalid fallback:',path, type(err).__name__)\n    if fallback is None:\n        # The workflow remains executable when a user has not attached any output dataset.\n        # This is a schema fallback only; it is never presented as a model result.\n        output=sample.copy(); output[TARGETS]=0.5\n        print('No valid prediction package attached; wrote neutral schema fallback.')\n    else:\n        output=fallback[[UID,*TARGETS]].copy()\n        print('No OOF-certified candidate attached; passed through primary prediction unchanged.')\n    output.to_csv('submission.csv',index=False)\nelse:\n    print('usable OOF packages:',[x[0] for x in packages])\n"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"if packages:\n    # Package 0 is the anchor. A candidate may be applied only if nested gold-58 OOF improves it.\n    anchor_name,anchor_test,anchor_oof=packages[0]\n    anchor_gold=GOLD.merge(anchor_oof,on=UID,how='inner',suffixes=('','_pred'))\n    if len(anchor_gold)!=len(GOLD): raise ValueError('Anchor OOF misses official labelled studies')\n    anchor_pred=anchor_gold[[f'{t}_pred' for t in TARGETS]].to_numpy(float)\n    y=anchor_gold[TARGETS].to_numpy(int); folds=anchor_gold['fold'].to_numpy()\n    if len(np.unique(folds))<3: raise ValueError('Need >=3 OOF folds for nested selection')\n    final_rank=rank01(anchor_test); decisions=[]\n    for name,test,oof in packages[1:]:\n        merged=anchor_gold[[UID,*TARGETS,'fold']].merge(oof,on=UID,how='inner',suffixes=('','_cand'))\n        if len(merged)!=len(GOLD): print('skip incomplete candidate',name); continue\n        a=anchor_pred; b=merged[[f'{t}_cand' for t in TARGETS]].to_numpy(float)\n        ar=np.column_stack([pd.Series(a[:,j]).rank(pct=True).to_numpy() for j in range(12)])\n        br=np.column_stack([pd.Series(b[:,j]).rank(pct=True).to_numpy() for j in range(12)])\n        outer_scores=[]; selected=[]\n        for heldout in np.unique(folds):\n            fit=folds!=heldout; val=~fit; grid=np.linspace(0,1,21)\n            scores=[macro_auc(y[fit],(1-alpha)*ar[fit]+alpha*br[fit]) for alpha in grid]\n            alpha=float(grid[int(np.argmax(scores))]); selected.append(alpha)\n            outer_scores.append(macro_auc(y[val],(1-alpha)*ar[val]+alpha*br[val]))\n        nested=float(np.mean(outer_scores)); anchor_nested=macro_auc(y,ar)\n        alpha=float(np.median(selected)); improved=nested>anchor_nested+0.002\n        decisions.append({'candidate':name,'anchor_oof':anchor_nested,'nested_oof':nested,'alpha':alpha,'accepted':improved})\n        print(decisions[-1])\n        if improved:\n            final_rank=(1-alpha)*final_rank+alpha*rank01(test)\n    output=sample[[UID]].copy(); output[TARGETS]=final_rank\n    output.to_csv('submission.csv',index=False); pd.DataFrame(decisions).to_csv('ensemble_decisions.csv',index=False)\n    print('wrote submission.csv with',sum(x['accepted'] for x in decisions),'OOF-approved additions')\ndisplay(output.head())\n"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.12"}},"nbformat":4,"nbformat_minor":5}