{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"},{"sourceId":5499219,"sourceType":"datasetVersion","datasetId":3167603},{"sourceId":5549164,"sourceType":"datasetVersion","datasetId":3197305},{"sourceId":5607816,"sourceType":"datasetVersion","datasetId":3225525},{"sourceId":5792099,"sourceType":"datasetVersion","datasetId":3327296},{"sourceId":6180004,"sourceType":"datasetVersion","datasetId":3546496},{"sourceId":13554858,"sourceType":"datasetVersion","datasetId":8609388},{"sourceId":14057766,"sourceType":"datasetVersion","datasetId":8947625},{"sourceId":14060435,"sourceType":"datasetVersion","datasetId":8949155},{"sourceId":270597770,"sourceType":"kernelVersion"}],"dockerImageVersionId":30527,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"forked from https://www.kaggle.com/code/siddhvr/cafa5-using-protbert-embeds\n\njust to be noted:\n\n- this is the same notebook which was submitted in CAFA5 for demonstration purpose (not much changes have been made)\n- it utilizes Protbert model finetuned on CAFA5 dataset (try training the model on CAFA6)\n- submission utilizes ensemble for cafa5 notebook (scroll all the way down to check)\n\n","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nsub = pd.read_csv(\"/kaggle/input/cafa-6-protein-function-prediction/sample_submission.tsv\", sep= \"\\t\", on_bad_lines='skip', header = None)\nsub.columns = [\"The Protein ID\", \"The Gene Ontology term (GO) ID\", \"Predicted link probability that GO appear in Protein\"]\nsub.head(5)","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:27:11.996045Z","iopub.execute_input":"2023-06-10T12:27:11.996589Z","iopub.status.idle":"2023-06-10T12:27:12.219293Z","shell.execute_reply.started":"2023-06-10T12:27:11.996555Z","shell.execute_reply":"2023-06-10T12:27:12.218403Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"MAIN_DIR = \"/kaggle/input/cafa-6-protein-function-prediction\"\n\n# UTILITARIES\nimport numpy as np\nfrom tqdm import tqdm\nimport time\nimport matplotlib.pyplot as plt\nplt.style.use('ggplot')\n\n# TORCH MODULES FOR METRICS COMPUTATION :\nimport torch\nfrom torch.utils.data import Dataset\nfrom torch import nn\nfrom torch.utils.data import random_split\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom torchmetrics.classification import MultilabelF1Score\nfrom torchmetrics.classification import MultilabelAccuracy\n\nimport pytorch_lightning as pl\nfrom pytorch_lightning import Trainer\nfrom pytorch_lightning.loggers import WandbLogger\n\n# WANDB FOR LIGHTNING :\nimport wandb\n\n# FILES VISUALIZATION\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-06-10T12:28:25.773695Z","iopub.execute_input":"2023-06-10T12:28:25.774061Z","iopub.status.idle":"2023-06-10T12:28:37.88911Z","shell.execute_reply.started":"2023-06-10T12:28:25.774031Z","shell.execute_reply":"2023-06-10T12:28:37.8881Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class config:\n    train_sequences_path = MAIN_DIR  + \"/Train/train_sequences.fasta\"\n    train_labels_path = MAIN_DIR + \"/Train/train_terms.tsv\"\n    test_sequences_path = MAIN_DIR + \"/Test/testsuperset.fasta\"\n    \n    num_labels = 500\n    n_epochs = 8\n    batch_size = 128\n    lr = 0.001\n    \n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:32:46.999405Z","iopub.execute_input":"2023-06-10T12:32:46.99998Z","iopub.status.idle":"2023-06-10T12:32:47.018051Z","shell.execute_reply.started":"2023-06-10T12:32:46.999936Z","shell.execute_reply":"2023-06-10T12:32:47.014509Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(config.device)","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:28:37.907778Z","iopub.execute_input":"2023-06-10T12:28:37.908444Z","iopub.status.idle":"2023-06-10T12:28:37.913751Z","shell.execute_reply.started":"2023-06-10T12:28:37.908412Z","shell.execute_reply":"2023-06-10T12:28:37.912807Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Directories for the different embedding vectors : \nembeds_map = {\n    \"T5\" : \"t5embeds\",\n    \"ProtBERT\" : \"protbert-embeddings-for-cafa6\",\n    \"EMS2\" : \"cafa-5-ems-2-embeddings-numpy\"\n}\n\n# Length of the different embedding vectors :\nembeds_dim = {\n    \"T5\" : 1024,\n    \"ProtBERT\" : 1024,\n    \"EMS2\" : 1280\n}","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:28:37.948982Z","iopub.execute_input":"2023-06-10T12:28:37.949523Z","iopub.status.idle":"2023-06-10T12:28:37.954513Z","shell.execute_reply.started":"2023-06-10T12:28:37.949493Z","shell.execute_reply":"2023-06-10T12:28:37.953508Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# This PyTorch Dataset class is responsible for loading the protein embeddings and \n# their corresponding labels. It handles both training data (embeddings + labels) \n# and test data (embeddings + protein IDs).\nclass ProteinSequenceDataset(Dataset):\n    def __init__(self, datatype, embeddings_source):\n        super(ProteinSequenceDataset).__init__()\n        self.datatype = datatype\n        \n        base_path = f\"/kaggle/input/{embeds_map[embeddings_source]}/\"\n        \n        # Construct paths correctly\n        embeds_path = os.path.join(base_path, f\"{datatype}_embeddings.npy\")\n        ids_path = os.path.join(base_path, f\"{datatype}_ids.npy\")\n        \n        # Handle special filenames for T5\n        if embeddings_source == \"T5\":\n            embeds_path = os.path.join(base_path, f\"{datatype}_embeds.npy\")\n\n        embeds = np.load(embeds_path)\n        ids = np.load(ids_path)\n            \n        # Optimized DataFrame creation\n        self.df = pd.DataFrame({\"EntryID\": ids, \"embed\": list(embeds)})\n        \n        if datatype==\"train\":\n            labels_path = \"/kaggle/input/train-targets-top\"+str(config.num_labels) +\\\n                \"/train_targets_top\"+str(config.num_labels)+\".npy\"\n            np_labels = np.load(labels_path)\n            \n            # Merge labels into the DataFrame\n            labels_df = pd.DataFrame({\"EntryID\": self.df['EntryID'], \"labels_vect\": list(np_labels)})\n            self.df = pd.merge(self.df, labels_df, on=\"EntryID\")\n            \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        embed = torch.tensor(self.df.iloc[index][\"embed\"], dtype=torch.float32)\n        if self.datatype == \"train\":\n            targets = torch.tensor(self.df.iloc[index][\"labels_vect\"], dtype=torch.float32)\n            return embed, targets\n        else: # datatype == \"test\"\n            protein_id = self.df.iloc[index][\"EntryID\"]\n            return embed, protein_id","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:28:40.86426Z","iopub.execute_input":"2023-06-10T12:28:40.864637Z","iopub.status.idle":"2023-06-10T12:28:40.878496Z","shell.execute_reply.started":"2023-06-10T12:28:40.864607Z","shell.execute_reply":"2023-06-10T12:28:40.876685Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## MODEL ARCHITECTURE BUILDING AND TRAINING","metadata":{}},{"cell_type":"code","source":"class MultiLayerPerceptron(torch.nn.Module):\n    def __init__(self, input_dim, num_classes):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(input_dim, 1024),\n            nn.BatchNorm1d(1024),\n            nn.ReLU(),\n            nn.Dropout(0.5),\n\n            nn.Linear(1024, 512),\n            nn.BatchNorm1d(512),\n            nn.ReLU(),\n            nn.Dropout(0.4),\n\n            nn.Linear(512, num_classes)\n        )\n\n    def forward(self, x):\n        return self.net(x)\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:30:31.424178Z","iopub.execute_input":"2023-06-10T12:30:31.424556Z","iopub.status.idle":"2023-06-10T12:30:31.432271Z","shell.execute_reply.started":"2023-06-10T12:30:31.424524Z","shell.execute_reply":"2023-06-10T12:30:31.431322Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# - `Conv1d` layers for feature extraction.\n# - `BatchNorm1d` to stabilize and speed up training.\n# - `ReLU` as the activation function.\n# - `MaxPool1d` for down-sampling.\n# - `Dropout` in the final fully-connected block to prevent overfitting.\nclass CNN1D(nn.Module):\n    def __init__(self, input_dim, num_classes):\n        super(CNN1D, self).__init__()\n        self.conv_block1 = nn.Sequential(\n            nn.Conv1d(in_channels=1, out_channels=32, kernel_size=5, padding=2),\n            nn.BatchNorm1d(32),\n            nn.ReLU(),\n            nn.MaxPool1d(kernel_size=2, stride=2)\n        )\n        self.conv_block2 = nn.Sequential(\n            nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3, padding=1),\n            nn.BatchNorm1d(64),\n            nn.ReLU(),\n            nn.MaxPool1d(kernel_size=2, stride=2)\n        )\n        \n        flattened_size = int(64 * input_dim / 4)\n        \n        self.fc_block = nn.Sequential(\n            nn.Linear(in_features=flattened_size, out_features=1024),\n            nn.ReLU(),\n            nn.Dropout(p=0.4), # Added Dropout to prevent overfitting\n            nn.Linear(in_features=1024, out_features=num_classes)\n        )\n\n    def forward(self, x):\n        # (batch_size, embed_size) -> (batch_size, 1, embed_size)\n        x = x.unsqueeze(1) \n        x = self.conv_block1(x)\n        x = self.conv_block2(x)\n        x = torch.flatten(x, 1)\n        x = self.fc_block(x)\n        return x\n","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:28:44.752653Z","iopub.execute_input":"2023-06-10T12:28:44.752992Z","iopub.status.idle":"2023-06-10T12:28:44.764379Z","shell.execute_reply.started":"2023-06-10T12:28:44.752966Z","shell.execute_reply":"2023-06-10T12:28:44.763364Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(embeddings_source, model_type=\"linear\", train_size=0.9):\n    \n    train_dataset = ProteinSequenceDataset(datatype=\"train\", embeddings_source = embeddings_source)\n    \n    train_set, val_set = random_split(train_dataset, lengths = [int(len(train_dataset)*train_size), len(train_dataset)-int(len(train_dataset)*train_size)])\n    train_dataloader = torch.utils.data.DataLoader(train_set, batch_size=config.batch_size, shuffle=True)\n    val_dataloader = torch.utils.data.DataLoader(val_set, batch_size=config.batch_size, shuffle=True)\n\n    if model_type == \"linear\":\n        model = MultiLayerPerceptron(input_dim=embeds_dim[embeddings_source], num_classes=config.num_labels).to(config.device)\n    if model_type == \"convolutional\":\n        model = CNN1D(input_dim=embeds_dim[embeddings_source], num_classes=config.num_labels).to(config.device)\n\n    optimizer = torch.optim.Adam(model.parameters(), lr = config.lr)\n    scheduler = ReduceLROnPlateau(optimizer, mode='max', factor=0.1, patience=1, verbose=True)\n    loss_fn = torch.nn.BCEWithLogitsLoss()\n    f1_metric = MultilabelF1Score(num_labels=config.num_labels, average='micro').to(config.device)\n    n_epochs = config.n_epochs\n\n    print(\"STARTING TRAINING...\")\n\n    best_val_f1 = 0.0\n    best_threshold = 0.5\n    \n    for epoch in range(config.n_epochs):\n        print(f\"\\nEPOCH {epoch+1}/{config.n_epochs}\")\n    \n        # =====================\n        # TRAIN PHASE\n        # =====================\n        model.train()\n        total_train_loss = 0.0\n    \n        for embed, targets in tqdm(train_dataloader, desc=\"Training\"):\n            embed = embed.to(config.device)\n            targets = targets.to(config.device)\n    \n            optimizer.zero_grad()\n    \n            logits = model(embed)                    # (B, num_labels)\n            loss = loss_fn(logits, targets)\n    \n            loss.backward()\n            optimizer.step()\n    \n            total_train_loss += loss.item()\n    \n        avg_train_loss = total_train_loss / len(train_dataloader)\n        print(f\"Average Training Loss: {avg_train_loss:.4f}\")\n    \n        # =====================\n        # VALIDATION PHASE\n        # =====================\n        model.eval()\n        all_val_probs = []\n        all_val_targets = []\n    \n        with torch.no_grad():\n            for embed, targets in tqdm(val_dataloader, desc=\"Validation\"):\n                embed = embed.to(config.device)\n                targets = targets.to(config.device)\n    \n                logits = model(embed)\n                probs = torch.sigmoid(logits)\n    \n                all_val_probs.append(probs)\n                all_val_targets.append(targets)\n    \n        all_val_probs = torch.cat(all_val_probs, dim=0)\n        all_val_targets = torch.cat(all_val_targets, dim=0)\n    \n        # =====================\n        # THRESHOLD SEARCH\n        # =====================\n        best_f1_epoch = 0.0\n        best_thresh_epoch = 0.0\n    \n        for thresh in np.arange(0.01, 0.51, 0.01):\n            preds_bin = (all_val_probs >= thresh).int()\n            f1 = f1_metric(preds_bin, all_val_targets.int())\n    \n            if f1 > best_f1_epoch:\n                best_f1_epoch = f1\n                best_thresh_epoch = thresh\n    \n        print(\n            f\"Validation F1: {best_f1_epoch:.4f} \"\n            f\"(best threshold = {best_thresh_epoch:.2f})\"\n        )\n    \n        # =====================\n        # LR SCHEDULER\n        # =====================\n        scheduler.step(best_f1_epoch)\n    \n        # =====================\n        # SAVE BEST MODEL\n        # =====================\n        if best_f1_epoch > best_val_f1:\n            best_val_f1 = best_f1_epoch\n            best_threshold = best_thresh_epoch\n            torch.save(model.state_dict(), \"best_model.pth\")\n            print(f\"✅ New best model saved! F1 = {best_val_f1:.4f}\")\n    \n    print(\"\\nTRAINING FINISHED\")\n    print(f\"Best Validation F1: {best_val_f1:.4f}\")\n    print(f\"Best Threshold: {best_threshold:.2f}\")\n    \n    # Load best model\n    model.load_state_dict(torch.load(\"best_model.pth\"))\n    \n    return model, best_threshold","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:30:40.624244Z","iopub.execute_input":"2023-06-10T12:30:40.62462Z","iopub.status.idle":"2023-06-10T12:30:40.643168Z","shell.execute_reply.started":"2023-06-10T12:30:40.624591Z","shell.execute_reply":"2023-06-10T12:30:40.64054Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#ems2_model, ems2_losses, ems2_scores = train_model(embeddings_source=\"EMS2\",model_type=\"convolutional\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model_train, best_threshold = train_model(embeddings_source=\"EMS2\", model_type=\"linear\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#t5_model, t5_losses, t5_scores = train_model(embeddings_source=\"T5\",model_type=\"linear\")","metadata":{"execution":{"iopub.status.busy":"2023-06-10T12:32:53.236496Z","iopub.execute_input":"2023-06-10T12:32:53.23685Z","iopub.status.idle":"2023-06-10T12:38:49.02523Z","shell.execute_reply.started":"2023-06-10T12:32:53.236821Z","shell.execute_reply":"2023-06-10T12:38:49.024184Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#protbert_model, protbert_losses, protbert_scores = train_model(embeddings_source=\"ProtBERT\",model_type=\"linear\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"plt.figure(figsize = (10, 4))\n#plt.plot(ems2_losses[\"val\"], label = \"EMS2\")\n#plt.plot(t5_losses[\"val\"], label = \"T5\")\n#plt.plot(protbert_losses[\"val\"], label = \"ProtBERT\") \nplt.title(\"Validation Losses for # Vector Embeddings\")\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Average Loss\")\nplt.legend()\nplt.show()\n\nplt.figure(figsize = (10, 4))\n#plt.plot(ems2_scores[\"val\"], label = \"EMS2\")\n#plt.plot(t5_scores[\"val\"], label = \"T5\")\n#plt.plot(protbert_scores[\"val\"], label = \"ProtBERT\")\nplt.title(\"Validation F1-Scores for # Vector Embeddings\")\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Average F1-Score\")\nplt.legend()\nplt.show()","metadata":{}},{"cell_type":"markdown","source":"## PREDICTION","metadata":{}},{"cell_type":"code","source":"import os, gc, math, random, sys, time\nfrom collections import defaultdict, Counter\nfrom typing import List, Dict, Tuple, Set\nimport numpy as np\nimport pandas as pd\nfrom sklearn.preprocessing import MultiLabelBinarizer\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models, callbacks, optimizers, losses, metrics\nimport torch\nimport numpy as np\nimport gc\nfrom pathlib import Path","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def parse_obo(go_obo_path: str) -> Tuple[Dict[str, Set[str]], Dict[str, Set[str]]]:\n    parents = defaultdict(set); children = defaultdict(set)\n    if not os.path.exists(go_obo_path): return parents, children\n    with open(go_obo_path,\"r\") as f:\n        cur_id=None\n        for line in f:\n            line=line.strip()\n            if line==\"[Term]\": cur_id=None\n            elif line.startswith(\"id: \"): cur_id=line.split(\"id: \")[1].strip()\n            elif line.startswith(\"is_a: \"):\n                pid=line.split()[1].strip()\n                if cur_id: parents[cur_id].add(pid); children[pid].add(cur_id)\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: parents[cur_id].add(pid); children[pid].add(cur_id)\n    print(f\"[io] Parsed OBO: {len(parents)} nodes with parents\")\n    return parents, children","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"GO_OBO_PATH = \"/kaggle/input/cafa-6-protein-function-prediction/Train/go-basic.obo\"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"parents_map, _ = parse_obo(GO_OBO_PATH)\nancestor_cache = {}","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_ancestors_cached(go_id, parents):\n    if go_id in ancestor_cache:\n        return ancestor_cache[go_id]\n\n    ans = set()\n    stack = [go_id]\n    while stack:\n        cur = stack.pop()\n        for p in parents.get(cur, []):\n            if p not in ans:\n                ans.add(p)\n                stack.append(p)\n\n    ancestor_cache[go_id] = ans\n    return ans\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(model, embeddings_source, threshold, parents_map, top_k=200):\n    test_dataset = ProteinSequenceDataset(\n        datatype=\"test\",\n        embeddings_source=embeddings_source\n    )\n    test_dataloader = torch.utils.data.DataLoader(\n        test_dataset,\n        batch_size=config.batch_size,\n        shuffle=False\n    )\n\n    model.eval()\n\n    # Load GO label names (top-N như khi train)\n    labels_df = pd.read_csv(\n        os.path.join(MAIN_DIR, \"Train/train_terms.tsv\"),\n        sep=\"\\t\"\n    )\n    top_terms = (\n        labels_df.groupby(\"term\")[\"EntryID\"]\n        .count()\n        .sort_values(ascending=False)\n    )\n    labels_names = top_terms.head(config.num_labels).index.values\n\n    print(\"\\nGENERATING PREDICTIONS FOR THE TEST SET...\")\n\n    rows = []\n\n    with torch.no_grad():\n        for embed, ids in tqdm(test_dataloader, desc=\"Predicting\"):\n            embed = embed.to(config.device)\n            probs = torch.nn.functional.sigmoid(model(embed)).cpu().numpy()\n\n            for i, protein_id in enumerate(ids):\n                protein_probs = probs[i]\n\n                # --- 1. Threshold ---\n                idxs = np.where(protein_probs > threshold)[0]\n                if len(idxs) == 0:\n                    continue\n\n                # --- 2. Collect + propagate ---\n                best_score = {}\n\n                for idx in idxs:\n                    go = labels_names[idx]\n                    s = float(protein_probs[idx])\n\n                    # original term\n                    best_score[go] = max(best_score.get(go, 0.0), s)\n\n                    # ancestors\n                    for anc in get_ancestors_cached(go, parents_map):\n                        best_score[anc] = max(best_score.get(anc, 0.0), s)\n\n                # --- 3. Top-K per protein ---\n                sorted_items = sorted(\n                    best_score.items(),\n                    key=lambda x: x[1],\n                    reverse=True\n                )[:top_k]\n\n                for go, s in sorted_items:\n                    rows.append({\n                        \"Id\": protein_id,\n                        \"GO term\": go,\n                        \"Confidence\": s\n                    })\n\n    submission_df = pd.DataFrame(rows)\n    print(\"PREDICTIONS COMPLETE.\")\n    return submission_df\n\n\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = predict(model_train, \"EMS2\", best_threshold,parents_map)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(submission_df)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<h4> SUBMISSION</h4>\nYou may submit this file as is.","metadata":{}},{"cell_type":"code","source":"submission_df.to_csv('submission.tsv', sep='\\t', header=False, index=False)","metadata":{},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<h4> or ensemble with some other public submission","metadata":{}},{"cell_type":"markdown","source":"#this submission was obtained by training models on BlstP and Foldseek offline\nsubmission2 = pd.read_csv('/kaggle/input/cafa-0322/submission.tsv',\n    sep='\\t', header=None, names=['Id2', 'GO term2', 'Confidence2']) ","metadata":{}},{"cell_type":"markdown","source":"submissions = submission2.merge(submission_df, left_on=['Id2', 'GO term2'], \n                                                  right_on=['Id', 'GO term'], how='outer')","metadata":{}},{"cell_type":"markdown","source":"submissions.drop(['Id', 'GO term'], axis=1, inplace=True)\nsubmissions['confidence_combined'] = submissions.apply(lambda row: row['Confidence2'] if not np.isnan(row['Confidence2']) else row['Confidence'], axis=1)","metadata":{}},{"cell_type":"markdown","source":"submissions[['Id2', 'GO term2', 'confidence_combined']].to_csv('submission.tsv', sep='\\t', header=False, index=False)","metadata":{}}]}