{"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":116062,"databundleVersionId":14875579}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# Cell 1 — Install everything needed\n!pip install obonet -q\n!pip install networkx -q\n\nprint(\"Libraries installed.\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:40:05.856369Z","iopub.execute_input":"2026-03-29T11:40:05.856677Z","iopub.status.idle":"2026-03-29T11:40:14.417241Z","shell.execute_reply.started":"2026-03-29T11:40:05.856650Z","shell.execute_reply":"2026-03-29T11:40:14.416016Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 2 — Imports\nimport pandas as pd\nimport numpy as np\nimport obonet\nimport networkx as nx\nfrom collections import defaultdict\nimport os\n\n# Check dataset files are available\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:40:14.419385Z","iopub.execute_input":"2026-03-29T11:40:14.419958Z","iopub.status.idle":"2026-03-29T11:40:15.096373Z","shell.execute_reply.started":"2026-03-29T11:40:14.419922Z","shell.execute_reply":"2026-03-29T11:40:15.094925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 3 — Load training GO term labels (corrected path)\ntrain_terms = pd.read_csv(\n    '/kaggle/input/competitions/cafa-6-protein-function-prediction/Train/train_terms.tsv',\n    sep='\\t',\n    names=['protein_id', 'go_term', 'ontology']\n)\n\nprint(\"Shape:\", train_terms.shape)\nprint(\"\\nFirst 5 rows:\")\nprint(train_terms.head())\nprint(\"\\nOntology breakdown (MF/BP/CC split):\")\nprint(train_terms['ontology'].value_counts())\nprint(\"\\nUnique proteins:\", train_terms['protein_id'].nunique())\nprint(\"Unique GO terms:\", train_terms['go_term'].nunique())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:42:00.964996Z","iopub.execute_input":"2026-03-29T11:42:00.965306Z","iopub.status.idle":"2026-03-29T11:42:01.582370Z","shell.execute_reply.started":"2026-03-29T11:42:00.965262Z","shell.execute_reply":"2026-03-29T11:42:01.581455Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 4 — Clean labels and load training sequences\n\n# Remove the header row that got read as data\ntrain_terms = train_terms[train_terms['protein_id'] != 'EntryID'].reset_index(drop=True)\nprint(\"Cleaned shape:\", train_terms.shape)\n\n# Load training protein sequences from FASTA file\ndef read_fasta(filepath):\n    \"\"\"Read a FASTA file and return a dict of {protein_id: sequence}\"\"\"\n    sequences = {}\n    current_id = None\n    current_seq = []\n    \n    with open(filepath, 'r') as f:\n        for line in f:\n            line = line.strip()\n            if line.startswith('>'):\n                # Save previous protein\n                if current_id is not None:\n                    sequences[current_id] = ''.join(current_seq)\n                # Start new protein — ID is the word after >\n                current_id = line[1:].split()[0]\n                current_seq = []\n            else:\n                current_seq.append(line)\n        # Save last protein\n        if current_id is not None:\n            sequences[current_id] = ''.join(current_seq)\n    \n    return sequences\n\n# Load training sequences\nprint(\"\\nLoading training sequences...\")\ntrain_seqs = read_fasta(\n    '/kaggle/input/competitions/cafa-6-protein-function-prediction/Train/train_sequences.fasta'\n)\nprint(f\"Training proteins loaded: {len(train_seqs)}\")\n\n# Show a sample sequence\nsample_id = list(train_seqs.keys())[0]\nprint(f\"\\nSample protein ID: {sample_id}\")\nprint(f\"Sample sequence (first 80 chars): {train_seqs[sample_id][:80]}\")\nprint(f\"Sample sequence length: {len(train_seqs[sample_id])}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:42:34.828918Z","iopub.execute_input":"2026-03-29T11:42:34.829687Z","iopub.status.idle":"2026-03-29T11:42:35.918507Z","shell.execute_reply.started":"2026-03-29T11:42:34.829648Z","shell.execute_reply":"2026-03-29T11:42:35.917606Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 5 — Fix IDs and load test sequences\n\n# Fix training sequence IDs — extract just the UniProt ID (middle part)\n# sp|A0A0C5B5G6|MOTSC_HUMAN  →  A0A0C5B5G6\ntrain_seqs_clean = {}\nfor raw_id, seq in train_seqs.items():\n    parts = raw_id.split('|')\n    if len(parts) >= 2:\n        clean_id = parts[1]  # take the middle part\n    else:\n        clean_id = raw_id    # fallback: keep as is\n    train_seqs_clean[clean_id] = seq\n\nprint(f\"Training sequences after ID fix: {len(train_seqs_clean)}\")\n\n# Check a few IDs now match train_terms format\nsample_ids = list(train_seqs_clean.keys())[:5]\nprint(f\"\\nSample cleaned IDs: {sample_ids}\")\n\n# Verify overlap with train_terms\nterm_ids = set(train_terms['protein_id'].unique())\nseq_ids  = set(train_seqs_clean.keys())\noverlap  = term_ids & seq_ids\nprint(f\"\\nProteins in train_terms:    {len(term_ids)}\")\nprint(f\"Proteins in train_seqs:     {len(seq_ids)}\")\nprint(f\"Overlap (matched):          {len(overlap)}\")\n\n# Load test sequences (proteins we need to predict for)\nprint(\"\\nLoading test sequences...\")\ntest_seqs_raw = read_fasta(\n    '/kaggle/input/competitions/cafa-6-protein-function-prediction/Test/testsuperset.fasta'\n)\n\n# Fix test IDs the same way\ntest_seqs = {}\nfor raw_id, seq in test_seqs_raw.items():\n    parts = raw_id.split('|')\n    if len(parts) >= 2:\n        clean_id = parts[1]\n    else:\n        clean_id = raw_id\n    test_seqs[clean_id] = seq\n\nprint(f\"Test proteins loaded: {len(test_seqs)}\")\n\n# Show a sample test protein\nsample_test_id = list(test_seqs.keys())[0]\nprint(f\"\\nSample test ID: {sample_test_id}\")\nprint(f\"Sample test sequence (first 80 chars): {test_seqs[sample_test_id][:80]}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:43:11.387100Z","iopub.execute_input":"2026-03-29T11:43:11.387979Z","iopub.status.idle":"2026-03-29T11:43:13.638220Z","shell.execute_reply.started":"2026-03-29T11:43:11.387941Z","shell.execute_reply":"2026-03-29T11:43:13.637172Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 6 — Load GO ontology (the hierarchy of all GO terms)\n\nimport obonet\n\nprint(\"Loading GO ontology graph...\")\ngo_graph = obonet.read_obo(\n    '/kaggle/input/competitions/cafa-6-protein-function-prediction/Train/go-basic.obo'\n)\n\nprint(f\"GO terms (nodes) in graph: {go_graph.number_of_nodes()}\")\nprint(f\"Relationships (edges):     {go_graph.number_of_edges()}\")\n\n# Show what a single GO term looks like\nsample_term = 'GO:0003677'\nif sample_term in go_graph.nodes:\n    data = go_graph.nodes[sample_term]\n    print(f\"\\nExample GO term: {sample_term}\")\n    print(f\"  Name:      {data.get('name', 'N/A')}\")\n    print(f\"  Namespace: {data.get('namespace', 'N/A')}\")\n\n# Map each GO term to its namespace (MF/BP/CC)\ngo_to_namespace = {\n    term: data.get('namespace', '')\n    for term, data in go_graph.nodes(data=True)\n}\n\n# Count terms per namespace\nfrom collections import Counter\nns_counts = Counter(go_to_namespace.values())\nprint(f\"\\nGO terms per namespace:\")\nfor ns, count in ns_counts.most_common():\n    print(f\"  {ns}: {count}\")\n\n# Build a function to get all ancestor terms for a GO term\n# (needed later for propagating predictions up the hierarchy)\ndef get_ancestors(go_graph, term):\n    \"\"\"Return all ancestor GO terms for a given term.\"\"\"\n    try:\n        return nx.ancestors(go_graph, term)\n    except:\n        return set()\n\n# Quick test\nancestors = get_ancestors(go_graph, 'GO:0003677')\nprint(f\"\\nAncestors of GO:0003677 (DNA binding): {len(ancestors)} parent terms\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:44:35.082332Z","iopub.execute_input":"2026-03-29T11:44:35.083431Z","iopub.status.idle":"2026-03-29T11:44:41.774460Z","shell.execute_reply.started":"2026-03-29T11:44:35.083387Z","shell.execute_reply":"2026-03-29T11:44:41.773543Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 7 — Build frequency-based baseline model\n\nprint(\"Counting GO term frequencies...\")\n\nterm_counts = train_terms.groupby(['go_term', 'ontology']).size().reset_index(name='count')\ntotal_proteins = train_terms['protein_id'].nunique()\n\nprint(f\"Total training proteins: {total_proteins}\")\nprint(f\"Total unique GO terms in training: {term_counts['go_term'].nunique()}\")\n\nterm_counts['frequency'] = term_counts['count'] / total_proteins\n\nterm_counts = term_counts.sort_values('frequency', ascending=False).reset_index(drop=True)\n\nprint(\"\\nTop 10 most frequent GO terms:\")\nprint(term_counts[['go_term', 'ontology', 'count', 'frequency']].head(10))\n\nprint(\"\\nBottom 5 least frequent GO terms:\")\nprint(term_counts[['go_term', 'ontology', 'count', 'frequency']].tail(5))\n\nMIN_FREQ = 0.01\nfrequent_terms = term_counts[term_counts['frequency'] >= MIN_FREQ].copy()\nprint(f\"\\nTerms with frequency >= {MIN_FREQ}: {len(frequent_terms)}\")\nprint(f\"Terms per ontology:\")\nprint(frequent_terms['ontology'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:46:45.107633Z","iopub.execute_input":"2026-03-29T11:46:45.108533Z","iopub.status.idle":"2026-03-29T11:46:45.328562Z","shell.execute_reply.started":"2026-03-29T11:46:45.108494Z","shell.execute_reply":"2026-03-29T11:46:45.327592Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 8 — Generate baseline predictions\n\nprint(\"Generating baseline predictions...\")\n\n# We will predict the same frequent GO terms for every test protein\n# Score = frequency (how often it appeared in training)\n# This is naive but gets us a valid submission fast\n\ntest_protein_ids = list(test_seqs.keys())\nprint(f\"Test proteins to predict for: {len(test_protein_ids)}\")\nprint(f\"GO terms we will predict per protein: {len(frequent_terms)}\")\n\n# Build prediction rows\nrows = []\nfor protein_id in test_protein_ids:\n    for _, term_row in frequent_terms.iterrows():\n        rows.append({\n            'protein_id': protein_id,\n            'go_term':    term_row['go_term'],\n            'score':      round(term_row['frequency'], 3)\n        })\n\npredictions_df = pd.DataFrame(rows)\nprint(f\"\\nTotal prediction rows generated: {len(predictions_df)}\")\nprint(f\"\\nSample predictions:\")\nprint(predictions_df.head(10))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:47:18.035493Z","iopub.execute_input":"2026-03-29T11:47:18.036266Z","iopub.status.idle":"2026-03-29T11:54:09.404340Z","shell.execute_reply.started":"2026-03-29T11:47:18.036224Z","shell.execute_reply":"2026-03-29T11:54:09.403551Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cell 9 — Save submission file\n\noutput_path = '/kaggle/working/submission.tsv'\n\nprint(\"Saving submission file...\")\n\n# Write in exact format required: protein_id TAB go_term TAB score\n# No header row allowed\nwith open(output_path, 'w') as f:\n    for _, row in predictions_df.iterrows():\n        f.write(f\"{row['protein_id']}\\t{row['go_term']}\\t{row['score']}\\n\")\n\nprint(f\"File saved to: {output_path}\")\n\n# Verify file looks correct\nprint(\"\\nFirst 5 lines of submission file:\")\nwith open(output_path, 'r') as f:\n    for i, line in enumerate(f):\n        if i >= 5:\n            break\n        print(repr(line))\n\n# Check file size\nimport os\nsize_mb = os.path.getsize(output_path) / (1024 * 1024)\nprint(f\"\\nFile size: {size_mb:.1f} MB\")\nprint(f\"Total lines: {len(predictions_df)}\")\nprint(\"\\nDone! Ready to submit.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-29T11:54:59.741769Z","iopub.execute_input":"2026-03-29T11:54:59.742549Z","iopub.status.idle":"2026-03-29T12:01:38.442117Z","shell.execute_reply.started":"2026-03-29T11:54:59.742513Z","shell.execute_reply":"2026-03-29T12:01:38.441253Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}