{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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":"2025-12-12T09:34:23.535330Z","iopub.execute_input":"2025-12-12T09:34:23.535706Z","iopub.status.idle":"2025-12-12T09:34:23.546425Z","shell.execute_reply.started":"2025-12-12T09:34:23.535684Z","shell.execute_reply":"2025-12-12T09:34:23.545549Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install biopython > dev null\n!pip install obonet > dev null","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T21:47:32.680096Z","iopub.execute_input":"2025-12-13T21:47:32.680369Z","iopub.status.idle":"2025-12-13T21:47:41.461100Z","shell.execute_reply.started":"2025-12-13T21:47:32.680351Z","shell.execute_reply":"2025-12-13T21:47:41.460291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom Bio import SeqIO\nfrom tqdm.auto import tqdm\nfrom collections import Counter\nimport networkx as nx\n\n# Visualization\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport matplotlib.patches as mpatches\nfrom matplotlib.gridspec import GridSpec\n\n# Modeling\nfrom sklearn.preprocessing import MultiLabelBinarizer\nfrom sklearn.multiclass import OneVsRestClassifier\nfrom sklearn.linear_model import LogisticRegression\n\nimport warnings\nimport obonet\nimport gc\n\nwarnings.filterwarnings('ignore')\npd.set_option('display.max_columns', None)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T21:48:51.144655Z","iopub.execute_input":"2025-12-13T21:48:51.145133Z","iopub.status.idle":"2025-12-13T21:48:51.150195Z","shell.execute_reply.started":"2025-12-13T21:48:51.145111Z","shell.execute_reply":"2025-12-13T21:48:51.149546Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class config:\n    # File Paths\n    TRAIN_TERMS_PATH = '/kaggle/input/cafa-6-protein-function-prediction/Train/train_terms.tsv'\n    TRAIN_SEQUENCES_PATH = '/kaggle/input/cafa-6-protein-function-prediction/Train/train_sequences.fasta'\n    TRAIN_TAXONOMY_PATH = '/kaggle/input/cafa-6-protein-function-prediction/Train/train_taxonomy.tsv'\n    TEST_SEQUENCES_PATH = '/kaggle/input/cafa-6-protein-function-prediction/Test/testsuperset.fasta'\n    IA_PATH = '/kaggle/input/cafa-6-protein-function-prediction/IA.tsv'\n    \n    # EDA & Plotting\n    COLORS = ['#221f1f', '#b20710', '#e50914', 'grey']\n    BACKGROUND_COLOR = '#f5f6f6'\n    \n    # Modeling\n    PROBABILITY_THRESHOLD = 0.02","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T21:49:35.635912Z","iopub.execute_input":"2025-12-13T21:49:35.636493Z","iopub.status.idle":"2025-12-13T21:49:35.640564Z","shell.execute_reply.started":"2025-12-13T21:49:35.636469Z","shell.execute_reply":"2025-12-13T21:49:35.639729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Loading tabular datasets...\")\ntrain_terms_df = pd.read_csv(config.TRAIN_TERMS_PATH, sep='\\t')\ntrain_taxonomy_df = pd.read_csv(config.TRAIN_TAXONOMY_PATH, sep='\\t', header=None, names=['EntryID', 'taxonomyID'])\nia_df = pd.read_csv(config.IA_PATH, sep='\\t', names=['term', 'ia'])\n\ndef load_fasta_to_dataframe(file_path, is_train=True):\n    records = []\n    parser = SeqIO.parse(file_path, \"fasta\")\n    for record in tqdm(parser, desc=f\"Parsing {file_path.split('/')[-1]}\"):\n        entry_id = record.id.split('|')[1] if is_train and '|' in record.id else record.id.split()[0]\n        records.append({'EntryID': entry_id, 'sequence': str(record.seq)})\n    return pd.DataFrame(records)\n\nprint(\"\\nLoading sequence datasets...\")\ntrain_sequences_df = load_fasta_to_dataframe(config.TRAIN_SEQUENCES_PATH, is_train=True)\n\nprint(\"Consolidating data into a master dataframe for EDA...\")\nprotein_labels = train_terms_df.groupby('EntryID')['term'].apply(list).reset_index(name='labels')\ntrain_df_eda = pd.merge(train_sequences_df, train_taxonomy_df, on='EntryID', how='left')\ntrain_df_eda = pd.merge(train_df_eda, protein_labels, on='EntryID', how='inner')\ntrain_df_eda['seq_length'] = train_df_eda['sequence'].str.len()\ntrain_df_eda['num_labels'] = train_df_eda['labels'].str.len()\nprint(\"Data loading complete.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T21:52:04.610936Z","iopub.execute_input":"2025-12-13T21:52:04.611234Z","iopub.status.idle":"2025-12-13T21:52:06.893735Z","shell.execute_reply.started":"2025-12-13T21:52:04.611214Z","shell.execute_reply":"2025-12-13T21:52:06.893017Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_sequence_features(seq):\n    if not seq or len(seq) == 0: return np.zeros(85)\n    aa_counts = Counter(seq)\n    length = len(seq)\n    aa_freq = np.array([aa_counts.get(aa, 0) / length for aa in 'ACDEFGHIKLMNPQRSTVWY'])\n    hydrophobic = sum(aa_counts.get(aa, 0) for aa in 'AILMFWYV') / length\n    charged = sum(aa_counts.get(aa, 0) for aa in 'DEKR') / length\n    aa_weights = {'A': 89, 'C': 121, 'D': 133, 'E': 147, 'F': 165, 'G': 75, 'H': 155, 'I': 131, 'K': 146, 'L': 131, 'M': 149, 'N': 132, 'P': 115, 'Q': 146, 'R': 174, 'S': 105, 'T': 119, 'V': 117, 'W': 204, 'Y': 181}\n    mol_weight = sum(aa_counts.get(aa, 0) * aa_weights.get(aa, 0) for aa in aa_counts)\n    groups = {'hydrophobic': set('AILMFWYV'),'polar': set('STNQ'),'positive': set('RK'),'negative': set('DE'),'aromatic': set('FWY'),'aliphatic': set('ILV'),'small': set('ACDGNPSTV')}\n    ctd_features = [sum(1 for aa in seq if aa in group_aas) / length for group_aas in groups.values()]\n    dipeptides = Counter([seq[i:i+2] for i in range(length - 1)])\n    top_dipeptides = ['AL', 'LA', 'LE', 'EA', 'AA', 'AS', 'SA', 'EL', 'LL', 'AE', 'SE', 'ES', 'GA', 'AG', 'VA', 'AV', 'LV', 'VL', 'LS', 'SL']\n    dipeptide_freq = np.array([dipeptides.get(dp, 0) / (length-1 if length > 1 else 1) for dp in top_dipeptides])\n    tripeptides = Counter([seq[i:i+3] for i in range(length - 2)])\n    top_tripeptides = ['ALA', 'LEA', 'EAL', 'LAL', 'AAA', 'LLE', 'ELE', 'ALE', 'GAL', 'ASA', 'VLA', 'LAV', 'SLS', 'LSL', 'GLA', 'LAG', 'AVL', 'VLA', 'SLE', 'LES']\n    tripeptide_freq = np.array([tripeptides.get(tp, 0) / (length-2 if length > 2 else 1) for tp in top_tripeptides])\n    return np.concatenate([aa_freq, np.array([np.log1p(length), hydrophobic, charged, np.log1p(mol_weight)]), np.array(ctd_features), dipeptide_freq, tripeptide_freq])\n\nprint(\"Extracting features for all training proteins...\")\n\nX_train_list, y_train_proteins = [], []\nprotein_ids_in_scope = set(train_terms_df['EntryID'].unique())\ntrain_sequences_dict = dict(zip(train_sequences_df['EntryID'], train_sequences_df['sequence']))\nfor pid, seq in tqdm(train_sequences_dict.items(), desc=\"Processing Train Sequences\"):\n    if pid in protein_ids_in_scope:\n        X_train_list.append(extract_sequence_features(seq))\n        y_train_proteins.append(pid)\nX_train = np.array(X_train_list)\nprint(f\"Training feature matrix shape: {X_train.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T21:54:29.504915Z","iopub.execute_input":"2025-12-13T21:54:29.505221Z","iopub.status.idle":"2025-12-13T21:55:03.181849Z","shell.execute_reply.started":"2025-12-13T21:54:29.505201Z","shell.execute_reply":"2025-12-13T21:55:03.180890Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Preparing labels and training models for each ontology...\")\nmodels, mlb_dict = {}, {}\n\n# CORRECT MAPPING of ontology name to the code used in the dataframe\nontology_map = {'BPO': 'P', 'CCO': 'C', 'MFO': 'F'}\n\nfor aspect_name, aspect_code in ontology_map.items():\n    print(f\"\\n--- Processing {aspect_name} ({aspect_code}) ---\")\n    \n    # Filter terms for the current ontology using the CORRECT code\n    ont_terms_df = train_terms_df[train_terms_df['aspect'] == aspect_code]\n    \n    # Continue if no terms are found for this ontology\n    if ont_terms_df.empty:\n        print(f\"No terms found for {aspect_name}. Skipping model training.\")\n        continue\n        \n    protein_terms = ont_terms_df.groupby('EntryID')['term'].apply(list).to_dict()\n    labels_list = [protein_terms.get(pid, []) for pid in y_train_proteins]\n    \n    mlb = MultiLabelBinarizer(sparse_output=True)\n    y_train_ont = mlb.fit_transform(labels_list)\n    \n    print(f\"Number of unique {aspect_name} terms: {y_train_ont.shape[1]}\")\n    \n    # Robustness check: only train if there are labels\n    if y_train_ont.shape[1] > 0:\n        mlb_dict[aspect_code] = mlb\n        model = OneVsRestClassifier(LogisticRegression(max_iter=500, solver='lbfgs', C=0.5, random_state=42), n_jobs=-1)\n        model.fit(X_train, y_train_ont)\n        models[aspect_code] = model\n        print(f\"Model for {aspect_name} trained successfully.\")\n    else:\n        print(f\"Skipping model training for {aspect_name} as no labels were binarized.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T21:57:56.170801Z","iopub.execute_input":"2025-12-13T21:57:56.171520Z","iopub.status.idle":"2025-12-13T23:12:22.842770Z","shell.execute_reply.started":"2025-12-13T21:57:56.171492Z","shell.execute_reply":"2025-12-13T23:12:22.841952Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\nLoading and processing test sequences for submission...\")\ntest_sequences_df = load_fasta_to_dataframe(config.TEST_SEQUENCES_PATH, is_train=False)\ntest_protein_ids = test_sequences_df['EntryID'].tolist()\ntest_sequences_dict = dict(zip(test_sequences_df['EntryID'], test_sequences_df['sequence']))\nBATCH_SIZE = 5000\nsubmission_list = []\n\nfor i in tqdm(range(0, len(test_protein_ids), BATCH_SIZE), desc=\"Predicting on Test Set\"):\n    batch_pids = test_protein_ids[i:i+BATCH_SIZE]\n    batch_seqs = [test_sequences_dict[pid] for pid in batch_pids]\n    X_batch = np.array([extract_sequence_features(seq) for seq in batch_seqs])\n    for aspect_code, model in models.items():\n        mlb = mlb_dict[aspect_code]\n        y_pred_proba = model.predict_proba(X_batch)\n        for j, pid in enumerate(batch_pids):\n            probs = y_pred_proba[j]\n            candidate_indices = np.where(probs > config.PROBABILITY_THRESHOLD)[0]\n            for idx in candidate_indices:\n                submission_list.append((pid, mlb.classes_[idx], probs[idx]))\n\nprint(f\"Generated {len(submission_list):,} total predictions.\")\nsubmission_df = pd.DataFrame(submission_list, columns=['Protein Id', 'GO Term Id', 'Prediction'])\nprint(\"Applying 1500 prediction limit per protein...\")\nsubmission_df = submission_df.sort_values(by=['Protein Id', 'Prediction'], ascending=[True, False])\nfinal_submission_df = submission_df.groupby('Protein Id').head(1500).reset_index(drop=True)\nfinal_submission_df.to_csv('submission.tsv', sep='\\t', index=False, header=False)\n\nprint(\"\\nSubmission file 'submission.tsv' created successfully.\")\nprint(f\"Total predictions in final submission: {len(final_submission_df):,}\")\nprint(\"Submission DataFrame Head:\")\ndisplay(final_submission_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T23:21:42.340376Z","iopub.execute_input":"2025-12-13T23:21:42.340950Z","iopub.status.idle":"2025-12-13T23:35:21.037147Z","shell.execute_reply.started":"2025-12-13T23:21:42.340924Z","shell.execute_reply":"2025-12-13T23:35:21.036576Z"}},"outputs":[],"execution_count":null}]}