{"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":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"},{"sourceId":290476004,"sourceType":"kernelVersion"},{"sourceId":290654813,"sourceType":"kernelVersion"}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"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)\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-01-08T03:42:09.330771Z","iopub.execute_input":"2026-01-08T03:42:09.331141Z","iopub.status.idle":"2026-01-08T03:42:10.583004Z","shell.execute_reply.started":"2026-01-08T03:42:09.331112Z","shell.execute_reply":"2026-01-08T03:42:10.582205Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install Biopython --q\n!pip install goatools --q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:44:00.071488Z","iopub.execute_input":"2026-01-08T03:44:00.072210Z","iopub.status.idle":"2026-01-08T03:44:07.789439Z","shell.execute_reply.started":"2026-01-08T03:44:00.072171Z","shell.execute_reply":"2026-01-08T03:44:07.788388Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport joblib","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:44:30.181068Z","iopub.execute_input":"2026-01-08T03:44:30.181514Z","iopub.status.idle":"2026-01-08T03:44:30.260050Z","shell.execute_reply.started":"2026-01-08T03:44:30.181476Z","shell.execute_reply":"2026-01-08T03:44:30.259179Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"lr_pred_mf = np.load(\"/kaggle/input/cafa6-prediction-using-lr/lr_pred_mf.npy\")\nlr_pred_bp = np.load(\"/kaggle/input/cafa6-prediction-using-lr/lr_pred_bp.npy\")\nlr_pred_cc = np.load(\"/kaggle/input/cafa6-prediction-using-lr/lr_pred_cc.npy\")\n\nmlp_pred_mf = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlp_pred_mf.npy\")\nmlp_pred_bp = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlp_pred_bp.npy\")\nmlp_pred_cc = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlp_pred_cc.npy\")\n\nY_mf = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/Y_mf.npy\")\nY_bp = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/Y_bp.npy\")\nY_cc = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/Y_cc.npy\")\n\nmlb_mf = joblib.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlb_mf.pkl\")\nmlb_bp = joblib.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlb_bp.pkl\")\nmlb_cc = joblib.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlb_cc.pkl\")\n\nY_val_mf = np.load(\"/kaggle/input/cafa6-prediction-using-lr/y_val_mf.npy\")\nY_val_bp = np.load(\"/kaggle/input/cafa6-prediction-using-lr/y_val_bp.npy\")\nY_val_cc = np.load(\"/kaggle/input/cafa6-prediction-using-lr/y_val_cc.npy\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:44:32.996283Z","iopub.execute_input":"2026-01-08T03:44:32.997026Z","iopub.status.idle":"2026-01-08T03:45:30.496358Z","shell.execute_reply.started":"2026-01-08T03:44:32.996994Z","shell.execute_reply":"2026-01-08T03:45:30.495420Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_active_go_terms(Y_matrix, mlb):\n    \"\"\"\n    Get only the GO terms that have at least one positive example.\n    This matches what your model actually predicts.\n    \n    Args:\n        Y_matrix: binary label matrix [n_samples, n_terms]\n        mlb: fitted MultiLabelBinarizer\n    \n    Returns:\n        tuple: (active_go_terms, column_indices)\n    \"\"\"\n    all_go_terms = list(mlb.classes_)\n    \n    # Find columns with at least one positive example\n    active_cols = np.where(Y_matrix.sum(axis=0) > 0)[0]\n    active_go_terms = [all_go_terms[i] for i in active_cols]\n    \n    print(f\"Total GO terms in MLB: {len(all_go_terms)}\")\n    print(f\"Active GO terms (with examples): {len(active_go_terms)}\")\n    \n    return active_go_terms, active_cols\n\n# For MF\ngo_terms_mf, active_cols_mf = get_active_go_terms(Y_mf, mlb_mf)\nprint(f\"MF: {len(go_terms_mf)} active terms\")\n\n# For BP\ngo_terms_bp, active_cols_bp = get_active_go_terms(Y_bp, mlb_bp)\nprint(f\"BP: {len(go_terms_bp)} active terms\")\n\n# For CC\ngo_terms_cc, active_cols_cc = get_active_go_terms(Y_cc, mlb_cc)\nprint(f\"CC: {len(go_terms_cc)} active terms\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:45:42.324308Z","iopub.execute_input":"2026-01-08T03:45:42.324889Z","iopub.status.idle":"2026-01-08T03:45:42.676426Z","shell.execute_reply.started":"2026-01-08T03:45:42.324854Z","shell.execute_reply":"2026-01-08T03:45:42.675441Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"go_terms_mf, active_cols_mf = get_active_go_terms(Y_mf, mlb_mf)\ngo_terms_bp, active_cols_bp = get_active_go_terms(Y_bp, mlb_bp)\ngo_terms_cc, active_cols_cc = get_active_go_terms(Y_cc, mlb_cc)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:45:49.643843Z","iopub.execute_input":"2026-01-08T03:45:49.644878Z","iopub.status.idle":"2026-01-08T03:45:49.986633Z","shell.execute_reply.started":"2026-01-08T03:45:49.644833Z","shell.execute_reply":"2026-01-08T03:45:49.985859Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nfrom goatools.obo_parser import GODag\ngo_dag = GODag(\"/kaggle/input/cafa-6-protein-function-prediction/Train/go-basic.obo\")\n\ndef apply_go_dag_consistency(y_pred, gos, go_dag):\n    \"\"\"\n    Enforce GO DAG consistency on prediction scores.\n    y_pred: np.ndarray [n_samples, n_terms]\n    gos: list of GO IDs aligned to y_pred columns\n    go_dag: GODag object from goatools\n    \"\"\"\n    # Convert gos to list if it's not already\n    gos = list(gos)\n    go_to_idx = {go: i for i, go in enumerate(gos)}\n    \n    # Sanity check\n    assert len(go_to_idx) == y_pred.shape[1], \\\n        f\"Mismatch: {len(go_to_idx)} GO terms but {y_pred.shape[1]} columns in y_pred\"\n    \n    y = y_pred.copy()\n    \n    for child_go in gos:\n        if child_go not in go_dag:\n            continue\n        child_idx = go_to_idx[child_go]\n        child_scores = y[:, child_idx]\n        \n        # Get all parent terms\n        for parent_go in go_dag[child_go].get_all_parents():\n            # Only propagate to parents that exist in our term list\n            if parent_go not in go_to_idx:\n                continue\n            \n            parent_idx = go_to_idx[parent_go]\n            y[:, parent_idx] = np.maximum(\n                y[:, parent_idx],\n                child_scores\n            )\n    return y\n\n\ndef propagate_ground_truth(y_true, gos, go_dag):\n    \"\"\"\n    Propagate ground truth labels up the GO DAG (add all ancestors).\n    This is critical for fair F-max evaluation!\n    \n    y_true: np.ndarray [n_samples, n_terms] - binary labels\n    gos: list of GO IDs aligned to y_true columns\n    go_dag: GODag object from goatools\n    \n    Returns: np.ndarray with ancestors added\n    \"\"\"\n    go_to_idx = {go: i for i, go in enumerate(gos)}\n    y_propagated = y_true.copy()\n    \n    for i in range(y_true.shape[0]):  # for each sample\n        for j, child_go in enumerate(gos):\n            if y_true[i, j] == 0:  # not annotated\n                continue\n            if child_go not in go_dag:\n                continue\n            \n            # Add all parent terms\n            for parent_go in go_dag[child_go].get_all_parents():\n                if parent_go in go_to_idx:\n                    parent_idx = go_to_idx[parent_go]\n                    y_propagated[i, parent_idx] = 1\n    \n    return y_propagated\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:46:13.951723Z","iopub.execute_input":"2026-01-08T03:46:13.952521Z","iopub.status.idle":"2026-01-08T03:46:15.107769Z","shell.execute_reply.started":"2026-01-08T03:46:13.952485Z","shell.execute_reply":"2026-01-08T03:46:15.106897Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def compute_fmax(pred, true, threshold):\n    # pred: (N, L) probabilities\n    # true: (N, L) binary labels\n\n    bin_pred = (pred >= threshold).astype(int)\n\n    tp = (bin_pred * true).sum(axis=1)\n    fp = (bin_pred * (1 - true)).sum(axis=1)\n    fn = ((1 - bin_pred) * true).sum(axis=1)\n\n    precision = tp / (tp + fp + 1e-8)\n    recall    = tp / (tp + fn + 1e-8)\n\n    f = 2 * precision * recall / (precision + recall + 1e-8)\n    return f.mean()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:46:19.255152Z","iopub.execute_input":"2026-01-08T03:46:19.256112Z","iopub.status.idle":"2026-01-08T03:46:19.261598Z","shell.execute_reply.started":"2026-01-08T03:46:19.256073Z","shell.execute_reply":"2026-01-08T03:46:19.260757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mlp_val_mf = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlp_val_mf.npy\")\nmlp_val_bp = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlp_val_bp.npy\")\nmlp_val_cc = np.load(\"/kaggle/input/cafa6-prediction-using-mlp/mlp_val_cc.npy\")\n\nlr_val_mf = np.load(\"/kaggle/input/cafa6-prediction-using-lr/lr_val_mf.npy\")\nlr_val_bp = np.load(\"/kaggle/input/cafa6-prediction-using-lr/lr_val_bp.npy\")\nlr_val_cc = np.load(\"/kaggle/input/cafa6-prediction-using-lr/lr_val_cc.npy\")\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:46:24.996767Z","iopub.execute_input":"2026-01-08T03:46:24.997634Z","iopub.status.idle":"2026-01-08T03:46:29.418219Z","shell.execute_reply.started":"2026-01-08T03:46:24.997601Z","shell.execute_reply":"2026-01-08T03:46:29.417386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"weights = np.linspace(0, 1, 21)  # 0.00, 0.05, ..., 1.00\n\nbest_w_mf, best_f_mf = None, -1\nbest_w_bp, best_f_bp = None, -1\nbest_w_cc, best_f_cc = None, -1\n\nfor w in weights:\n    # MF\n    ens_mf = w * mlp_val_mf + (1 - w) * lr_val_mf\n    f_mf = compute_fmax(ens_mf, Y_val_mf, 0.37)\n    if f_mf > best_f_mf:\n        best_f_mf, best_w_mf = f_mf, w\n\n    # BP\n    ens_bp = w * mlp_val_bp + (1 - w) * lr_val_bp\n    f_bp = compute_fmax(ens_bp, Y_val_bp, 0.24)\n    if f_bp > best_f_bp:\n        best_f_bp, best_w_bp = f_bp, w\n\n    # CC\n    ens_cc = w * mlp_val_cc + (1 - w) * lr_val_cc\n    f_cc = compute_fmax(ens_cc, Y_val_cc, 0.29)\n    if f_cc > best_f_cc:\n        best_f_cc, best_w_cc = f_cc, w\n\nprint(\"Best MF weight:\", best_w_mf, \"F-max:\", best_f_mf)\nprint(\"Best BP weight:\", best_w_bp, \"F-max:\", best_f_bp)\nprint(\"Best CC weight:\", best_w_cc, \"F-max:\", best_f_cc)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:46:34.531920Z","iopub.execute_input":"2026-01-08T03:46:34.532244Z","iopub.status.idle":"2026-01-08T03:47:18.959839Z","shell.execute_reply.started":"2026-01-08T03:46:34.532221Z","shell.execute_reply":"2026-01-08T03:47:18.959034Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ens_mf = best_w_mf * mlp_pred_mf + (1 - best_w_mf) * lr_pred_mf\nens_bp = best_w_bp * mlp_pred_bp + (1 - best_w_bp) * lr_pred_bp\nens_cc = best_w_cc * mlp_pred_cc + (1 - best_w_cc) * lr_pred_cc\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:48:34.278642Z","iopub.execute_input":"2026-01-08T03:48:34.280531Z","iopub.status.idle":"2026-01-08T03:49:01.455906Z","shell.execute_reply.started":"2026-01-08T03:48:34.280474Z","shell.execute_reply":"2026-01-08T03:49:01.454942Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ens_mf = apply_go_dag_consistency(ens_mf, go_terms_mf, go_dag)\nens_bp = apply_go_dag_consistency(ens_bp, go_terms_bp, go_dag)\nens_cc = apply_go_dag_consistency(ens_cc, go_terms_cc, go_dag)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T03:49:19.188694Z","iopub.execute_input":"2026-01-08T03:49:19.189466Z","iopub.status.idle":"2026-01-08T03:54:53.901557Z","shell.execute_reply.started":"2026-01-08T03:49:19.189434Z","shell.execute_reply":"2026-01-08T03:54:53.900452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from Bio import SeqIO\nimport pandas as pd\n\ntest_path = \"/kaggle/input/cafa-6-protein-function-prediction/Test/testsuperset.fasta\"\n\n\ntest_records = list(SeqIO.parse(test_path,\"fasta\"))\ndf_test = pd.DataFrame({\n    \"protein_id\":[rec.id.split(\" \")[0] for rec in test_records],\n    \"sequence\":[str(rec.seq) for rec in test_records],\n})\n\nt_mf, t_bp, t_cc = 0.37, 0.24, 0.29\nbin_mf = (ens_mf >= t_mf)\nbin_bp = (ens_bp >= t_bp)\nbin_cc = (ens_cc >= t_cc)\n\nrows = []\ntest_protein_ids = df_test[\"protein_id\"].tolist()\nfor i, pid in enumerate(test_protein_ids):\n    for j, term in enumerate(go_terms_mf):\n        if bin_mf[i, j]:\n            rows.append([pid, term, float(ens_mf[i, j])])\n    for j, term in enumerate(go_terms_bp):\n        if bin_bp[i, j]:\n            rows.append([pid, term, float(ens_bp[i, j])])\n    for j, term in enumerate(go_terms_cc):\n        if bin_cc[i, j]:\n            rows.append([pid, term, float(ens_cc[i, j])])\n\ndf_ens = pd.DataFrame(rows, columns=[\"ProteinID\", \"GO_term\", \"Score\"])\ndf_ens.to_csv(\"submission.tsv\", sep=\"\\t\", index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T04:00:34.216262Z","iopub.execute_input":"2026-01-08T04:00:34.216680Z","iopub.status.idle":"2026-01-08T04:06:52.826630Z","shell.execute_reply.started":"2026-01-08T04:00:34.216650Z","shell.execute_reply":"2026-01-08T04:06:52.825663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_ens.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-08T04:08:36.160285Z","iopub.execute_input":"2026-01-08T04:08:36.160708Z","iopub.status.idle":"2026-01-08T04:08:36.192242Z","shell.execute_reply.started":"2026-01-08T04:08:36.160676Z","shell.execute_reply":"2026-01-08T04:08:36.191366Z"}},"outputs":[],"execution_count":null}]}