{"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":"none","dataSources":[{"sourceType":"competition","sourceId":41875,"databundleVersionId":5521661}],"dockerImageVersionId":31286,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install goatools","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:30:30.583384Z","iopub.execute_input":"2026-02-24T04:30:30.583713Z","iopub.status.idle":"2026-02-24T04:31:12.834313Z","shell.execute_reply.started":"2026-02-24T04:30:30.583674Z","shell.execute_reply":"2026-02-24T04:31:12.833265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom collections import defaultdict, deque\nimport matplotlib.pyplot as plt\nfrom goatools.obo_parser import GODag\nimport random\nfrom tqdm.auto import tqdm\n\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:31:12.836721Z","iopub.execute_input":"2026-02-24T04:31:12.837048Z","iopub.status.idle":"2026-02-24T04:31:13.705774Z","shell.execute_reply.started":"2026-02-24T04:31:12.837001Z","shell.execute_reply":"2026-02-24T04:31:13.704942Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Load GO ontology\ngo_dag = GODag(\"/kaggle/input/competitions/cafa-5-protein-function-prediction/Train/go-basic.obo\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:31:13.706703Z","iopub.execute_input":"2026-02-24T04:31:13.707133Z","iopub.status.idle":"2026-02-24T04:31:14.785557Z","shell.execute_reply.started":"2026-02-24T04:31:13.707105Z","shell.execute_reply":"2026-02-24T04:31:14.78476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_file = \"/kaggle/input/competitions/cafa-5-protein-function-prediction/Train/train_terms.tsv\"\n\ntrain_df = pd.read_csv(train_file, sep=\"\\t\")\n\nprint(train_df.head())\nprint(\"Total annotation rows:\", len(train_df))\nprint(\"Total proteins:\", train_df[\"EntryID\"].nunique())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:31:14.786591Z","iopub.execute_input":"2026-02-24T04:31:14.786914Z","iopub.status.idle":"2026-02-24T04:31:17.785162Z","shell.execute_reply.started":"2026-02-24T04:31:14.786881Z","shell.execute_reply":"2026-02-24T04:31:17.784245Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"protein2terms = defaultdict(set)\n\nfor _, row in train_df.iterrows():\n    protein2terms[row['EntryID']].add(row['term'])\n\nN = len(protein2terms)  # Total number of proteins\nprint(\"Total proteins:\", N)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:31:17.786444Z","iopub.execute_input":"2026-02-24T04:31:17.786883Z","iopub.status.idle":"2026-02-24T04:35:02.885126Z","shell.execute_reply.started":"2026-02-24T04:31:17.786846Z","shell.execute_reply":"2026-02-24T04:35:02.884238Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"go_dag['GO:0000001']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:02.886467Z","iopub.execute_input":"2026-02-24T04:35:02.887167Z","iopub.status.idle":"2026-02-24T04:35:02.893755Z","shell.execute_reply.started":"2026-02-24T04:35:02.887137Z","shell.execute_reply":"2026-02-24T04:35:02.892913Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def propagate_terms(go_terms, go_dag):\n    all_terms = set()\n    \n    for term in go_terms:\n        if term in go_dag:\n            all_terms.add(term)\n            all_terms.update(go_dag[term].get_all_parents())\n    \n    return all_terms\n\n\n# Propagate for every protein\nprotein2terms_propagated = {}\n\nfor protein, terms in protein2terms.items():\n    protein2terms_propagated[protein] = propagate_terms(terms, go_dag)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:02.896251Z","iopub.execute_input":"2026-02-24T04:35:02.89658Z","iopub.status.idle":"2026-02-24T04:35:35.587554Z","shell.execute_reply.started":"2026-02-24T04:35:02.896554Z","shell.execute_reply":"2026-02-24T04:35:35.586698Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"term_frequency = defaultdict(int)\n\nfor terms in protein2terms_propagated.values():\n    for term in terms:\n        term_frequency[term] += 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:35.588793Z","iopub.execute_input":"2026-02-24T04:35:35.589138Z","iopub.status.idle":"2026-02-24T04:35:36.852303Z","shell.execute_reply.started":"2026-02-24T04:35:35.589109Z","shell.execute_reply":"2026-02-24T04:35:36.851406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"root_bp = \"GO:0008150\"\n\nprint(\"Root frequency:\", term_frequency[root_bp])\nprint(\"Total proteins:\", N)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:36.85336Z","iopub.execute_input":"2026-02-24T04:35:36.853665Z","iopub.status.idle":"2026-02-24T04:35:36.858959Z","shell.execute_reply.started":"2026-02-24T04:35:36.853628Z","shell.execute_reply":"2026-02-24T04:35:36.858043Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"root_bp = \"GO:0003674\"\n\nprint(\"Root frequency:\", term_frequency[root_bp])\nprint(\"Total proteins:\", N)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:36.859916Z","iopub.execute_input":"2026-02-24T04:35:36.860495Z","iopub.status.idle":"2026-02-24T04:35:36.876786Z","shell.execute_reply.started":"2026-02-24T04:35:36.860469Z","shell.execute_reply":"2026-02-24T04:35:36.87583Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"root_bp = \"GO:0005575\"\n\nprint(\"Root frequency:\", term_frequency[root_bp])\nprint(\"Total proteins:\", N)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:36.878016Z","iopub.execute_input":"2026-02-24T04:35:36.878375Z","iopub.status.idle":"2026-02-24T04:35:36.895987Z","shell.execute_reply.started":"2026-02-24T04:35:36.878339Z","shell.execute_reply":"2026-02-24T04:35:36.894948Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IA = {}\n\nfor term, freq_t in term_frequency.items():\n    \n    if term not in go_dag:\n        continue\n        \n    parents = go_dag[term].parents\n    \n    # Root term → IA = 0\n    if not parents:\n        IA[term] = 0.0\n        continue\n    \n    # Get maximum parent frequency\n    parent_freqs = []\n    for parent in parents:\n        parent_id = parent.id\n        if parent_id in term_frequency:\n            parent_freqs.append(term_frequency[parent_id])\n    \n    if not parent_freqs:\n        IA[term] = 0.0\n        continue\n    \n    max_parent_freq = max(parent_freqs)\n    \n    # Compute conditional probability\n    p_cond = freq_t / max_parent_freq\n    \n    # Numerical safety\n    if p_cond <= 0:\n        IA[term] = 0.0\n    else:\n        IA[term] = -np.log(p_cond)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:36.897157Z","iopub.execute_input":"2026-02-24T04:35:36.897751Z","iopub.status.idle":"2026-02-24T04:35:37.053144Z","shell.execute_reply.started":"2026-02-24T04:35:36.897718Z","shell.execute_reply":"2026-02-24T04:35:37.051976Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"IA root:\", IA[\"GO:0008150\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.054127Z","iopub.execute_input":"2026-02-24T04:35:37.054424Z","iopub.status.idle":"2026-02-24T04:35:37.059283Z","shell.execute_reply.started":"2026-02-24T04:35:37.054389Z","shell.execute_reply":"2026-02-24T04:35:37.058452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"max_value = max(IA.values())\nmin_value = min(IA.values())\n\nprint(\"Min IA:\", min_value)\nprint(\"Max IA:\", max_value)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.060457Z","iopub.execute_input":"2026-02-24T04:35:37.06078Z","iopub.status.idle":"2026-02-24T04:35:37.080146Z","shell.execute_reply.started":"2026-02-24T04:35:37.060754Z","shell.execute_reply":"2026-02-24T04:35:37.079042Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IC = {}\n\nfor term, freq in term_frequency.items():\n    p = freq / N\n    IC[term] = -np.log(p)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.081147Z","iopub.execute_input":"2026-02-24T04:35:37.081464Z","iopub.status.idle":"2026-02-24T04:35:37.152295Z","shell.execute_reply.started":"2026-02-24T04:35:37.081436Z","shell.execute_reply":"2026-02-24T04:35:37.151243Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"IC root:\", IC[\"GO:0008150\"])\nprint(\"IA root:\", IA[\"GO:0008150\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.15339Z","iopub.execute_input":"2026-02-24T04:35:37.153696Z","iopub.status.idle":"2026-02-24T04:35:37.158936Z","shell.execute_reply.started":"2026-02-24T04:35:37.153672Z","shell.execute_reply":"2026-02-24T04:35:37.158022Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"proteins_with_BP = sum(\n    1 for terms in protein2terms_propagated.values()\n    if \"GO:0008150\" in terms\n)\n\nprint(proteins_with_BP)\nprint(term_frequency[\"GO:0008150\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.159994Z","iopub.execute_input":"2026-02-24T04:35:37.16035Z","iopub.status.idle":"2026-02-24T04:35:37.204918Z","shell.execute_reply.started":"2026-02-24T04:35:37.160301Z","shell.execute_reply":"2026-02-24T04:35:37.203854Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"depths = []\nia_values = []\n\nfor term, ia in IA.items():\n    if term in go_dag:\n        depths.append(go_dag[term].depth)\n        ia_values.append(ia)\n\nplt.figure()\nplt.scatter(depths, ia_values, alpha=0.3)\nplt.xlabel(\"GO Term Depth\")\nplt.ylabel(\"Information Accretion (IA)\")\nplt.title(\"Depth vs IA\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.205889Z","iopub.execute_input":"2026-02-24T04:35:37.206157Z","iopub.status.idle":"2026-02-24T04:35:37.724133Z","shell.execute_reply.started":"2026-02-24T04:35:37.206132Z","shell.execute_reply":"2026-02-24T04:35:37.723122Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"violations = []\n\nfor term, freq in term_frequency.items():\n    if term not in go_dag:\n        continue\n        \n    for parent in go_dag[term].parents:\n        parent_id = parent.id\n        if parent_id in term_frequency:\n            if term_frequency[parent_id] < freq:\n                violations.append((term, parent_id))\n\nprint(\"Number of frequency violations:\", len(violations))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.725284Z","iopub.execute_input":"2026-02-24T04:35:37.725617Z","iopub.status.idle":"2026-02-24T04:35:37.800581Z","shell.execute_reply.started":"2026-02-24T04:35:37.725589Z","shell.execute_reply":"2026-02-24T04:35:37.799249Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"prob_violations = []\n\nfor term, freq_t in term_frequency.items():\n    if term not in go_dag:\n        continue\n        \n    parents = go_dag[term].parents\n    \n    for parent in parents:\n        parent_id = parent.id\n        if parent_id in term_frequency:\n            p = freq_t / term_frequency[parent_id]\n            if p > 1:\n                prob_violations.append((term, parent_id))\n\nprint(\"Probability violations:\", len(prob_violations))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.801649Z","iopub.execute_input":"2026-02-24T04:35:37.801991Z","iopub.status.idle":"2026-02-24T04:35:37.882826Z","shell.execute_reply.started":"2026-02-24T04:35:37.801942Z","shell.execute_reply":"2026-02-24T04:35:37.881836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"roots = [\"GO:0008150\", \"GO:0003674\", \"GO:0005575\"]\n\nfor root in roots:\n    print(root, \"freq:\", term_frequency.get(root, 0), \n          \"IA:\", IA.get(root, None))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.883937Z","iopub.execute_input":"2026-02-24T04:35:37.884281Z","iopub.status.idle":"2026-02-24T04:35:37.889766Z","shell.execute_reply.started":"2026-02-24T04:35:37.884252Z","shell.execute_reply":"2026-02-24T04:35:37.888936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"correlation = np.corrcoef(depths, ia_values)[0,1]\nprint(\"Depth–IA correlation:\", correlation)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.893716Z","iopub.execute_input":"2026-02-24T04:35:37.894005Z","iopub.status.idle":"2026-02-24T04:35:37.915352Z","shell.execute_reply.started":"2026-02-24T04:35:37.893978Z","shell.execute_reply":"2026-02-24T04:35:37.914416Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"roots = {\n    \"BP\": \"GO:0008150\",\n    \"MF\": \"GO:0003674\",\n    \"CC\": \"GO:0005575\"\n}\n\nfor name, root in roots.items():\n    print(name, term_frequency.get(root, 0))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.916425Z","iopub.execute_input":"2026-02-24T04:35:37.916748Z","iopub.status.idle":"2026-02-24T04:35:37.9339Z","shell.execute_reply.started":"2026-02-24T04:35:37.916721Z","shell.execute_reply":"2026-02-24T04:35:37.93265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"depth_to_ia = defaultdict(list)\n\nfor term, ia in IA.items():\n    if term in go_dag:\n        depth = go_dag[term].depth\n        depth_to_ia[depth].append(ia)\n\ndepths_sorted = sorted(depth_to_ia.keys())\nmean_ia = [np.mean(depth_to_ia[d]) for d in depths_sorted]\n\nplt.figure()\nplt.plot(depths_sorted, mean_ia)\nplt.xlabel(\"GO Term Depth\")\nplt.ylabel(\"Mean IA\")\nplt.title(\"Mean IA per Depth\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:37.934942Z","iopub.execute_input":"2026-02-24T04:35:37.93519Z","iopub.status.idle":"2026-02-24T04:35:38.121312Z","shell.execute_reply.started":"2026-02-24T04:35:37.935168Z","shell.execute_reply":"2026-02-24T04:35:38.120478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"zero_ia_terms = sum(1 for v in IA.values() if abs(v) < 1e-8)\nprint(\"Number of near-zero IA terms:\", zero_ia_terms)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:38.122377Z","iopub.execute_input":"2026-02-24T04:35:38.122703Z","iopub.status.idle":"2026-02-24T04:35:38.131944Z","shell.execute_reply.started":"2026-02-24T04:35:38.122672Z","shell.execute_reply":"2026-02-24T04:35:38.130677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"interesting = []\n\nfor term, ia in IA.items():\n    if term in go_dag:\n        depth = go_dag[term].depth\n        if depth > 10 and ia < 1:\n            interesting.append((term, depth, ia))\n\nprint(\"Deep but low-IA terms:\", len(interesting))\nprint(interesting[:10])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:38.133252Z","iopub.execute_input":"2026-02-24T04:35:38.13368Z","iopub.status.idle":"2026-02-24T04:35:38.18023Z","shell.execute_reply.started":"2026-02-24T04:35:38.133643Z","shell.execute_reply":"2026-02-24T04:35:38.179411Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ia_df = pd.read_csv('/kaggle/input/competitions/cafa-5-protein-function-prediction/IA.txt', sep='\\t', header=None, names=['EntryID', 'IA_no'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:38.181345Z","iopub.execute_input":"2026-02-24T04:35:38.181658Z","iopub.status.idle":"2026-02-24T04:35:38.225883Z","shell.execute_reply.started":"2026-02-24T04:35:38.181623Z","shell.execute_reply":"2026-02-24T04:35:38.224781Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"depth_to_ia1 = defaultdict(list)\n\nfor term, ia in tqdm(IA.items()):\n    if term in go_dag:\n        depth = go_dag[term].depth\n        depth_to_ia1[depth].append(ia_df[ia_df['EntryID']==term]['IA_no'])\n\ndepths_sorted1 = sorted(depth_to_ia1.keys())\nmean_ia1 = [np.mean(depth_to_ia1[d]) for d in depths_sorted1]\n\nplt.figure()\nplt.plot(depths_sorted1, mean_ia1)\nplt.xlabel(\"GO Term Depth\")\nplt.ylabel(\"Mean IA\")\nplt.title(\"Mean IA per Depth\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:35:38.226991Z","iopub.execute_input":"2026-02-24T04:35:38.227255Z","iopub.status.idle":"2026-02-24T04:37:52.270945Z","shell.execute_reply.started":"2026-02-24T04:35:38.227231Z","shell.execute_reply":"2026-02-24T04:37:52.264619Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# np.random.seed(42)\n\n# # True annotations\n# true_annotations = protein2terms_propagated\n\n# # Create fake prediction scores\n# prediction_scores = defaultdict(dict)\n\n# for protein, true_terms in true_annotations.items():\n    \n#     for term in IA.keys():\n        \n#         # True terms get higher average score\n#         if term in true_terms:\n#             score = np.random.beta(5,2)   # skewed high\n#         else:\n#             score = np.random.beta(1,5)   # skewed low\n        \n#         prediction_scores[protein][term] = score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:37:52.274134Z","iopub.execute_input":"2026-02-24T04:37:52.274571Z","iopub.status.idle":"2026-02-24T04:37:52.28766Z","shell.execute_reply.started":"2026-02-24T04:37:52.274531Z","shell.execute_reply":"2026-02-24T04:37:52.286273Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.random.seed(42)\nrandom.seed(42)\n\ntrue_annotations = protein2terms_propagated\n\nall_terms = list(IA.keys())\n\nprediction_scores = defaultdict(dict)\n\nNEGATIVE_SAMPLE_SIZE = 200  # number of false terms per protein\n\nfor protein, true_terms in tqdm(true_annotations.items()):\n    \n    # Positive predictions (true terms)\n    for term in true_terms:\n        score = np.random.beta(5, 2)\n        prediction_scores[protein][term] = score\n    \n    # Sample some false terms\n    false_candidates = list(set(all_terms) - true_terms)\n    sampled_false = random.sample(false_candidates, \n                                   min(NEGATIVE_SAMPLE_SIZE, len(false_candidates)))\n    \n    for term in sampled_false:\n        score = np.random.beta(1, 5)\n        prediction_scores[protein][term] = score","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T04:37:52.296439Z","iopub.execute_input":"2026-02-24T04:37:52.296941Z","iopub.status.idle":"2026-02-24T04:53:31.072378Z","shell.execute_reply.started":"2026-02-24T04:37:52.296894Z","shell.execute_reply":"2026-02-24T04:53:31.071405Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Precompute true denominators\ntrue_count = {}\ntrue_weight = {}\n\nfor protein, terms in true_annotations.items():\n    true_count[protein] = len(terms)\n    true_weight[protein] = sum(IA[t] for t in terms)\n\n\ndef compute_fmax_sparse(weighted=False, n_thresholds=50):\n    \n    thresholds = np.linspace(0, 1, n_thresholds)\n    f_scores = []\n    \n    for tau in tqdm(thresholds):\n        \n        P_list = []\n        R_list = []\n        \n        for protein, preds in prediction_scores.items():\n            \n            true_terms = true_annotations[protein]\n            \n            # Filter predicted terms at threshold\n            predicted_terms = {\n                t for t, s in preds.items() if s >= tau\n            }\n            \n            if not predicted_terms:\n                continue\n            \n            tp = predicted_terms & true_terms\n            \n            if weighted:\n                tp_val = sum(IA[t] for t in tp)\n                pred_val = sum(IA[t] for t in predicted_terms)\n                true_val = true_weight[protein]\n            else:\n                tp_val = len(tp)\n                pred_val = len(predicted_terms)\n                true_val = true_count[protein]\n            \n            if pred_val == 0 or true_val == 0:\n                continue\n            \n            P_list.append(tp_val / pred_val)\n            R_list.append(tp_val / true_val)\n        \n        if len(P_list) == 0:\n            f_scores.append(0)\n            continue\n        \n        P = np.mean(P_list)\n        R = np.mean(R_list)\n        \n        if P + R == 0:\n            f_scores.append(0)\n        else:\n            f_scores.append(2 * P * R / (P + R))\n    \n    return thresholds, f_scores, max(f_scores)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T05:40:27.16611Z","iopub.execute_input":"2026-02-24T05:40:27.166455Z","iopub.status.idle":"2026-02-24T05:40:28.572844Z","shell.execute_reply.started":"2026-02-24T05:40:27.166426Z","shell.execute_reply":"2026-02-24T05:40:28.571788Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"t_unw, f_unw, fmax_unw = compute_fmax_sparse(weighted=False)\nt_w, f_w, fmax_w = compute_fmax_sparse(weighted=True)\n\nprint(\"Unweighted Fmax:\", fmax_unw)\nprint(\"Weighted Fmax:\", fmax_w)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-24T05:40:37.407295Z","iopub.execute_input":"2026-02-24T05:40:37.408236Z","iopub.status.idle":"2026-02-24T05:40:50.418062Z","shell.execute_reply.started":"2026-02-24T05:40:37.408199Z","shell.execute_reply":"2026-02-24T05:40:50.416733Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure()\nplt.plot(t_unw, f_unw)\nplt.plot(t_w, f_w)\nplt.xlabel(\"Threshold\")\nplt.ylabel(\"F-score\")\nplt.title(\"Unweighted vs IA-Weighted F-score\")\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Relative drop:\", (fmax_unw - fmax_w) / fmax_unw)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.random.seed(42)\nrandom.seed(42)\n\n# Build depth dictionary\nterm_depth = {}\n\nfor term in IA:\n    if term in go_dag:\n        term_depth[term] = go_dag[term].depth\n\n# Define shallow and deep sets\nshallow_terms = {t for t, d in term_depth.items() if d <= 4}\ndeep_terms = {t for t, d in term_depth.items() if d >= 8}\n\nprint(\"Shallow terms:\", len(shallow_terms))\nprint(\"Deep terms:\", len(deep_terms))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def simulate_model_shallow():\n    \n    preds = defaultdict(dict)\n    NEG_SAMPLE = 200\n    \n    for protein, true_terms in tqdm(true_annotations.items()):\n        \n        # Positive predictions\n        for term in true_terms:\n            if term in shallow_terms:\n                score = np.random.beta(6,2)   # very confident\n            else:\n                score = np.random.beta(2,4)   # weaker for deep\n            preds[protein][term] = score\n        \n        # Sample false shallow terms\n        false_shallow = list(shallow_terms - true_terms)\n        sampled_false = random.sample(false_shallow,\n                                      min(NEG_SAMPLE, len(false_shallow)))\n        \n        for term in sampled_false:\n            score = np.random.beta(2,4)  # noisy shallow overprediction\n            preds[protein][term] = score\n    \n    return preds","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def simulate_model_deep():\n    \n    preds = defaultdict(dict)\n    NEG_SAMPLE = 200\n    \n    for protein, true_terms in tqdm(true_annotations.items()):\n        \n        # Positive predictions\n        for term in true_terms:\n            if term in deep_terms:\n                score = np.random.beta(6,2)  # strong for deep\n            else:\n                score = np.random.beta(2,4)  # weaker shallow\n            preds[protein][term] = score\n        \n        # Sample false deep terms\n        false_deep = list(deep_terms - true_terms)\n        sampled_false = random.sample(false_deep,\n                                      min(NEG_SAMPLE, len(false_deep)))\n        \n        for term in sampled_false:\n            score = np.random.beta(2,4)\n            preds[protein][term] = score\n    \n    return preds","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def compute_fmax_model(prediction_dict, weighted=False, n_thresholds=40):\n    \n    thresholds = np.linspace(0, 1, n_thresholds)\n    f_scores = []\n    \n    for tau in tqdm(thresholds):\n        \n        P_list = []\n        R_list = []\n        \n        for protein, preds in prediction_dict.items():\n            \n            true_terms = true_annotations[protein]\n            predicted_terms = {t for t, s in preds.items() if s >= tau}\n            \n            if not predicted_terms:\n                continue\n            \n            tp = predicted_terms & true_terms\n            \n            if weighted:\n                tp_val = sum(IA[t] for t in tp)\n                pred_val = sum(IA[t] for t in predicted_terms)\n                true_val = sum(IA[t] for t in true_terms)\n            else:\n                tp_val = len(tp)\n                pred_val = len(predicted_terms)\n                true_val = len(true_terms)\n            \n            if pred_val == 0 or true_val == 0:\n                continue\n            \n            P_list.append(tp_val / pred_val)\n            R_list.append(tp_val / true_val)\n        \n        if not P_list:\n            f_scores.append(0)\n            continue\n        \n        P = np.mean(P_list)\n        R = np.mean(R_list)\n        \n        if P + R == 0:\n            f_scores.append(0)\n        else:\n            f_scores.append(2 * P * R / (P + R))\n    \n    return max(f_scores)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_A = simulate_model_shallow()\nmodel_B = simulate_model_deep()\n\nA_unw = compute_fmax_model(model_A, weighted=False)\nA_w   = compute_fmax_model(model_A, weighted=True)\n\nB_unw = compute_fmax_model(model_B, weighted=False)\nB_w   = compute_fmax_model(model_B, weighted=True)\n\nprint(\"Model A (Shallow-biased)\")\nprint(\"  Unweighted Fmax:\", A_unw)\nprint(\"  Weighted Fmax:\", A_w)\n\nprint(\"\\nModel B (Deep-biased)\")\nprint(\"  Unweighted Fmax:\", B_unw)\nprint(\"  Weighted Fmax:\", B_w)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"depth_dict = {}\n\nfor term, node in go_dag.items():\n    depth_dict[term] = node.depth","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEEP_THRESHOLD = 4  # adjust if needed\ndeep_true = {}\n\nfor protein, terms in true_annotations.items():\n    deep_terms = [t for t in terms if depth_dict[t] >= DEEP_THRESHOLD]\n    deep_true[protein] = deep_terms\n\nprediction_scores_C = {}\n\nall_deep_terms = [\n    t for t, d in depth_dict.items()\n    if d >= DEEP_THRESHOLD\n]\n\nfor protein in true_annotations:\n    \n    preds = {}\n    true_deep = deep_true.get(protein, [])\n    \n    # 1–2 correct deep terms\n    if len(true_deep) > 0:\n        n_correct = min(len(true_deep), 1)\n        chosen = random.sample(true_deep, n_correct)\n        for t in chosen:\n            preds[t] = random.uniform(0.8, 1.0)\n    \n    # Rare false deep\n    if random.random() < 0.15:\n        false_candidates = list(\n            set(all_deep_terms) - set(true_annotations[protein])\n        )\n        if false_candidates:\n            t = random.choice(false_candidates)\n            preds[t] = random.uniform(0.4, 0.6)\n    \n    # Fallback: ensure at least one prediction\n    if not preds:\n        fallback = random.choice(list(true_annotations[protein]))\n        preds[fallback] = random.uniform(0.6, 0.8)\n    \n    prediction_scores_C[protein] = preds\n\n# for protein in true_annotations:\n    \n#     preds = {}\n    \n#     true_deep = deep_true[protein]\n    \n#     # ---- 1️⃣ Predict 1–2 correct deep terms ----\n#     if len(true_deep) > 0:\n        \n#         n_correct = min(len(true_deep), random.choice([1, 2]))\n#         chosen = random.sample(true_deep, n_correct)\n        \n#         for t in chosen:\n#             preds[t] = random.uniform(0.8, 1.0)\n    \n#     # ---- 2️⃣ Occasionally add one deep false positive ----\n#     if random.random() < 0.3:  # 30% of proteins\n        \n#         false_candidates = list(\n#             set(all_deep_terms) - set(true_annotations[protein])\n#         )\n        \n#         if len(false_candidates) > 0:\n#             t = random.choice(false_candidates)\n#             preds[t] = random.uniform(0.4, 0.6)\n    \n    prediction_scores_C[protein] = preds\n\nprediction_scores = prediction_scores_C\n\nt_unw, f_unw, fmax_unw = compute_fmax_sparse(weighted=False)\nt_w, f_w, fmax_w = compute_fmax_sparse(weighted=True)\n\nprint(\"Model C (Selective Deep)\")\nprint(\"Unweighted Fmax:\", fmax_unw)\nprint(\"Weighted Fmax:\", fmax_w)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}