{"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":"gpu","dataSources":[{"sourceId":116062,"databundleVersionId":14875579,"sourceType":"competition"}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\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","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# setup","metadata":{}},{"cell_type":"code","source":"!pip install obonet Bio\n!pip install biopython pandas scipy numpy transformers \n!pip install torch torchvision torchaudio","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T10:50:28.013438Z","iopub.execute_input":"2025-12-29T10:50:28.014175Z","iopub.status.idle":"2025-12-29T10:50:41.733353Z","shell.execute_reply.started":"2025-12-29T10:50:28.014131Z","shell.execute_reply":"2025-12-29T10:50:41.732621Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Dataset visualization","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nfrom Bio import SeqIO\n\n\nprint(\"=== TRAIN: train_sequences.fasta ===\")\ntrain_sequences = list(SeqIO.parse(\"/kaggle/input/cafa-6-protein-function-prediction/Train/train_sequences.fasta\", \"fasta\"))\nprint(\"Total sequences:\", len(train_sequences))\nprint(\"Example record:\")\nprint(\"ID:\", train_sequences[0].id)\nprint(\"Sequence length:\", len(train_sequences[0].seq))\nprint()\n\nprint(\"=== TRAIN: train_terms.tsv ===\")\ntrain_terms = pd.read_csv(\"/kaggle/input/cafa-6-protein-function-prediction/Train/train_terms.tsv\", sep=\"\\t\", header=None, names=[\"protein_id\", \"go_term\"])\nprint(train_terms.head())\nprint(\"Total annotations:\", len(train_terms))\nprint(\"Unique GO terms:\", train_terms.go_term.nunique())\nprint()\n\nprint(\"=== TRAIN: train_taxonomy.tsv ===\")\ntrain_tax = pd.read_csv(\"/kaggle/input/cafa-6-protein-function-prediction/Train/train_taxonomy.tsv\", sep=\"\\t\", header=None, names=[\"protein_id\", \"taxon_id\"])\nprint(train_tax.head())\nprint(\"Taxon count:\", train_tax.taxon_id.nunique())\nprint()\n\nprint(\"=== TRAIN: IA.tsv (Information Accretion values) ===\")\nia = pd.read_csv(\"/kaggle/input/cafa-6-protein-function-prediction/IA.tsv\", sep=\"\\t\", header=None, names=[\"go_term\", \"IA_value\"])\nprint(ia.head())\nprint(\"Total GO terms with IA values:\", len(ia))\nprint()\n\nprint(\"=== TRAIN: go-basic.obo (Ontology structure) ===\")\n# Only printing first few lines\nwith open(\"/kaggle/input/cafa-6-protein-function-prediction/Train/go-basic.obo\", \"r\") as f:\n    for i in range(20):\n        print(f.readline().strip())\nprint(\"... (ontology continues)\")\nprint()\n\nprint(\"=== TRAIN: sample_submission.tsv ===\")\nsample_sub = pd.read_csv(\n    \"/kaggle/input/cafa-6-protein-function-prediction/sample_submission.tsv\",\n    sep=\"\\t\",\n    engine=\"python\",\n    comment=\"#\",\n    on_bad_lines=\"skip\"\n)\nprint(sample_sub.head())\nprint(sample_sub.columns)\n\n\n\nprint(\"=== TEST: testsuperset.fasta ===\")\ntest_sequences = list(SeqIO.parse(\"/kaggle/input/cafa-6-protein-function-prediction/Test/testsuperset.fasta\", \"fasta\"))\nprint(\"Total test sequences:\", len(test_sequences))\nprint(\"Example record:\")\nprint(\"ID:\", test_sequences[0].id)\nprint(\"Sequence length:\", len(test_sequences[0].seq))\nprint()\n\nprint(\"=== TEST: testsuperset-taxon-list.tsv ===\")\ntest_tax = pd.read_csv(\"/kaggle/input/cafa-6-protein-function-prediction/Test/testsuperset-taxon-list.tsv\", sep=\"\\t\", header=None, names=[\"protein_id\", \"taxon_id\"])\nprint(test_tax.head())\nprint(\"Test taxon count:\", test_tax.taxon_id.nunique())\nprint()\n\nprint(\"=== DONE ===\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T10:31:56.890197Z","iopub.execute_input":"2025-12-29T10:31:56.890529Z","iopub.status.idle":"2025-12-29T10:32:01.630286Z","shell.execute_reply.started":"2025-12-29T10:31:56.890499Z","shell.execute_reply":"2025-12-29T10:32:01.629328Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# SOTA-style pipeline. with ESM-2 650M, ","metadata":{}},{"cell_type":"markdown","source":"I uses ESM-2 650M, a 1D-CNN architecture, and Hierarchical Label Propagation.","metadata":{}},{"cell_type":"markdown","source":"## PART 1: 650M EMBEDDING EXTRACTION","metadata":{}},{"cell_type":"code","source":"import os\nimport torch\nimport numpy as np\nfrom Bio import SeqIO\nfrom transformers import AutoTokenizer, EsmModel\nfrom tqdm import tqdm\n\nclass CFG:\n    BASE = \"/kaggle/input/cafa-6-protein-function-prediction\"\n    TRAIN_FASTA = f\"{BASE}/Train/train_sequences.fasta\"\n    TEST_FASTA  = f\"{BASE}/Test/testsuperset.fasta\"\n    OUT_DIR = \"/kaggle/working\"\n    \n    # USE THE BIG MODEL\n    MODEL_NAME = \"facebook/esm2_t33_650M_UR50D\"\n    MAX_LEN = 1022\n    BS = 64 # Small batch size to prevent GPU OOM\n    DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\ndef extract_embeddings(fasta_path, prefix):\n    print(f\">> Processing {prefix} with 650M Model...\")\n    npy_path = f\"{CFG.OUT_DIR}/{prefix}_emb_650M.npy\"\n    ids_path = f\"{CFG.OUT_DIR}/{prefix}_ids_650M.npy\"\n    \n    if os.path.exists(npy_path): return\n\n    tokenizer = AutoTokenizer.from_pretrained(CFG.MODEL_NAME)\n    model = EsmModel.from_pretrained(CFG.MODEL_NAME).to(CFG.DEVICE)\n    model.eval()\n\n    ids, seqs = [], []\n    for rec in SeqIO.parse(fasta_path, \"fasta\"):\n        ids.append(str(rec.id).split(\"|\")[1] if \"|\" in rec.id else str(rec.id))\n        seqs.append(str(rec.seq)[:CFG.MAX_LEN])\n\n    # Sort for smart batching\n    perm_idx = np.argsort([len(s) for s in seqs])\n    ids = [ids[i] for i in perm_idx]\n    seqs = [seqs[i] for i in perm_idx]\n\n    embeddings = []\n    print(f\"   Inference on {len(seqs)} sequences...\")\n    \n    with torch.no_grad(), torch.amp.autocast('cuda'):\n        for i in tqdm(range(0, len(seqs), CFG.BS)):\n            batch = seqs[i : i + CFG.BS]\n            inputs = tokenizer(batch, return_tensors=\"pt\", padding=True, truncation=True, max_length=CFG.MAX_LEN).to(CFG.DEVICE)\n            out = model(**inputs).last_hidden_state[:, 0, :] # CLS token\n            embeddings.append(out.cpu().numpy().astype(np.float16))\n\n    # Restore order could be done here, but we'll just save sorted\n    # and map IDs later. It's safer.\n    full_embs = np.vstack(embeddings)\n    np.save(npy_path, full_embs)\n    np.save(ids_path, ids)\n    \n    del model, tokenizer\n    torch.cuda.empty_cache()\n\nif __name__ == \"__main__\":\n    if not os.path.exists(CFG.TRAIN_FASTA): # Kaggle path fix\n        CFG.TRAIN_FASTA = f\"{CFG.BASE}/train_sequences.fasta\"\n        CFG.TEST_FASTA = f\"{CFG.BASE}/testsuperset.fasta\"\n        \n    extract_embeddings(CFG.TRAIN_FASTA, \"train\")\n    extract_embeddings(CFG.TEST_FASTA, \"test\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T10:55:43.932061Z","iopub.execute_input":"2025-12-29T10:55:43.932943Z","iopub.status.idle":"2025-12-29T18:59:42.560545Z","shell.execute_reply.started":"2025-12-29T10:55:43.932900Z","shell.execute_reply":"2025-12-29T18:59:42.559676Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## PART 2: CNN-RESNET TRAINING & SUBMISSION","metadata":{}},{"cell_type":"code","source":"# ==========================================\n# PART 2: CNN-RESNET TRAINING (DTYPE FIXED)\n# ==========================================\nimport os\nimport gc\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nimport numpy as np\nimport pandas as pd\nfrom torch.utils.data import TensorDataset, DataLoader\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import f1_score\nfrom collections import defaultdict, Counter\nimport obonet\nimport networkx as nx\nfrom tqdm import tqdm\n\nclass CFG:\n    BASE = \"/kaggle/input/cafa-6-protein-function-prediction\"\n    WORK = \"/kaggle/working\"\n    TRAIN_TERMS = f\"{BASE}/Train/train_terms.tsv\"\n    OBO = f\"{BASE}/Train/go-basic.obo\"\n    \n    # Fallback paths\n    if not os.path.exists(TRAIN_TERMS):\n        TRAIN_TERMS = f\"{BASE}/train_terms.tsv\"\n        OBO = f\"{BASE}/go-basic.obo\"\n\n    EMBED_DIM = 1280  # 650M Dimension\n    BS = 2048         # Large batch for training heads\n    LR = 1e-3\n    EPOCHS = 20       # CNNs converge nicely\n    DEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n    \n    # We limit labels to top N to reduce noise\n    MAX_LABELS = {\"MFO\": 1500, \"BPO\": 3000, \"CCO\": 1000}\n\n# --- 1. IMPROVED ARCHITECTURE: CNN-ResNet ---\nclass ResidualBlock(nn.Module):\n    def __init__(self, channels):\n        super().__init__()\n        self.block = nn.Sequential(\n            nn.Linear(channels, channels),\n            nn.BatchNorm1d(channels),\n            nn.ReLU(),\n            nn.Linear(channels, channels),\n            nn.BatchNorm1d(channels)\n        )\n        self.relu = nn.ReLU()\n    \n    def forward(self, x):\n        return self.relu(x + self.block(x))\n\nclass ProteinClassifier(nn.Module):\n    def __init__(self, in_dim, out_dim):\n        super().__init__()\n        # 1. Project & Convolve (Simulated 1D Conv via Linear for embeddings)\n        self.feature_extract = nn.Sequential(\n            nn.Linear(in_dim, 1024),\n            nn.BatchNorm1d(1024),\n            nn.ReLU(),\n            nn.Dropout(0.4)\n        )\n        \n        # 2. Residual Blocks (Deep Reasoning)\n        self.res1 = ResidualBlock(1024)\n        self.res2 = ResidualBlock(1024)\n        \n        # 3. Classification Head\n        self.head = nn.Linear(1024, out_dim)\n\n    def forward(self, x):\n        x = self.feature_extract(x)\n        x = self.res1(x)\n        x = self.res2(x)\n        return self.head(x)\n\n# --- 2. HIERARCHY LOGIC ---\nclass Ontology:\n    def __init__(self, path):\n        print(\">> Loading Ontology...\")\n        self.graph = obonet.read_obo(path)\n        self.ns_map = {\"molecular_function\": \"MFO\", \"biological_process\": \"BPO\", \"cellular_component\": \"CCO\"}\n    def namespace(self, term):\n        return self.ns_map.get(self.graph.nodes[term].get(\"namespace\"))\n\ndef propagate_preds(preds, term_list, ont, aspect):\n    \"\"\"Enforces Parent >= Child rule\"\"\"\n    term2idx = {t: i for i, t in enumerate(term_list)}\n    parent_map = []\n    \n    # Precompute parents for every term\n    for term in term_list:\n        parents = []\n        if term in ont.graph:\n            for p in ont.graph.predecessors(term):\n                if p in term2idx and ont.namespace(p) == aspect:\n                    parents.append(term2idx[p])\n        parent_map.append(parents)\n        \n    # Bubble up scores (Iterate 3 times)\n    for _ in range(3):\n        for i in range(len(term_list)):\n            if parent_map[i]:\n                child_score = preds[:, i:i+1]\n                parent_indices = parent_map[i]\n                preds[:, parent_indices] = np.maximum(preds[:, parent_indices], child_score)\n    return preds\n\n# --- 3. ROBUST DATA LOADING ---\ndef load_data(aspect, ont, tr_map):\n    print(f\">> Loading {aspect} Data...\")\n    try:\n        df = pd.read_csv(CFG.TRAIN_TERMS, sep=\"\\t\", names=[\"pid\", \"term\", \"aspect\"], header=0)\n    except:\n        df = pd.read_csv(CFG.TRAIN_TERMS, sep=\"\\t\", names=[\"pid\", \"term\", \"aspect\"], header=None)\n        \n    # Robust Filtering\n    aliases = {\n        \"MFO\": [\"MFO\", \"molecular_function\", \"F\"],\n        \"BPO\": [\"BPO\", \"biological_process\", \"P\"],\n        \"CCO\": [\"CCO\", \"cellular_component\", \"C\"]\n    }\n    df = df[df['aspect'].astype(str).isin(aliases[aspect])]\n    \n    # Count & Propagate\n    term_counts = Counter()\n    pid2terms = defaultdict(set)\n    \n    print(\"   Propagating Labels (CPU heavy)...\")\n    for pid, term in tqdm(zip(df['pid'], df['term']), total=len(df)):\n        if pid not in tr_map: continue\n        \n        annot = {term}\n        if term in ont.graph:\n            parents = nx.ancestors(ont.graph, term)\n            annot.update([p for p in parents if ont.namespace(p) == aspect])\n            \n        pid2terms[pid].update(annot)\n        term_counts.update(annot)\n        \n    # Select Top N terms\n    top_terms = [t for t, _ in term_counts.most_common(CFG.MAX_LABELS[aspect])]\n    term2idx = {t: i for i, t in enumerate(top_terms)}\n    \n    # Build Dataset\n    X_idx = []\n    Y = []\n    \n    for pid, terms in pid2terms.items():\n        if pid in tr_map:\n            X_idx.append(tr_map[pid])\n            vec = np.zeros(len(top_terms), dtype=np.float32)\n            indices = [term2idx[t] for t in terms if t in term2idx]\n            vec[indices] = 1.0\n            Y.append(vec)\n            \n    return np.array(X_idx), np.array(Y), top_terms\n\n# --- 4. MAIN LOOP ---\ndef main():\n    # Load Embeddings\n    try:\n        # Load in mmap mode to save RAM\n        Xtr_disk = np.load(f\"{CFG.WORK}/train_emb_650M.npy\", mmap_mode=\"r\")\n        Xte_disk = np.load(f\"{CFG.WORK}/test_emb_650M.npy\", mmap_mode=\"r\")\n        tr_ids = np.load(f\"{CFG.WORK}/train_ids_650M.npy\")\n        te_ids = np.load(f\"{CFG.WORK}/test_ids_650M.npy\")\n        tr_map = {p: i for i, p in enumerate(tr_ids)}\n    except FileNotFoundError:\n        print(\"❌ Embeddings not found. Run PART 1 first!\")\n        return\n\n    ont = Ontology(CFG.OBO)\n    submission = []\n\n    for aspect in [\"MFO\", \"BPO\", \"CCO\"]:\n        # Load & Align\n        X_indices, Y, terms = load_data(aspect, ont, tr_map)\n        if len(Y) < 100: continue\n        \n        print(f\"   Loading {len(X_indices)} embeddings into RAM...\")\n        X = Xtr_disk[X_indices]\n        \n        # Split\n        X_tr, X_val, y_tr, y_val = train_test_split(X, Y, test_size=0.1, random_state=42)\n        \n        # Train\n        model = ProteinClassifier(CFG.EMBED_DIM, len(terms)).to(CFG.DEVICE)\n        opt = optim.AdamW(model.parameters(), lr=CFG.LR, weight_decay=1e-4)\n        criterion = nn.BCEWithLogitsLoss()\n        \n        train_dl = DataLoader(TensorDataset(torch.tensor(X_tr), torch.tensor(y_tr)), batch_size=CFG.BS, shuffle=True)\n        \n        print(f\">> Training {aspect} Model...\")\n        for epoch in range(CFG.EPOCHS):\n            model.train()\n            for x, y in train_dl:\n                # === FIX 1: FORCE FLOAT32 CONVERSION ===\n                x = x.to(CFG.DEVICE).float()\n                y = y.to(CFG.DEVICE).float()\n                \n                opt.zero_grad()\n                loss = criterion(model(x), y)\n                loss.backward()\n                opt.step()\n                \n        # Validate\n        model.eval()\n        with torch.no_grad():\n            # === FIX 2: FORCE FLOAT32 ON VALIDATION ===\n            v_input = torch.tensor(X_val).to(CFG.DEVICE).float()\n            v_probs = torch.sigmoid(model(v_input)).cpu().numpy()\n            \n            best_th, best_f1 = 0.2, 0.0\n            for th in np.arange(0.15, 0.5, 0.05):\n                score = f1_score(y_val, (v_probs > th), average=\"micro\")\n                if score > best_f1: best_f1, best_th = score, th\n            \n            print(f\"   Best Threshold: {best_th:.2f} (F1: {best_f1:.4f})\")\n            \n            # Inference on Test (Chunked)\n            preds = []\n            for i in range(0, len(Xte_disk), CFG.BS):\n                # === FIX 3: FORCE FLOAT32 ON TEST ===\n                batch = torch.tensor(Xte_disk[i:i+CFG.BS]).to(CFG.DEVICE).float()\n                preds.append(torch.sigmoid(model(batch)).cpu().numpy())\n            preds = np.vstack(preds)\n            \n            print(\"   Propagating predictions...\")\n            preds = propagate_preds(preds, terms, ont, aspect)\n            \n            count = 0\n            for i, pid in enumerate(te_ids):\n                idxs = np.where(preds[i] >= best_th)[0]\n                \n                if len(idxs) == 0: idxs = np.argsort(preds[i])[-3:]\n                \n                if len(idxs) > 70:\n                    top_k = np.argsort(preds[i][idxs])[-70:]\n                    idxs = idxs[top_k]\n                    \n                for idx in idxs:\n                    submission.append(f\"{pid}\\t{terms[idx]}\\t{preds[i][idx]:.3f}\")\n                    count += 1\n            print(f\"   Added {count} rows.\")\n            \n        del model, X, Y\n        gc.collect()\n        torch.cuda.empty_cache()\n\n    print(\">> Saving submission.tsv...\")\n    with open(\"submission.tsv\", \"w\") as f:\n        f.write(\"\\n\".join(submission))\n    print(\">> DONE.\")\n\nif __name__ == \"__main__\":\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:02:37.282540Z","iopub.execute_input":"2025-12-29T19:02:37.282847Z","iopub.status.idle":"2025-12-29T19:09:38.194748Z","shell.execute_reply.started":"2025-12-29T19:02:37.282817Z","shell.execute_reply":"2025-12-29T19:09:38.194103Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"=== submission: submission.tsv ===\")\ndf = pd.read_csv(\"/kaggle/working/submission.tsv\", sep=\"\\t\", header=None, names=[\"protein_id\", \"go_term\"])\nprint(df.head())\nprint(\"\\nTotal annotations:\", len(df))\nprint(\"\\nUnique GO terms:\", df.go_term.nunique())\nprint()\n\nprint(\"Unique proteins count:\", df.protein_id.nunique())\nprint()\n\nprint(\"=== DONE ===\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:13:10.267768Z","iopub.execute_input":"2025-12-29T19:13:10.268071Z","iopub.status.idle":"2025-12-29T19:13:20.137344Z","shell.execute_reply.started":"2025-12-29T19:13:10.268044Z","shell.execute_reply":"2025-12-29T19:13:20.136533Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Visualizations","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\ndf = pd.read_csv(\n    \"/kaggle/working/submission.tsv\",\n    sep=\"\\t\",\n    header=None,\n    names=[\"protein_id\", \"go_term\", \"score\"]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:14:47.441626Z","iopub.execute_input":"2025-12-29T19:14:47.442228Z","iopub.status.idle":"2025-12-29T19:14:55.803976Z","shell.execute_reply.started":"2025-12-29T19:14:47.442197Z","shell.execute_reply":"2025-12-29T19:14:55.803399Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(8, 5))\nplt.hist(df['score'], bins=50, color='skyblue', edgecolor='black')\nplt.title('Distribution of GO Term Prediction Scores')\nplt.xlabel('Confidence Score')\nplt.ylabel('Frequency')\nplt.grid(True, linestyle='--', alpha=0.6)\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:14:59.844828Z","iopub.execute_input":"2025-12-29T19:14:59.845334Z","iopub.status.idle":"2025-12-29T19:15:00.483783Z","shell.execute_reply.started":"2025-12-29T19:14:59.845307Z","shell.execute_reply":"2025-12-29T19:15:00.483198Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_go = df['go_term'].value_counts().head(20)\n\nplt.figure(figsize=(10, 6))\ntop_go.plot(kind='bar', color='lightcoral')\nplt.title('Top 20 Most Frequent GO Terms')\nplt.xlabel('GO Term')\nplt.ylabel('Number of Annotations')\nplt.xticks(rotation=45, ha='right')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:15:43.974810Z","iopub.execute_input":"2025-12-29T19:15:43.975626Z","iopub.status.idle":"2025-12-29T19:15:46.135799Z","shell.execute_reply.started":"2025-12-29T19:15:43.975584Z","shell.execute_reply":"2025-12-29T19:15:46.135136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"top_10_go = df['go_term'].value_counts().head(10).index\ndf_top = df[df['go_term'].isin(top_10_go)]\n\nplt.figure(figsize=(12, 6))\nsns.boxplot(data=df_top, x='go_term', y='score', palette='Set2')\nplt.title('Confidence Score Distribution for Top 10 GO Terms')\nplt.xticks(rotation=45, ha='right')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:15:54.611410Z","iopub.execute_input":"2025-12-29T19:15:54.611700Z","iopub.status.idle":"2025-12-29T19:15:59.664786Z","shell.execute_reply.started":"2025-12-29T19:15:54.611674Z","shell.execute_reply":"2025-12-29T19:15:59.664030Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"protein_counts = df['protein_id'].value_counts().head(20)\n\nplt.figure(figsize=(10, 6))\nprotein_counts.plot(kind='bar', color='mediumseagreen')\nplt.title('Top 20 Proteins by Number of GO Annotations')\nplt.xlabel('Protein ID')\nplt.ylabel('Number of GO Terms Predicted')\nplt.xticks(rotation=45, ha='right')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:16:16.708859Z","iopub.execute_input":"2025-12-29T19:16:16.709453Z","iopub.status.idle":"2025-12-29T19:16:18.828817Z","shell.execute_reply.started":"2025-12-29T19:16:16.709425Z","shell.execute_reply":"2025-12-29T19:16:18.828105Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Score min:\", df['score'].min())\nprint(\"Score max:\", df['score'].max())\nprint(\"Any scores > 1?\", (df['score'] > 1).any())\nprint(\"Any negative scores?\", (df['score'] < 0).any())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-29T19:16:28.210781Z","iopub.execute_input":"2025-12-29T19:16:28.211354Z","iopub.status.idle":"2025-12-29T19:16:28.315103Z","shell.execute_reply.started":"2025-12-29T19:16:28.211325Z","shell.execute_reply":"2025-12-29T19:16:28.314423Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}