{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"},{"sourceId":14452635,"sourceType":"datasetVersion","datasetId":9231252}],"dockerImageVersionId":31239,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CAFA-6: GOA + ProtT5 Ensemble (0.370)\n**Approach:**\n- GOA (Gene Ontology Annotation) database\n- ProtT5 + InterPro predictions\n- GOA+ propagation\n**Score:** 0.370\n## Required Dataset\nAdd dataset: `ymuroya47/cafa6-goa-predictions`\nContains:\n- `goa_submission.tsv` - GOA database predictions\n- `prott5_interpro_predictions.tsv` - ProtT5 + InterPro predictions","metadata":{"_uuid":"c5acfe1c-dcaa-440b-9593-0fb2f514de14","_cell_guid":"91f18208-b3d4-4908-b167-c4c0df4a2bd0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"import os\nimport numpy as np\nfrom collections import defaultdict\nfrom tqdm.auto import tqdm\n\n# Paths\nCOMPETITION_DATA = '/kaggle/input/cafa-6-protein-function-prediction'\nPREDICTION_DATA = '/kaggle/input/cafa6-goa-predictions'\n\n# Check paths\nprint(\"Competition data:\", os.listdir(COMPETITION_DATA))\nprint(\"Prediction data:\", os.listdir(PREDICTION_DATA))","metadata":{"_uuid":"daaa6b46-626e-43b3-8536-f749607ad4d8","_cell_guid":"723505c4-408d-495e-867f-09e34ee85b43","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-01-10T09:49:18.683899Z","iopub.execute_input":"2026-01-10T09:49:18.684177Z","iopub.status.idle":"2026-01-10T09:49:18.906469Z","shell.execute_reply.started":"2026-01-10T09:49:18.684155Z","shell.execute_reply":"2026-01-10T09:49:18.905109Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 1. Parse GO Ontology","metadata":{"_uuid":"443ec29a-6386-4661-b339-545345129b13","_cell_guid":"dece1843-1938-48fc-b623-b24a300f79ec","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"OBO_PATH = f'{COMPETITION_DATA}/Train/go-basic.obo'\n\nterm_parents = defaultdict(set)\nwith open(OBO_PATH, 'r') as f:\n  cur_id = None\n  for line in f:\n      line = line.strip()\n      if line.startswith('id: '):\n          cur_id = line.split('id: ')[1].strip()\n      elif line.startswith('is_a: ') and cur_id:\n          term_parents[cur_id].add(line.split()[1].strip())\n      elif line.startswith('relationship: part_of ') and cur_id:\n          term_parents[cur_id].add(line.split()[2].strip())\n\nROOTS = {'GO:0003674', 'GO:0008150', 'GO:0005575'}\n\nancestors_map = {}\ndef get_ancestors(term):\n  if term in ancestors_map:\n      return ancestors_map[term]\n  parents = term_parents.get(term, set())\n  all_anc = set(parents)\n  for p in parents:\n      all_anc |= get_ancestors(p)\n  ancestors_map[term] = all_anc\n  return all_anc\n\nfor term in term_parents:\n  get_ancestors(term)\nprint(f\"GO terms: {len(ancestors_map)}\")","metadata":{"_uuid":"c4b52357-8aa5-4038-8af8-f3a9dfadd4fa","_cell_guid":"d3948d25-195f-4f29-9c1a-7981cab8199d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-01-10T09:49:18.908514Z","iopub.execute_input":"2026-01-10T09:49:18.910051Z","iopub.status.idle":"2026-01-10T09:49:19.986292Z","shell.execute_reply.started":"2026-01-10T09:49:18.910021Z","shell.execute_reply":"2026-01-10T09:49:19.985048Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Load Predictions","metadata":{"_uuid":"d93e182a-9888-4061-9227-b2b00c22a201","_cell_guid":"6076885b-c1a4-4ba2-86c3-f639445daa1e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"def load_predictions(filepath):\n  \"\"\"Load predictions: {protein: {go_term: score}}\"\"\"\n  data = defaultdict(dict)\n  with open(filepath, 'r') as f:\n      for line in tqdm(f, desc=f\"Loading {os.path.basename(filepath)}\"):\n          parts = line.strip().split('\\t')\n          if len(parts) >= 3:\n              pid, go, score = parts[0], parts[1], float(parts[2])\n              data[pid][go] = max(data[pid].get(go, 0), score)\n  return data\n\n# Load GOA predictions\ngoa_preds = load_predictions(f'{PREDICTION_DATA}/goa_submission.tsv')\nprint(f\"GOA proteins: {len(goa_preds)}\")\n\n# Load ProtT5 + InterPro predictions\nprott5_preds = load_predictions(f'{PREDICTION_DATA}/prott5_interpro_predictions.tsv')\nprint(f\"ProtT5+InterPro proteins: {len(prott5_preds)}\")","metadata":{"_uuid":"d3eea170-8eab-45de-8ec4-124ea6245ed1","_cell_guid":"b9c2d229-3e9c-489f-a288-ddbbd3ce59c4","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Create Ensemble\n**Weights:**\n- GOA: 55% (curated database annotations)\n- ProtT5+InterPro: 45% (model predictions)","metadata":{"_uuid":"b1e38a1b-2eba-4d7f-840d-cbe656b525ef","_cell_guid":"aefcfb19-a54a-4a4f-a33e-cc694319322e","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"WEIGHT_GOA = 0.55\nWEIGHT_PROTT5 = 0.45\n\nprint(f\"Ensemble weights: GOA={WEIGHT_GOA}, ProtT5={WEIGHT_PROTT5}\")\n\nall_proteins = set(goa_preds.keys()) | set(prott5_preds.keys())\nensemble = defaultdict(dict)\n\nfor pid in tqdm(all_proteins, desc=\"Creating ensemble\"):\n  goa = goa_preds.get(pid, {})\n  prott5 = prott5_preds.get(pid, {})\n  all_terms = set(goa.keys()) | set(prott5.keys())\n\n  for term in all_terms:\n      s_goa = goa.get(term, 0)\n      s_prott5 = prott5.get(term, 0)\n\n      if s_goa > 0 and s_prott5 > 0:\n          # Both predict -> weighted average\n          ensemble[pid][term] = WEIGHT_GOA * s_goa + WEIGHT_PROTT5 * s_prott5\n      elif s_goa > 0:\n          # Only GOA\n          ensemble[pid][term] = s_goa\n      else:\n          # Only ProtT5\n          ensemble[pid][term] = s_prott5\n\nprint(f\"Ensemble proteins: {len(ensemble)}\")","metadata":{"_uuid":"435f66ce-c537-4b38-953e-19e0b7d0ce78","_cell_guid":"3e5df958-784a-428b-a8ea-0bca4c6e9e30","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Apply GOA+ Propagation\n- Positive propagation: Parent scores >= Child scores\n- Negative propagation: Child scores constrained by parent\n- Power scaling: Boost confidence of top predictions","metadata":{"_uuid":"5a2eeeca-4f67-4e66-a617-2efd396c1925","_cell_guid":"cfc1aa33-97d9-4020-999f-8a2720a10af5","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"CONFIG = {\n  'NEG_PROP_ALPHA': 0.7,\n  'SCALING_POWER': 0.8,\n  'MAX_SCORE': 0.95,\n  'TOP_K': 200,\n}\n\ndef process_protein(scores_dict):\n  updated = scores_dict.copy()\n\n  # Positive propagation: ensure parent >= child\n  for term, score in scores_dict.items():\n      for anc in get_ancestors(term):\n          updated[anc] = max(updated.get(anc, 0), score)\n\n  # Negative propagation: constrain child by parent\n  for term in list(updated.keys()):\n      if term in ROOTS:\n          continue\n      ancs = get_ancestors(term)\n      if ancs:\n          anc_scores = [updated.get(a, 0) for a in ancs if a in updated]\n          if anc_scores and min(anc_scores) < updated[term]:\n              alpha = CONFIG['NEG_PROP_ALPHA']\n              updated[term] = alpha * min(anc_scores) + (1-alpha) * updated[term]\n\n  # Power scaling\n  non_root = [s for t, s in updated.items() if t not in ROOTS]\n  if non_root:\n      max_val = max(non_root)\n      if 0 < max_val < CONFIG['MAX_SCORE']:\n          for t in updated:\n              if t not in ROOTS:\n                  updated[t] = min(1.0, np.power(updated[t]/max_val, CONFIG['SCALING_POWER']) * CONFIG['MAX_SCORE'])\n\n  # Force roots to 1.0\n  for r in ROOTS:\n      updated[r] = 1.0\n\n  return updated","metadata":{"_uuid":"fa59a960-762a-4f66-b3a0-61a4088b68f5","_cell_guid":"a2954e09-f38c-443f-bfc3-cbf2b96664ce","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"final_rows = []\n\nfor pid, scores in tqdm(ensemble.items(), desc=\"Applying propagation\"):\n  updated = process_protein(scores)\n\n  # Top-K predictions\n  sorted_terms = sorted(updated.items(), key=lambda x: -x[1])[:CONFIG['TOP_K']]\n  for go, s in sorted_terms:\n      if s >= 0.001:\n          final_rows.append(f\"{pid}\\t{go}\\t{s:.6f}\")\n\nprint(f\"Total predictions: {len(final_rows):,}\")","metadata":{"_uuid":"0d84b84b-3c10-41a3-9001-c491dfcbb74f","_cell_guid":"2e438097-dc5f-44b0-b338-3b23a4750766","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Write Submission","metadata":{"_uuid":"5ad7e85a-5841-4115-bc50-4a7c1897c715","_cell_guid":"9755f3d8-23b7-47ee-a054-9db5bb8a0c15","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"OUTPUT_FILE = 'submission.tsv'\n\nwith open(OUTPUT_FILE, 'w') as f:\n  for line in final_rows:\n      f.write(line + \"\\n\")\n\nsize_mb = os.path.getsize(OUTPUT_FILE) / (1024*1024)\nprint(f\"Submission file: {OUTPUT_FILE}\")\nprint(f\"Size: {size_mb:.1f} MB\")\nprint(f\"Predictions: {len(final_rows):,}\")","metadata":{"_uuid":"478f439f-5808-4f43-a7f5-d49b7e351733","_cell_guid":"a6d22c04-585a-4763-bf9c-6edb8ee356ea","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Summary\nThis notebook achieves **0.370** by combining:\n1. **GOA Database** - Curated GO annotations from UniProt-GOA\n2. **ProtT5 + InterPro** - Protein language model predictions\n3. **GOA+ Propagation** - Ontology-aware post-processing","metadata":{"_uuid":"69d39b7d-5ef2-42bf-a3e3-57281877747d","_cell_guid":"e40b15b8-a287-4c07-95f1-99b95f8dc0f6","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}}]}