{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install biopython\n\n!wget https://github.com/bbuchfink/diamond/releases/download/v2.1.8/diamond-linux64.tar.gz\n!tar -xzf diamond-linux64.tar.gz\n!chmod +x diamond\n!mv diamond /kaggle/working/diamond\n!diamond version","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nGO_OBO = \"/kaggle/input/cafa-6/go-basic.obo\"\nTRAIN_FASTA = \"/kaggle/input/cafa-6/train_sequences.fasta\"\nTRAIN_TERMS = \"/kaggle/input/cafa-6/train_terms.tsv\"\nTRAIN_TAXONOMY = \"/kaggle/input/cafa-6/train_taxonomy.tsv\"\nTEST_FASTA = \"/kaggle/input/cafa-6/testsuperset.fasta\"\nSAMPLE_SUB = \"/kaggle/input/cafa-6/sample_submission.tsv\"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"paths = {\n    \"GO_OBO\": GO_OBO,\n    \"TRAIN_FASTA\": TRAIN_FASTA,\n    \"TRAIN_TERMS\": TRAIN_TERMS,\n    \"TEST_FASTA\": TEST_FASTA,\n    \"SAMPLE_SUB\": SAMPLE_SUB,\n    \"TRAIN_TAXONOMY\": TRAIN_TAXONOMY\n}\n\nfor name, path in paths.items():\n    print(f\"{name}: {'OK' if os.path.exists(path) else 'MISSING'} -> {path}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===============================\n# IMPORTS\n# ===============================\n\nimport os\nimport subprocess\nfrom collections import defaultdict\nimport pandas as pd\nimport networkx as nx\nfrom Bio import SeqIO\nfrom tqdm.auto import tqdm\n\n# ===============================\n# PATHS (KAGGLE)\n# ===============================\n\nGO_OBO = \"/kaggle/input/cafa-6/go-basic.obo\"\nTRAIN_FASTA = \"/kaggle/input/cafa-6/train_sequences.fasta\"\nTRAIN_TERMS = \"/kaggle/input/cafa-6/train_terms.tsv\"\nTEST_FASTA = \"/kaggle/input/cafa-6/testsuperset.fasta\"\nSAMPLE_SUB = \"/kaggle/input/cafa-6/sample_submission.tsv\"\nTRAIN_TAXONOMY = \"/kaggle/input/cafa-6/train_taxonomy.tsv\"\n\n\nWORKDIR = \"/kaggle/working\"\nDB_NAME = f\"{WORKDIR}/train_db\"\nBLAST_OUT = f\"{WORKDIR}/blast.tsv\"\nSUBMISSION = f\"{WORKDIR}/submission.tsv\"\n\nos.makedirs(WORKDIR, exist_ok=True)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===============================\n# MODULE 1 — GO DAG\n# ===============================\n\ndef load_go_dag(obo_file):\n    graph = nx.DiGraph()\n    aspect = {}\n    current = None\n\n    with open(obo_file) as f:\n        for line in f:\n            line = line.strip()\n            if line == \"[Term]\":\n                current = {}\n            elif line.startswith(\"id: GO:\"):\n                current[\"id\"] = line.split()[-1]\n            elif line.startswith(\"namespace:\"):\n                ns = line.split()[-1]\n                aspect[current[\"id\"]] = (\n                    \"F\" if \"molecular\" in ns else\n                    \"P\" if \"biological\" in ns else\n                    \"C\"\n                )\n            elif line.startswith(\"is_a: GO:\"):\n                parent = line.split()[1]\n                graph.add_edge(current[\"id\"], parent)\n\n    ancestor_cache = {t: nx.ancestors(graph, t) for t in graph.nodes}\n    return graph, aspect, ancestor_cache\n\nprint(\"Loading GO DAG...\")\ngo_graph, go_aspect, go_ancestors = load_go_dag(GO_OBO)\nprint(f\"GO terms loaded: {len(go_graph.nodes)}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===============================\n# MODULE 2 — LOAD ANNOTATIONS\n# ===============================\n\nprint(\"Loading training annotations...\")\n\nannotations = defaultdict(set)\ndf = pd.read_csv(TRAIN_TERMS, sep=\"\\t\", names=[\"protein\", \"go\", \"aspect\"])\ndf = df[df[\"aspect\"].isin([\"F\", \"P\"])]\n\nfor _, r in df.iterrows():\n    go = r[\"go\"]\n    if go not in go_ancestors:\n        continue\n    protein = r[\"protein\"]\n    annotations[protein].add(go)\n    annotations[protein].update(go_ancestors[go])\n\nprint(f\"Annotated proteins: {len(annotations)}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\n# ===============================\n# MODULE 3 — DIAMOND DATABASE\n# ===============================\n\nprint(\"Checking Diamond database...\")\n\nDIAMOND = \"/kaggle/working/diamond\"\n\nif not os.path.exists(DB_NAME + \".dmnd\"):\n    print(\"Building Diamond database from training sequences...\")\n    subprocess.run(\n        [DIAMOND, \"makedb\", \"--in\", TRAIN_FASTA, \"--db\", DB_NAME],\n        check=True\n    )\nelse:\n    print(\"Diamond database already exists\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# MODULE 4 – RUN DIAMOND BLAST\n# =========================\n\nprint(\"Running Diamond BLAST...\")\n\nDIAMOND = \"/kaggle/working/diamond\"   # correct for your setup\n\nsubprocess.run(\n    [\n        DIAMOND, \"blastp\",\n        \"--query\", TEST_FASTA,\n        \"--db\", DB_NAME,\n        \"--out\", BLAST_OUT,\n        \"--outfmt\", \"6\",          # FIXED FORMAT (12 columns)\n        \"--evalue\", \"1e-5\",\n        \"--max-target-seqs\", \"25\",\n        \"--threads\", \"4\"\n    ],\n    check=True\n)\n\nprint(\"BLAST finished\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =========================\n# MODULE 5 – HOMOLOGY TRANSFER\n# =========================\n\nprint(\"Transferring GO terms...\")\n\npredictions = defaultdict(dict)\n\nwith open(BLAST_OUT) as f:\n    for line in tqdm(f):\n        parts = line.strip().split()\n\n        qid = parts[0]\n        sid = parts[1]\n        pid = float(parts[2])\n        alen = int(parts[3])\n        qlen = int(parts[4])\n\n        if qlen == 0:\n            continue\n\n        coverage = (alen / qlen) * 100\n        if pid < 20 or coverage < 50:\n            continue\n\n\n        # Normalize subject ID\n        if \"|\" in sid:\n            sid = sid.split(\"|\")[1]\n        sid = sid.strip()\n\n        if sid not in annotations:\n            continue\n\n        for term in annotations[sid]:\n            if go_aspect.get(term) not in (\"F\", \"P\"):\n                continue\n\n            score = (pid / 100) * (coverage / 100)\n            predictions[qid][term] = max(\n                predictions[qid].get(term, 0.0),\n                score\n            )\n\nprint(f\"Proteins with predictions: {len(predictions)}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ===============================\n# MODULE 6 — TRUE PATH RULE\n# ===============================\n\nprint(\"Applying GO term propagation...\")\n\nfinal_predictions = {}\n\nfor pid, terms in predictions.items():\n    expanded = dict(terms)\n    for t, s in terms.items():\n        for parent in go_ancestors.get(t, []):\n            expanded[parent] = max(expanded.get(parent, 0), s * 0.9)\n    final_predictions[pid] = expanded","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(len(final_predictions))\nprint(sum(len(v) for v in final_predictions.values()))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================\n# MODULE 7 – SUBMISSION FILE (STREAMING, SAFE)\n# ============================\n\nprint(\"Writing submission file (streaming)...\")\n\nwith open(SUBMISSION, \"w\") as out:\n    # Write header\n    out.write(\"EntryID\\tterm\\tconfidence\\n\")\n\n    row_count = 0\n\n    for protein, terms in final_predictions.items():\n        for go, score in terms.items():\n            if score >= 0.1 and go_aspect.get(go) in [\"F\", \"P\"]:\n                out.write(f\"{protein}\\t{go}\\t{score:.3f}\\n\")\n                row_count += 1\n\nprint(\"DONE\")\nprint(f\"Submission saved to: {SUBMISSION}\")\nprint(f\"Total predictions written: {row_count}\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}