{"cells": [{"cell_type": "markdown", "metadata": {}, "source": "# CAFA-6: GOA+ Boost v1\n\n**Techniques from top kernels:**\n1. Use proven ensemble as single high-quality source\n2. GOA+ propagation (parent >= child)\n3. Rank normalization (boost max to 0.95)\n4. Root terms forced to 1.0\n5. No mixing with low-score sources"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "import os\nimport gc\nimport numpy as np\nimport pandas as pd\nfrom collections import defaultdict\nfrom tqdm.auto import tqdm\n\nprint('CAFA-6 GOA+ Boost v1')"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Config\nOBO_PATH = '/kaggle/input/cafa-6-protein-function-prediction/Train/go-basic.obo'\n# Use the proven ensemble (0.358-0.373 range)\nINPUT_SUBMISSION = '/kaggle/input/cafa-6-proven-ensemble-submission/submission.tsv'\n\nROOTS = {'GO:0003674', 'GO:0008150', 'GO:0005575'}\n\n# GOA+ parameters\nTARGET_MAX_SCORE = 0.95  # Boost max prediction to this\nMIN_SCORE_THRESHOLD = 0.01  # Drop below this\nLOW_CLIP = 0.05  # Clip scores below this to 0\n\nprint(f'Input: {os.path.basename(INPUT_SUBMISSION)}')\nprint(f'Target max score: {TARGET_MAX_SCORE}')"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Parse GO ontology\nprint('[1/4] Parsing GO ontology...')\n\ndef parse_obo(obo_path):\n    parents = defaultdict(set)\n    with open(obo_path, 'r') as f:\n        cur_id = None\n        for line in f:\n            line = line.strip()\n            if line == '[Term]':\n                cur_id = None\n            elif line.startswith('id: '):\n                cur_id = line.split('id: ')[1].strip()\n            elif line.startswith('is_a: '):\n                pid = line.split()[1].strip()\n                if cur_id:\n                    parents[cur_id].add(pid)\n            elif line.startswith('relationship: part_of '):\n                parts = line.split()\n                if len(parts) >= 3:\n                    pid = parts[2].strip()\n                    if cur_id:\n                        parents[cur_id].add(pid)\n    return parents\n\ndef build_ancestors_map(term_parents):\n    ancestors = {}\n    def get_ancestors(term):\n        if term in ancestors:\n            return ancestors[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[term] = all_anc\n        return all_anc\n    \n    for term in term_parents:\n        get_ancestors(term)\n    return ancestors\n\nterm_parents = parse_obo(OBO_PATH)\nancestors_map = build_ancestors_map(term_parents)\nprint(f'Loaded {len(term_parents)} GO terms')"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Load submission\nprint('[2/4] Loading submission...')\n\ndf = pd.read_csv(INPUT_SUBMISSION, sep='\\t', header=None, \n                 names=['protein_id', 'go_term', 'score'])\ndf['protein_id'] = df['protein_id'].astype(str)\ndf['go_term'] = df['go_term'].astype(str)\ndf['score'] = pd.to_numeric(df['score'], errors='coerce').fillna(0).clip(0, 1.0)\n\nprint(f'Loaded {len(df):,} predictions')\nprint(f'Proteins: {df.protein_id.nunique():,}')\nprint(f'Score range: {df.score.min():.3f} - {df.score.max():.3f}')\n\n# Clip low scores\nbefore = len(df)\ndf = df[df['score'] >= LOW_CLIP]\nprint(f'After clipping < {LOW_CLIP}: {len(df):,} ({before - len(df):,} removed)')"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# GOA+ Processing\nprint('[3/4] Applying GOA+ propagation with rank normalization...')\n\n# Group by protein\nprotein_groups = df.groupby('protein_id')\nn_proteins = len(protein_groups)\n\nfinal_rows = []\n\nfor i, (pid, group) in enumerate(tqdm(protein_groups, total=n_proteins)):\n    terms_dict = dict(zip(group['go_term'], group['score']))\n    final_scores = terms_dict.copy()\n    \n    # A. POSITIVE PROPAGATION: Parent >= Child\n    for term, score in list(terms_dict.items()):\n        if term in ancestors_map:\n            for anc in ancestors_map[term]:\n                final_scores[anc] = max(final_scores.get(anc, 0), score)\n    \n    # B. FORCE ROOTS to 1.0\n    for root in ROOTS:\n        final_scores[root] = 1.0\n    \n    # C. RANK NORMALIZATION (key technique from top kernels)\n    # Find max non-root score\n    max_non_root = 0.0\n    for t, s in final_scores.items():\n        if t not in ROOTS:\n            max_non_root = max(max_non_root, s)\n    \n    # Scale up if max is below target\n    if max_non_root > 0 and max_non_root < TARGET_MAX_SCORE:\n        scale = TARGET_MAX_SCORE / max_non_root\n        for t in final_scores:\n            if t not in ROOTS:\n                final_scores[t] = min(1.0, final_scores[t] * scale)\n    \n    # Collect results\n    for term, score in final_scores.items():\n        if score >= MIN_SCORE_THRESHOLD:\n            final_rows.append((pid, term, round(score, 6)))\n\nprint(f'Total predictions after GOA+: {len(final_rows):,}')"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Create final dataframe and save\nprint('[4/4] Saving submission...')\n\nfinal_df = pd.DataFrame(final_rows, columns=['protein_id', 'go_term', 'score'])\nfinal_df = final_df.sort_values(['protein_id', 'score'], ascending=[True, False])\nfinal_df.to_csv('submission.tsv', sep='\\t', index=False, header=False)\n\nprint(f'\\n{\"=\"*60}')\nprint('DONE - CAFA6 GOA+ Boost v1')\nprint(f'Total predictions: {len(final_df):,}')\nprint(f'Unique proteins: {final_df.protein_id.nunique():,}')\nprint(f'{\"=\"*60}')"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Analysis\nprint('\\nScore distribution:')\nprint(f'min={final_df.score.min():.3f}, max={final_df.score.max():.3f}, mean={final_df.score.mean():.3f}')\n\nprint('\\nSample:')\nprint(final_df.head(10))"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "!head submission.tsv"}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"name": "python", "version": "3.10.0"}}, "nbformat": 4, "nbformat_minor": 4}