{"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":"none","dataSources":[{"sourceId":116062,"databundleVersionId":14084779,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🔬 Ultimate CAFA6 EDA | GO Beyond the Data\n\n<div style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px; border-radius: 15px; color: white; text-align: center; margin-bottom: 30px;\">\n    <h2 style=\"margin: 0; font-size: 2.5em;\">Deep Dive into Protein Function Prediction</h2>\n    <p style=\"font-size: 1.2em; margin-top: 15px;\">Comprehensive Analysis • 82K+ Proteins • 26K+ GO Terms</p>\n</div>\n\n<div style=\"background: #f8fafc; padding: 20px; border-left: 5px solid #667eea; border-radius: 5px; margin: 20px 0;\">\n    <p style=\"margin: 0; font-size: 16px;\">\n        <strong>👤 Author:</strong> <a href=\"https://www.kaggle.com/guntasdhanjal\" style=\"color: #667eea; text-decoration: none;\">Guntas Dhanjal</a> | \n        <strong>📅 Updated:</strong> 04 November 2025\n    </p>\n</div>\n\n---\n\n## 🎯 What is CAFA?\n\n<div style=\"background: linear-gradient(to right, #fef3c7, #fde68a); padding: 20px; border-radius: 10px; border-left: 5px solid #f59e0b; margin: 20px 0;\">\n    <strong>Critical Assessment of Functional Annotation (CAFA)</strong> is a community-wide challenge to evaluate and advance computational methods for predicting protein function.\n</div>\n\n**This competition is unique because:**\n- 🔮 **Prospective evaluation** - Test proteins currently have NO labels\n- ⏰ **Time-based** - Proteins gaining experimental validation during the competition will be scored\n- 🏆 **Real impact** - Results published in top scientific journals\n\n---\n\n## 📋 Challenge Overview\n\n| Aspect | Details |\n|--------|---------|\n| 🎯 **Task** | Predict protein functions from amino acid sequences |\n| 📊 **Output** | Gene Ontology (GO) terms across 3 subontologies |\n| 🏷️ **Type** | Multi-label hierarchical classification |\n| 📈 **Evaluation** | Fmax score weighted by Information Accretion (IA) |\n| ⚠️ **Difficulty** | Extreme class imbalance + hierarchical dependencies |\n\n---\n\n## 🔑 Key Concepts\n\n<div style=\"background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 25px; border-radius: 10px; color: white; margin: 15px 0;\">\n    <h3 style=\"margin: 0 0 10px 0;\">🧬 Protein Sequences</h3>\n    <p style=\"margin: 0; line-height: 1.6;\">Chains of 20 amino acid types that fold into 3D structures determining biological function</p>\n</div>\n\n<div style=\"background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); padding: 25px; border-radius: 10px; color: white; margin: 15px 0;\">\n    <h3 style=\"margin: 0 0 10px 0;\">🏷️ Gene Ontology (GO)</h3>\n    <p style=\"margin: 0; line-height: 1.6;\">Standardized vocabulary describing protein functions across species in a hierarchical structure</p>\n</div>\n\n<div style=\"background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); padding: 25px; border-radius: 10px; color: white; margin: 15px 0;\">\n    <h3 style=\"margin: 0 0 10px 0;\">📊 Information Accretion</h3>\n    <p style=\"margin: 0; line-height: 1.6;\">Weight metric giving higher scores to rare, specific terms vs common, general terms</p>\n</div>\n\n---\n\n### 📂 Three GO Subontologies\n\n<div style=\"background: #dbeafe; padding: 20px; border-radius: 10px; text-align: center; margin: 10px 0; border: 2px solid #3b82f6;\">\n    <div style=\"font-size: 40px; margin-bottom: 10px;\">⚙️</div>\n    <h4 style=\"color: #1e40af; margin: 10px 0;\">MFO - Molecular Function</h4>\n    <p style=\"color: #64748b; font-size: 14px; font-style: italic;\">What does it do?</p>\n</div>\n\n<div style=\"background: #fce7f3; padding: 20px; border-radius: 10px; text-align: center; margin: 10px 0; border: 2px solid #ec4899;\">\n    <div style=\"font-size: 40px; margin-bottom: 10px;\">🔄</div>\n    <h4 style=\"color: #be185d; margin: 10px 0;\">BPO - Biological Process</h4>\n    <p style=\"color: #64748b; font-size: 14px; font-style: italic;\">What pathway is it in?</p>\n</div>\n\n<div style=\"background: #dcfce7; padding: 20px; border-radius: 10px; text-align: center; margin: 10px 0; border: 2px solid #22c55e;\">\n    <div style=\"font-size: 40px; margin-bottom: 10px;\">📍</div>\n    <h4 style=\"color: #166534; margin: 10px 0;\">CCO - Cellular Component</h4>\n    <p style=\"color: #64748b; font-size: 14px; font-style: italic;\">Where is it located?</p>\n</div>\n\n---\n\n## 📂 Dataset Structure\n```\ncafa-6-protein-function-prediction/\n│\n├── Train/\n│   ├── train_sequences.fasta      # 82,404 protein sequences\n│   ├── train_terms.tsv            # 537,027 GO term annotations\n│   ├── train_taxonomy.tsv         # Species information (1,381 taxa)\n│   └── go-basic.obo               # GO hierarchy (40,122 terms)\n│\n├── Test/\n│   └── testsuperset.fasta         # 224,309 sequences for prediction\n│\n└── IA.tsv                         # Information Accretion weights (40,122 terms)\n```\n\n---\n\n<div style=\"background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 30px; border-radius: 15px; text-align: center; color: white; margin: 30px 0;\">\n    <h1 style=\"margin: 0; font-size: 2em;\">🚀 Let's Explore the Data!</h1>\n    <p style=\"margin: 15px 0 0 0; font-size: 1.1em;\">Comprehensive analysis of proteins, sequences, and functional annotations</p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"## 📦 Installation & Setup\n\nInstalling required packages for comprehensive analysis:\n- **obonet**: Gene Ontology (GO) graph parsing\n- **biopython**: Protein sequence handling\n- **plotly**: Interactive visualizations","metadata":{}},{"cell_type":"code","source":"import sys\n!{sys.executable} -m pip install obonet biopython -q\n!pip install --upgrade plotly","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:00.273278Z","iopub.execute_input":"2025-11-04T12:52:00.273581Z","iopub.status.idle":"2025-11-04T12:52:29.863900Z","shell.execute_reply.started":"2025-11-04T12:52:00.273550Z","shell.execute_reply":"2025-11-04T12:52:29.862671Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 📚 Import Required Libraries\n\nSetting up our analysis toolkit with essential libraries for:\n- Data manipulation (pandas, numpy)\n- Visualization (matplotlib, seaborn, plotly)\n- Bioinformatics (Bio, obonet, networkx)","metadata":{}},{"cell_type":"code","source":"# Core data science\nimport pandas as pd\nimport numpy as np\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Visualization\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport plotly.express as px\nimport plotly.graph_objects as go\nfrom plotly.subplots import make_subplots\n\n# Bioinformatics\nfrom Bio import SeqIO\nimport obonet\nimport networkx as nx\n\n# Utilities\nfrom collections import Counter, defaultdict\nfrom typing import Dict, List, Tuple\nimport os\nfrom tqdm.notebook import tqdm\n\n# Set style\nsns.set_style('whitegrid')\nplt.rcParams['figure.figsize'] = (14, 6)\nplt.rcParams['font.size'] = 11\n\nprint(\"✅ All libraries loaded successfully!\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:29.865874Z","iopub.execute_input":"2025-11-04T12:52:29.866305Z","iopub.status.idle":"2025-11-04T12:52:32.381867Z","shell.execute_reply.started":"2025-11-04T12:52:29.866276Z","shell.execute_reply":"2025-11-04T12:52:32.380942Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 📂 Data Loading\n\nLoading all competition files and performing initial validation checks.","metadata":{}},{"cell_type":"code","source":"# Define paths\nBASE_PATH = '/kaggle/input/cafa-6-protein-function-prediction'\nTRAIN_PATH = f'{BASE_PATH}/Train'\nTEST_PATH = f'{BASE_PATH}/Test'\n\npaths = {\n    'train_seq': f'{TRAIN_PATH}/train_sequences.fasta',\n    'train_terms': f'{TRAIN_PATH}/train_terms.tsv',\n    'train_tax': f'{TRAIN_PATH}/train_taxonomy.tsv',\n    'go_obo': f'{TRAIN_PATH}/go-basic.obo',\n    'test_seq': f'{TEST_PATH}/testsuperset.fasta',\n    'ia': f'{BASE_PATH}/IA.tsv'\n}\n\n# Load tabular data\nprint(\"Loading datasets...\")\ntrain_terms = pd.read_csv(paths['train_terms'], sep='\\t')\ntrain_tax = pd.read_csv(paths['train_tax'], sep='\\t', header=None, names=['EntryID', 'TaxonID'])\nia_df = pd.read_csv(paths['ia'], sep='\\t', header=None, names=['term', 'IA'])\n\n# Load GO graph\nprint(\"Loading Gene Ontology graph...\")\ngo_graph = obonet.read_obo(paths['go_obo'])\n\n# Display data overview\nprint(\"\\n\" + \"=\"*70)\nprint(\"📊 DATA OVERVIEW\")\nprint(\"=\"*70)\nprint(f\"✓ Training Terms:        {len(train_terms):,} annotations\")\nprint(f\"✓ Unique Proteins:       {train_terms['EntryID'].nunique():,}\")\nprint(f\"✓ Unique GO Terms:       {train_terms['term'].nunique():,}\")\nprint(f\"✓ Unique Species (Taxa): {train_tax['TaxonID'].nunique():,}\")\nprint(f\"✓ GO Graph Nodes:        {go_graph.number_of_nodes():,}\")\nprint(f\"✓ GO Graph Edges:        {go_graph.number_of_edges():,}\")\nprint(f\"✓ IA Records:            {len(ia_df):,}\")\nprint(\"=\"*70)\n\n# Preview data\ndisplay(train_terms.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:32.385892Z","iopub.execute_input":"2025-11-04T12:52:32.386203Z","iopub.status.idle":"2025-11-04T12:52:40.040206Z","shell.execute_reply.started":"2025-11-04T12:52:32.386181Z","shell.execute_reply":"2025-11-04T12:52:40.039266Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: white; margin: 0; font-size: 32px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\">\n    🧬 Protein Sequence Analysis\n  </h1>\n  <p style=\"color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-size: 16px;\">\n    Understanding the building blocks of protein function\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"### Part 1: Loading and Basic Statistics\n\nExtracting key features from FASTA sequences:\n- Sequence lengths\n- Amino acid composition\n- Dataset statistics","metadata":{}},{"cell_type":"code","source":"def load_fasta_stats(fasta_path, max_seqs=None):\n    \"\"\"Load FASTA file and extract sequence statistics\"\"\"\n    data = []\n    aa_counter = Counter()\n    \n    for i, record in enumerate(tqdm(SeqIO.parse(fasta_path, 'fasta'), desc=\"Loading sequences\")):\n        if max_seqs and i >= max_seqs:\n            break\n            \n        entry_id = record.id.split('|')[1] if '|' in record.id else record.id\n        seq = str(record.seq)\n        \n        data.append({\n            'EntryID': entry_id,\n            'length': len(seq),\n            'seq': seq\n        })\n        aa_counter.update(seq)\n    \n    return pd.DataFrame(data), aa_counter\n\n# Load training sequences\nprint(\"Loading training sequences...\")\ntrain_seqs, train_aa_counts = load_fasta_stats(paths['train_seq'])\n\n# Basic statistics\nprint(f\"\\n📊 Sequence Statistics:\")\nprint(f\"   Total sequences: {len(train_seqs):,}\")\nprint(f\"   Mean length: {train_seqs['length'].mean():.1f} amino acids\")\nprint(f\"   Median length: {train_seqs['length'].median():.0f} amino acids\")\nprint(f\"   Min length: {train_seqs['length'].min()}\")\nprint(f\"   Max length: {train_seqs['length'].max():,}\")\nprint(f\"   Std dev: {train_seqs['length'].std():.1f}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:40.042445Z","iopub.execute_input":"2025-11-04T12:52:40.042705Z","iopub.status.idle":"2025-11-04T12:52:43.737255Z","shell.execute_reply.started":"2025-11-04T12:52:40.042685Z","shell.execute_reply":"2025-11-04T12:52:43.736256Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Part 2: Sequence Length Distribution\n\nUnderstanding the distribution of protein lengths helps with:\n- Batch size selection for training\n- Model architecture decisions (e.g., max sequence length)\n- Identifying potential outliers","metadata":{}},{"cell_type":"code","source":"# Create comprehensive length visualization\nfig = make_subplots(\n    rows=2, cols=2,\n    subplot_titles=('Distribution', 'Box Plot', 'Cumulative Distribution', 'Log Scale'),\n    specs=[[{\"type\": \"histogram\"}, {\"type\": \"box\"}],\n           [{\"type\": \"scatter\"}, {\"type\": \"histogram\"}]]\n)\n\n# 1. Histogram\nfig.add_trace(\n    go.Histogram(x=train_seqs['length'], nbinsx=100, name='Count',\n                 marker_color='lightblue', showlegend=False),\n    row=1, col=1\n)\n\n# 2. Box plot\nfig.add_trace(\n    go.Box(y=train_seqs['length'], name='Length', marker_color='lightcoral',\n           boxmean='sd', showlegend=False),\n    row=1, col=2\n)\n\n# 3. Cumulative distribution\nsorted_lengths = np.sort(train_seqs['length'])\ncumulative = np.arange(1, len(sorted_lengths) + 1) / len(sorted_lengths) * 100\nfig.add_trace(\n    go.Scatter(x=sorted_lengths, y=cumulative, mode='lines',\n               name='Cumulative %', line=dict(color='green', width=2),\n               showlegend=False),\n    row=2, col=1\n)\n\n# 4. Log scale histogram\nfig.add_trace(\n    go.Histogram(x=train_seqs['length'], nbinsx=100, name='Count (log)',\n                 marker_color='mediumpurple', showlegend=False),\n    row=2, col=2\n)\n\nfig.update_xaxes(title_text=\"Sequence Length\", row=2, col=1)\nfig.update_xaxes(title_text=\"Sequence Length (log)\", type=\"log\", row=2, col=2)\nfig.update_yaxes(title_text=\"Count\", row=1, col=1)\nfig.update_yaxes(title_text=\"Cumulative %\", row=2, col=1)\n\nfig.update_layout(height=800, title_text=\"🧬 Protein Sequence Length Analysis\", showlegend=False)\nfig.show()\n\n# Key percentiles\npercentiles = train_seqs['length'].quantile([0.25, 0.5, 0.75, 0.90, 0.95, 0.99])\nprint(\"\\n📊 Key Percentiles:\")\nfor p, val in percentiles.items():\n    print(f\"   {int(p*100)}th percentile: {val:.0f} amino acids\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:43.738246Z","iopub.execute_input":"2025-11-04T12:52:43.738564Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Part 3: Amino Acid Composition Analysis\n\nThe 20 standard amino acids and their properties:\n- **Hydrophobic**: A, V, I, L, M, F, W, P\n- **Polar**: S, T, C, Y, N, Q\n- **Charged (+)**: K, R, H\n- **Charged (-)**: D, E\n- **Special**: G (smallest)\n\nUnderstanding amino acid distribution can reveal:\n- Dataset biases\n- Protein type composition\n- Potential feature engineering opportunities","metadata":{}},{"cell_type":"code","source":"# Calculate amino acid frequencies\naa_total = sum(train_aa_counts.values())\naa_freq = {aa: count/aa_total*100 for aa, count in train_aa_counts.items()}\n\n# Sort by frequency\naa_sorted = sorted(aa_freq.items(), key=lambda x: x[1], reverse=True)\namino_acids = [x[0] for x in aa_sorted]\nfrequencies = [x[1] for x in aa_sorted]\n\n# Amino acid properties\naa_properties = {\n    'L': 'Hydrophobic', 'A': 'Hydrophobic', 'G': 'Special', 'V': 'Hydrophobic',\n    'S': 'Polar', 'E': 'Charged(-)', 'I': 'Hydrophobic', 'K': 'Charged(+)',\n    'R': 'Charged(+)', 'T': 'Polar', 'D': 'Charged(-)', 'P': 'Hydrophobic',\n    'N': 'Polar', 'Q': 'Polar', 'F': 'Hydrophobic', 'Y': 'Polar',\n    'M': 'Hydrophobic', 'H': 'Charged(+)', 'C': 'Polar', 'W': 'Hydrophobic'\n}\n\ncolors = {\n    'Hydrophobic': '#3498db',\n    'Polar': '#2ecc71',\n    'Charged(+)': '#e74c3c',\n    'Charged(-)': '#f39c12',\n    'Special': '#9b59b6'\n}\n\nbar_colors = [colors[aa_properties.get(aa, 'Special')] for aa in amino_acids]\n\n# Create visualization\nfig = go.Figure()\n\nfig.add_trace(go.Bar(\n    x=amino_acids,\n    y=frequencies,\n    marker_color=bar_colors,\n    text=[f'{f:.2f}%' for f in frequencies],\n    textposition='outside',\n    hovertemplate='<b>%{x}</b><br>Frequency: %{y:.3f}%<br>Property: ' + \n                  '<extra></extra>'\n))\n\nfig.update_layout(\n    title='🔤 Amino Acid Composition in Training Proteins',\n    xaxis_title='Amino Acid',\n    yaxis_title='Frequency (%)',\n    height=500,\n    showlegend=False\n)\n\nfig.show()\n\n# Group by property\nproperty_freq = defaultdict(float)\nfor aa, freq in aa_freq.items():\n    prop = aa_properties.get(aa, 'Special')\n    property_freq[prop] += freq\n\nprint(\"\\n📊 Amino Acid Composition by Property:\")\nfor prop, freq in sorted(property_freq.items(), key=lambda x: x[1], reverse=True):\n    print(f\"   {prop:15s}: {freq:5.2f}%\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: white; margin: 0; font-size: 32px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\">\n    🏷️ GO Terms & Label Analysis\n  </h1>\n  <p style=\"color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-size: 16px;\">\n    Understanding the prediction targets and class distribution\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"### Part 1: Class Imbalance & Distribution\n\nThe CAFA challenge involves predicting GO terms across three subontologies:\n- **MFO**: Molecular Function (What does the protein do?)\n- **BPO**: Biological Process (What pathway is it involved in?)\n- **CCO**: Cellular Component (Where is it located?)\n\nUnderstanding class imbalance is crucial for:\n- Choosing appropriate loss functions\n- Setting up balanced validation sets\n- Calibrating prediction thresholds","metadata":{}},{"cell_type":"code","source":"# Map aspect codes\naspect_map = {'F': 'MFO', 'P': 'BPO', 'C': 'CCO'}\ntrain_terms['aspect_name'] = train_terms['aspect'].map(aspect_map)\n\n# Overall statistics\nprint(\"=\"*70)\nprint(\"📊 LABEL DISTRIBUTION STATISTICS\")\nprint(\"=\"*70)\nprint(f\"Total annotations:        {len(train_terms):,}\")\nprint(f\"Unique proteins:          {train_terms['EntryID'].nunique():,}\")\nprint(f\"Unique GO terms:          {train_terms['term'].nunique():,}\")\nprint(f\"Avg terms per protein:    {len(train_terms) / train_terms['EntryID'].nunique():.2f}\")\nprint(\"=\"*70)\n\n# Aspect distribution\naspect_counts = train_terms['aspect_name'].value_counts()\nprint(\"\\n📊 Distribution by Subontology:\")\nfor aspect, count in aspect_counts.items():\n    pct = count / len(train_terms) * 100\n    print(f\"   {aspect}: {count:,} ({pct:.1f}%)\")\n\n# Visualize aspect distribution\nfig = make_subplots(\n    rows=1, cols=2,\n    subplot_titles=('Annotation Count', 'Unique Terms'),\n    specs=[[{\"type\": \"bar\"}, {\"type\": \"bar\"}]]\n)\n\n# Annotation counts\nfig.add_trace(\n    go.Bar(x=aspect_counts.index, y=aspect_counts.values,\n           marker_color=['#3498db', '#e74c3c', '#2ecc71'],\n           text=aspect_counts.values, textposition='outside'),\n    row=1, col=1\n)\n\n# Unique terms per aspect\nunique_terms = train_terms.groupby('aspect_name')['term'].nunique()\nfig.add_trace(\n    go.Bar(x=unique_terms.index, y=unique_terms.values,\n           marker_color=['#3498db', '#e74c3c', '#2ecc71'],\n           text=unique_terms.values, textposition='outside'),\n    row=1, col=2\n)\n\nfig.update_layout(height=400, showlegend=False,\n                 title_text='🏷️ GO Term Distribution by Subontology')\nfig.update_xaxes(title_text=\"Subontology\")\nfig.update_yaxes(title_text=\"Count\", row=1, col=1)\nfig.update_yaxes(title_text=\"Unique Terms\", row=1, col=2)\nfig.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Part 2: Term Frequency Analysis\n\n**The Long-Tail Problem:**\n\nCAFA exhibits extreme class imbalance - a few terms appear very frequently,\nwhile most terms are rare. This is the classic \"long-tail distribution\" that\nmakes this challenge particularly difficult.\n\n**Key Statistics:**\n- Most frequent term vs least frequent term ratio\n- Percentage of terms appearing < 10 times\n- Head vs tail contribution to total annotations","metadata":{}},{"cell_type":"code","source":"# Calculate term frequencies\nterm_freq = train_terms['term'].value_counts()\n\n# Statistics\nprint(\"=\"*70)\nprint(\"📊 TERM FREQUENCY STATISTICS\")\nprint(\"=\"*70)\nprint(f\"Total unique terms:       {len(term_freq):,}\")\nprint(f\"Most frequent term:       {term_freq.iloc[0]:,} proteins\")\nprint(f\"Least frequent term:      {term_freq.iloc[-1]:,} proteins\")\nprint(f\"Median frequency:         {term_freq.median():.0f} proteins\")\nprint(f\"Mean frequency:           {term_freq.mean():.1f} proteins\")\nprint(\"=\"*70)\n\n# Rare terms analysis\nrare_terms = (term_freq <= 5).sum()\nvery_rare = (term_freq == 1).sum()\nprint(f\"\\n📊 Rare Terms Analysis:\")\nprint(f\"   Terms appearing ≤5 times:  {rare_terms:,} ({rare_terms/len(term_freq)*100:.1f}%)\")\nprint(f\"   Terms appearing once:      {very_rare:,} ({very_rare/len(term_freq)*100:.1f}%)\")\n\n# Head vs tail\nhead_20_pct = term_freq.iloc[:int(len(term_freq)*0.2)].sum()\ntail_80_pct = term_freq.iloc[int(len(term_freq)*0.2):].sum()\nprint(f\"\\n📊 Pareto Analysis:\")\nprint(f\"   Top 20% terms cover:       {head_20_pct/term_freq.sum()*100:.1f}% of annotations\")\nprint(f\"   Bottom 80% terms cover:    {tail_80_pct/term_freq.sum()*100:.1f}% of annotations\")\n\n# Visualization: Log-scale distribution\nfig = go.Figure()\n\nfig.add_trace(go.Scatter(\n    x=np.arange(1, len(term_freq) + 1),\n    y=term_freq.values,\n    mode='lines',\n    name='Term Frequency',\n    line=dict(color='#3498db', width=2),\n    fill='tozeroy'\n))\n\nfig.update_layout(\n    title='📉 Term Frequency Distribution (Long-Tail)',\n    xaxis_title='Term Rank',\n    yaxis_title='Frequency (log scale)',\n    yaxis_type='log',\n    height=500,\n    hovermode='x'\n)\n\nfig.show()\n\n# Top 20 most frequent terms\nprint(\"\\n🔝 Top 20 Most Frequent Terms:\")\ntop_20 = term_freq.head(20)\nfor i, (term, count) in enumerate(top_20.items(), 1):\n    term_name = go_graph.nodes.get(term, {}).get('name', 'Unknown')\n    print(f\"   {i:2d}. {term}: {count:5,} | {term_name}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Part 3: Information Accretion (IA) Weights\n\n**What is IA?**\n\nInformation Accretion measures how \"informative\" a GO term is:\n- **High IA**: Rare, specific terms (hard to predict, high value)\n- **Low IA**: Common, general terms (easier to predict, lower value)\n\nThe competition metric (F-max) is weighted by IA, meaning:\n✅ Correctly predicting rare terms is rewarded more\n❌ Missing rare terms is penalized more\n\n**Key Question**: Is there a relationship between term frequency and IA?","metadata":{}},{"cell_type":"code","source":"# Merge frequency and IA\nterm_freq_df = term_freq.reset_index()\nterm_freq_df.columns = ['term', 'frequency']\nmerged = term_freq_df.merge(ia_df, on='term', how='left')\n\n# Statistics\nprint(\"=\"*70)\nprint(\"📊 INFORMATION ACCRETION (IA) STATISTICS\")\nprint(\"=\"*70)\nprint(f\"Terms with IA values:     {merged['IA'].notna().sum():,}\")\nprint(f\"Mean IA:                  {ia_df['IA'].mean():.3f}\")\nprint(f\"Median IA:                {ia_df['IA'].median():.3f}\")\nprint(f\"Max IA:                   {ia_df['IA'].max():.3f}\")\nprint(f\"Min IA:                   {ia_df['IA'].min():.3f}\")\nprint(\"=\"*70)\n\n# Correlation analysis\ncorr_pearson = merged[['frequency', 'IA']].corr().iloc[0, 1]\ncorr_spearman = merged[['frequency', 'IA']].corr(method='spearman').iloc[0, 1]\n\nprint(f\"\\n📊 Frequency vs IA Correlation:\")\nprint(f\"   Pearson:  {corr_pearson:.4f}\")\nprint(f\"   Spearman: {corr_spearman:.4f}\")\nprint(f\"\\n💡 Negative correlation confirms: Rare terms have higher IA!\")\n\n# Visualization\nfig = make_subplots(\n    rows=1, cols=2,\n    subplot_titles=('Frequency vs IA (log-scale)', 'IA Distribution'),\n)\n\n# Scatter plot\nsample = merged.sample(min(10000, len(merged)), random_state=42)\nfig.add_trace(\n    go.Scatter(\n        x=sample['frequency'], y=sample['IA'],\n        mode='markers',\n        marker=dict(size=5, color=sample['IA'], colorscale='Viridis',\n                   showscale=True, colorbar=dict(title=\"IA\")),\n        text=sample['term'],\n        hovertemplate='<b>%{text}</b><br>Freq: %{x}<br>IA: %{y:.3f}<extra></extra>'\n    ),\n    row=1, col=1\n)\n\n# IA distribution\nfig.add_trace(\n    go.Histogram(x=ia_df['IA'], nbinsx=50, marker_color='lightcoral'),\n    row=1, col=2\n)\n\nfig.update_xaxes(title_text=\"Frequency (log)\", type=\"log\", row=1, col=1)\nfig.update_xaxes(title_text=\"IA Value\", row=1, col=2)\nfig.update_yaxes(title_text=\"IA\", row=1, col=1)\nfig.update_yaxes(title_text=\"Count\", row=1, col=2)\nfig.update_layout(height=500, showlegend=False,\n                 title_text='⚖️ Information Accretion Analysis')\nfig.show()\n\n# High-value targets\nhigh_ia = merged.nlargest(15, 'IA')\nprint(\"\\n🎯 Top 15 Highest-Value Terms (High IA):\")\nfor i, row in enumerate(high_ia.itertuples(), 1):\n    term_name = go_graph.nodes.get(row.term, {}).get('name', 'Unknown')\n    print(f\"   {i:2d}. IA={row.IA:.3f} | Freq={row.frequency:3d} | {term_name[:50]}\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Part 4: Label Cardinality (Multi-Label Nature)\n\n**Multi-Label Classification:**\n\nUnlike traditional classification where each sample has ONE label, in CAFA:\n- Each protein can have MULTIPLE GO terms\n- Terms can be from different subontologies\n- This affects model architecture and evaluation\n\n**Questions to explore:**\n- How many labels does a typical protein have?\n- Are there proteins with very few or very many labels?\n- How does label count vary by subontology?","metadata":{}},{"cell_type":"code","source":"# Calculate labels per protein\nlabels_per_protein = train_terms.groupby('EntryID')['term'].count()\n\nprint(\"=\"*70)\nprint(\"📊 LABEL CARDINALITY STATISTICS\")\nprint(\"=\"*70)\nprint(f\"Mean labels per protein:  {labels_per_protein.mean():.2f}\")\nprint(f\"Median labels per protein:{labels_per_protein.median():.0f}\")\nprint(f\"Min labels:               {labels_per_protein.min()}\")\nprint(f\"Max labels:               {labels_per_protein.max()}\")\nprint(f\"Std deviation:            {labels_per_protein.std():.2f}\")\nprint(\"=\"*70)\n\n# Percentiles\npercentiles = labels_per_protein.quantile([0.25, 0.5, 0.75, 0.90, 0.95, 0.99])\nprint(\"\\n📊 Label Count Percentiles:\")\nfor p, val in percentiles.items():\n    print(f\"   {int(p*100)}th percentile: {val:.0f} labels\")\n\n# Visualization\nfig = make_subplots(\n    rows=2, cols=2,\n    subplot_titles=('Overall Distribution', 'By Subontology', \n                   'Cumulative Distribution', 'Violin Plot by Aspect'),\n    specs=[[{\"type\": \"histogram\"}, {\"type\": \"bar\"}],\n           [{\"type\": \"scatter\"}, {\"type\": \"violin\"}]]\n)\n\n# 1. Overall histogram\nfig.add_trace(\n    go.Histogram(x=labels_per_protein, nbinsx=50, marker_color='lightblue'),\n    row=1, col=1\n)\n\n# 2. By subontology\nlabels_by_aspect = train_terms.groupby(['EntryID', 'aspect_name'])['term'].count().reset_index()\navg_by_aspect = labels_by_aspect.groupby('aspect_name')['term'].mean().sort_values(ascending=False)\nfig.add_trace(\n    go.Bar(x=avg_by_aspect.index, y=avg_by_aspect.values,\n           marker_color=['#3498db', '#e74c3c', '#2ecc71'],\n           text=[f'{v:.2f}' for v in avg_by_aspect.values],\n           textposition='outside'),\n    row=1, col=2\n)\n\n# 3. Cumulative distribution\nsorted_labels = np.sort(labels_per_protein)\ncumulative = np.arange(1, len(sorted_labels) + 1) / len(sorted_labels) * 100\nfig.add_trace(\n    go.Scatter(x=sorted_labels, y=cumulative, mode='lines',\n               line=dict(color='green', width=2)),\n    row=2, col=1\n)\n\n# 4. Violin plot by aspect\nfor aspect in ['BPO', 'CCO', 'MFO']:\n    aspect_data = labels_by_aspect[labels_by_aspect['aspect_name'] == aspect]['term']\n    fig.add_trace(\n        go.Violin(y=aspect_data, name=aspect, box_visible=True, meanline_visible=True),\n        row=2, col=2\n    )\n\nfig.update_xaxes(title_text=\"Number of Labels\", row=1, col=1)\nfig.update_xaxes(title_text=\"Subontology\", row=1, col=2)\nfig.update_xaxes(title_text=\"Number of Labels\", row=2, col=1)\nfig.update_yaxes(title_text=\"Count\", row=1, col=1)\nfig.update_yaxes(title_text=\"Avg Labels\", row=1, col=2)\nfig.update_yaxes(title_text=\"Cumulative %\", row=2, col=1)\nfig.update_yaxes(title_text=\"Label Count\", row=2, col=2)\n\nfig.update_layout(height=800, showlegend=False,\n                 title_text='🌐 Multi-Label Cardinality Analysis')\nfig.show()\n\nprint(\"\\n💡 Key Insights:\")\nprint(f\"   • Most proteins have {labels_per_protein.median():.0f}-{labels_per_protein.quantile(0.75):.0f} labels\")\nprint(f\"   • {(labels_per_protein == 1).sum():,} proteins have only 1 label\")\nprint(f\"   • {(labels_per_protein > 20).sum():,} proteins have >20 labels (complex functions)\")\n","metadata":{"trusted":true,"execution":{"iopub.status.idle":"2025-11-04T12:52:47.249466Z","shell.execute_reply.started":"2025-11-04T12:52:46.553520Z","shell.execute_reply":"2025-11-04T12:52:47.248485Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: white; margin: 0; font-size: 32px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\">\n    🌳 Gene Ontology Hierarchy\n  </h1>\n  <p style=\"color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-size: 16px;\">\n    Exploring the DAG structure and term relationships\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"Gene Ontology is organized as a **Directed Acyclic Graph (DAG)**:\n- **Directed**: Parent → Child relationships (general → specific)\n- **Acyclic**: No circular dependencies\n- **Multiple inheritance**: A term can have multiple parents\n\n**Key relationships:**\n- `is_a`: Child is a type of parent (e.g., \"ATP binding\" is_a \"nucleotide binding\")\n- `part_of`: Child is part of parent process\n\n**True Path Rule**: If a protein has term X, it also has all ancestors of X","metadata":{}},{"cell_type":"code","source":"# Define ontology roots\nroots = {\n    'BPO': 'GO:0008150',  # biological_process\n    'CCO': 'GO:0005575',  # cellular_component\n    'MFO': 'GO:0003674'   # molecular_function\n}\n\n# Basic graph statistics\nprint(\"=\"*70)\nprint(\"🌳 GENE ONTOLOGY GRAPH STRUCTURE\")\nprint(\"=\"*70)\nprint(f\"Total GO terms (nodes):   {go_graph.number_of_nodes():,}\")\nprint(f\"Total relationships:      {go_graph.number_of_edges():,}\")\nprint(f\"Avg edges per node:       {go_graph.number_of_edges()/go_graph.number_of_nodes():.2f}\")\nprint(\"=\"*70)\n\n# Analyze each subontology\nprint(\"\\n📊 Subontology Statistics:\")\nfor name, root in roots.items():\n    if root in go_graph:\n        # Get all descendants\n        descendants = nx.descendants(go_graph, root)\n        descendants.add(root)\n        \n        # Count terms in training set\n        aspect_code = {'BPO': 'P', 'CCO': 'C', 'MFO': 'F'}[name]\n        terms_in_train = train_terms[train_terms['aspect'] == aspect_code]['term'].nunique()\n        \n        print(f\"\\n   {name}:\")\n        print(f\"      Total terms in GO:     {len(descendants):,}\")\n        print(f\"      Used in training:      {terms_in_train:,} ({terms_in_train/len(descendants)*100:.1f}%)\")\n\n# Sample term exploration\nsample_term = 'GO:0005515'  # protein binding\nif sample_term in go_graph:\n    term_info = go_graph.nodes[sample_term]\n    print(f\"\\n🔍 Example Term: {sample_term}\")\n    print(f\"   Name: {term_info.get('name', 'N/A')}\")\n    print(f\"   Namespace: {term_info.get('namespace', 'N/A')}\")\n    print(f\"   Definition: {term_info.get('def', 'N/A')[:100]}...\")\n    \n    # Parents and children\n    parents = list(go_graph.predecessors(sample_term))\n    children = list(go_graph.successors(sample_term))\n    print(f\"   Parent terms: {len(parents)}\")\n    print(f\"   Child terms: {len(children)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:47.250490Z","iopub.execute_input":"2025-11-04T12:52:47.250758Z","iopub.status.idle":"2025-11-04T12:52:47.611766Z","shell.execute_reply.started":"2025-11-04T12:52:47.250738Z","shell.execute_reply":"2025-11-04T12:52:47.610958Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: #334155; margin: 0; font-size: 32px; text-shadow: 1px 1px 2px rgba(255,255,255,0.5);\">\n    📏 Term Depth Analysis\n  </h1>\n  <p style=\"color: #475569; margin: 10px 0 0 0; font-size: 16px;\">\n    Understanding hierarchy depth and IA relationships\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"**Depth** = shortest path from root to term\n\n**Why depth matters:**\n- Shallow terms (depth 1-3): General, common functions\n- Medium depth (4-7): Specific functional categories  \n- Deep terms (8+): Highly specific, rare functions\n\n**Hypothesis**: Deeper terms should have higher IA (more informative)","metadata":{}},{"cell_type":"code","source":"def calculate_depths(graph, root):\n    \"\"\"Calculate shortest path from root to all descendants\"\"\"\n    depths = {}\n    if root not in graph:\n        return depths\n    \n    # Use ancestors (reversed direction in obonet)\n    depths[root] = 0\n    \n    # Get all nodes that can reach the root\n    for node in graph.nodes():\n        if node == root:\n            continue\n        try:\n            # In obonet, edges point FROM child TO parent\n            # So we need shortest path FROM node TO root\n            depth = nx.shortest_path_length(graph, node, root)\n            depths[node] = depth\n        except nx.NetworkXNoPath:\n            pass\n    \n    return depths\n\n# Calculate depths for all subontologies\nall_depths = {}\nfor name, root in roots.items():\n    depths = calculate_depths(go_graph, root)\n    all_depths[name] = depths\n    \n    if depths:\n        print(f\"\\n📏 {name} Depth Statistics:\")\n        depth_values = list(depths.values())\n        print(f\"   Max depth:     {max(depth_values)}\")\n        print(f\"   Mean depth:    {np.mean(depth_values):.2f}\")\n        print(f\"   Median depth:  {np.median(depth_values):.0f}\")\n\n# Create depth dataframe for training terms\ndepth_data = []\nfor _, row in train_terms.iterrows():\n    term = row['term']\n    aspect = row['aspect_name']\n    \n    if aspect in all_depths and term in all_depths[aspect]:\n        depth = all_depths[aspect][term]\n        depth_data.append({\n            'term': term,\n            'aspect': aspect,\n            'depth': depth\n        })\n\ndepth_df = pd.DataFrame(depth_data)\n\n# Check if we have data\nif len(depth_df) == 0:\n    print(\"\\n⚠️ Warning: No depth data calculated. Check GO graph structure.\")\nelse:\n    print(f\"\\n✅ Calculated depths for {len(depth_df):,} terms\")\n\n# Merge with IA (only if we have depth data)\nif len(depth_df) > 0:\n    depth_ia = depth_df.merge(ia_df, on='term', how='left')\n    \n    # Visualization\n    fig = make_subplots(\n        rows=1, cols=2,\n        subplot_titles=('Depth Distribution by Subontology', 'Depth vs IA Relationship')\n    )\n    \n    # 1. Depth distribution\n    for aspect in ['BPO', 'CCO', 'MFO']:\n        aspect_depths = depth_df[depth_df['aspect'] == aspect]['depth']\n        if len(aspect_depths) > 0:\n            fig.add_trace(\n                go.Violin(y=aspect_depths, name=aspect, box_visible=True, meanline_visible=True),\n                row=1, col=1\n            )\n    \n    # 2. Depth vs IA scatter\n    depth_ia_clean = depth_ia.dropna()\n    if len(depth_ia_clean) > 0:\n        sample = depth_ia_clean.sample(min(5000, len(depth_ia_clean)), random_state=42)\n        fig.add_trace(\n            go.Scatter(\n                x=sample['depth'], y=sample['IA'],\n                mode='markers',\n                marker=dict(size=4, color=sample['IA'], colorscale='Viridis',\n                           opacity=0.6),\n                showlegend=False\n            ),\n            row=1, col=2\n        )\n        \n        # Add trend line\n        from scipy import stats\n        slope, intercept, r_value, p_value, std_err = stats.linregress(\n            depth_ia_clean['depth'], depth_ia_clean['IA']\n        )\n        line_x = np.array([depth_ia_clean['depth'].min(), depth_ia_clean['depth'].max()])\n        line_y = slope * line_x + intercept\n        fig.add_trace(\n            go.Scatter(x=line_x, y=line_y, mode='lines',\n                       line=dict(color='red', width=2, dash='dash'),\n                       name=f'Trend (R²={r_value**2:.3f})'),\n            row=1, col=2\n        )\n        \n        print(f\"\\n💡 Depth-IA Correlation: {r_value:.3f}\")\n        print(f\"   Positive correlation confirms: Deeper terms have higher IA!\")\n    \n    fig.update_xaxes(title_text=\"Subontology\", row=1, col=1)\n    fig.update_xaxes(title_text=\"Depth\", row=1, col=2)\n    fig.update_yaxes(title_text=\"Depth\", row=1, col=1)\n    fig.update_yaxes(title_text=\"IA\", row=1, col=2)\n    fig.update_layout(height=500, title_text='📏 GO Term Depth Analysis')\n    fig.show()\nelse:\n    print(\"⚠️ Skipping visualization due to missing depth data\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:52:47.612759Z","iopub.execute_input":"2025-11-04T12:52:47.613131Z","iopub.status.idle":"2025-11-04T12:53:17.923425Z","shell.execute_reply.started":"2025-11-04T12:52:47.613110Z","shell.execute_reply":"2025-11-04T12:53:17.922079Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: white; margin: 0; font-size: 32px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\">\n    🦠 Taxonomy Analysis\n  </h1>\n  <p style=\"color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-size: 16px;\">\n    Understanding species distribution in training data\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"\n**Why taxonomy matters:**\n- Proteins from related species often have similar functions\n- Evolutionary conservation can be leveraged for prediction\n- Understanding species distribution helps assess model generalization\n\n**Model organisms** (well-studied species) dominate the training data","metadata":{}},{"cell_type":"code","source":"# Species frequency\nspecies_counts = train_tax['TaxonID'].value_counts()\n\nprint(\"=\"*70)\nprint(\"🦠 TAXONOMY STATISTICS\")\nprint(\"=\"*70)\nprint(f\"Total proteins:           {len(train_tax):,}\")\nprint(f\"Unique species (taxa):    {len(species_counts):,}\")\nprint(f\"Most common taxon:        {species_counts.iloc[0]:,} proteins\")\nprint(f\"Median proteins per taxon:{species_counts.median():.0f}\")\nprint(\"=\"*70)\n\n# Top species (common model organisms)\nprint(\"\\n🔝 Top 15 Species by Protein Count:\")\ntop_species = species_counts.head(15)\n\n# Common model organism IDs\nmodel_organisms = {\n    9606: 'Homo sapiens (Human)',\n    10090: 'Mus musculus (Mouse)',\n    10116: 'Rattus norvegicus (Rat)',\n    7227: 'Drosophila melanogaster (Fruit fly)',\n    6239: 'Caenorhabditis elegans (Nematode)',\n    3702: 'Arabidopsis thaliana (Plant)',\n    559292: 'Saccharomyces cerevisiae (Yeast)',\n    83333: 'Escherichia coli K-12',\n    284812: 'Schizosaccharomyces pombe (Fission yeast)'\n}\n\nfor i, (taxon, count) in enumerate(top_species.items(), 1):\n    name = model_organisms.get(taxon, f'TaxonID {taxon}')\n    pct = count / len(train_tax) * 100\n    print(f\"   {i:2d}. {name:40s}: {count:6,} ({pct:5.2f}%)\")\n\n# Visualization\nfig = go.Figure()\n\nfig.add_trace(go.Bar(\n    y=[model_organisms.get(t, f'Taxon {t}') for t in top_species.index],\n    x=top_species.values,\n    orientation='h',\n    marker_color='lightseagreen',\n    text=top_species.values,\n    textposition='outside'\n))\n\nfig.update_layout(\n    title='🦠 Top 15 Species in Training Data',\n    xaxis_title='Number of Proteins',\n    yaxis_title='Species',\n    height=600,\n    yaxis={'categoryorder':'total ascending'}\n)\n\nfig.show()\n\n# Distribution analysis\nprint(\"\\n📊 Distribution Analysis:\")\ntop_10_pct = species_counts.head(10).sum() / len(train_tax) * 100\ntop_50_pct = species_counts.head(50).sum() / len(train_tax) * 100\nprint(f\"   Top 10 species cover:     {top_10_pct:.1f}% of proteins\")\nprint(f\"   Top 50 species cover:     {top_50_pct:.1f}% of proteins\")\nprint(f\"   Species with 1 protein:   {(species_counts == 1).sum():,}\")\nprint(f\"   Species with <10 proteins:{(species_counts < 10).sum():,}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:53:17.926182Z","iopub.execute_input":"2025-11-04T12:53:17.926486Z","iopub.status.idle":"2025-11-04T12:53:17.996833Z","shell.execute_reply.started":"2025-11-04T12:53:17.926465Z","shell.execute_reply":"2025-11-04T12:53:17.995796Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: white; margin: 0; font-size: 32px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\">\n    🔗 Term Co-occurrence Analysis\n  </h1>\n  <p style=\"color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-size: 16px;\">\n    Discovering functional relationships between GO terms\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"**Co-occurrence patterns reveal:**\n- Terms that commonly appear together\n- Potential functional modules\n- Cross-ontology relationships\n\nThis information can help:\n- Feature engineering (term combinations)\n- Model architecture (multi-task learning)\n- Post-processing (co-prediction rules)","metadata":{}},{"cell_type":"code","source":"# Sample proteins for co-occurrence analysis (for performance)\nsample_size = min(10000, train_terms['EntryID'].nunique())\nsample_proteins = train_terms['EntryID'].unique()[:sample_size]\nsample_data = train_terms[train_terms['EntryID'].isin(sample_proteins)]\n\n# Calculate co-occurrence\nprint(\"🔗 Calculating term co-occurrence patterns...\")\nco_occurrence = Counter()\n\nfor protein, group in tqdm(sample_data.groupby('EntryID'), desc=\"Processing\"):\n    terms = group['term'].tolist()\n    if len(terms) > 1:\n        # Count all pairs\n        for i in range(len(terms)):\n            for j in range(i+1, len(terms)):\n                pair = tuple(sorted([terms[i], terms[j]]))\n                co_occurrence[pair] += 1\n\n# Top co-occurring pairs\ntop_pairs = sorted(co_occurrence.items(), key=lambda x: x[1], reverse=True)[:20]\n\nprint(\"\\n🔝 Top 20 Co-occurring Term Pairs:\")\nprint(f\"{'Rank':<6}{'Count':<8}{'Term 1':<15}{'Term 2':<15}{'Names'}\")\nprint(\"=\"*100)\n\nfor i, (pair, count) in enumerate(top_pairs, 1):\n    term1, term2 = pair\n    name1 = go_graph.nodes.get(term1, {}).get('name', 'Unknown')[:20]\n    name2 = go_graph.nodes.get(term2, {}).get('name', 'Unknown')[:20]\n    print(f\"{i:<6}{count:<8}{term1:<15}{term2:<15}{name1} + {name2}\")\n\n# Cross-ontology co-occurrence\nprint(\"\\n📊 Cross-Ontology Co-occurrence:\")\ncross_onto = defaultdict(int)\n\nfor protein, group in sample_data.groupby('EntryID'):\n    aspects = set(group['aspect'].tolist())\n    if len(aspects) > 1:\n        for asp in aspects:\n            cross_onto[asp] += 1\n\ntotal_proteins = len(sample_data['EntryID'].unique())\nprint(f\"   Proteins with MF+BP+CC: {len(sample_data.groupby('EntryID')['aspect'].apply(lambda x: len(set(x)) == 3))}\")\nprint(f\"   Proteins with 2 aspects: {len(sample_data.groupby('EntryID')['aspect'].apply(lambda x: len(set(x)) == 2))}\")\nprint(f\"   Proteins with 1 aspect:  {len(sample_data.groupby('EntryID')['aspect'].apply(lambda x: len(set(x)) == 1))}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:53:17.997698Z","iopub.execute_input":"2025-11-04T12:53:17.997972Z","iopub.status.idle":"2025-11-04T12:53:21.029936Z","shell.execute_reply.started":"2025-11-04T12:53:17.997943Z","shell.execute_reply":"2025-11-04T12:53:21.028911Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 25px; border-radius: 12px; margin: 25px 0; box-shadow: 0 6px 15px rgba(0,0,0,0.2);\">\n  <h1 style=\"color: white; margin: 0; font-size: 32px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);\">\n    🎯 Train-Test Analysis\n  </h1>\n  <p style=\"color: rgba(255,255,255,0.9); margin: 10px 0 0 0; font-size: 16px;\">\n    Comparing distributions and identifying potential shifts\n  </p>\n</div>","metadata":{}},{"cell_type":"markdown","source":"**Important**: This is a **prospective evaluation**\n- Test proteins currently have NO annotations\n- During evaluation, only proteins that gain experimental validation will be scored\n- We can still analyze distributional properties\n\n**Key questions:**\n- How do test sequences compare to training sequences?\n- Is there protein ID overlap?\n- Are sequence length distributions similar?","metadata":{}},{"cell_type":"code","source":"# Load test sequences\nprint(\"Loading test sequences...\")\ntest_seqs = []\nfor record in tqdm(SeqIO.parse(paths['test_seq'], 'fasta'), desc=\"Loading test set\"):\n    entry_id = record.id.split('|')[1] if '|' in record.id else record.id\n    test_seqs.append({\n        'EntryID': entry_id,\n        'length': len(record.seq)\n    })\n\ntest_df = pd.DataFrame(test_seqs)\n\n# Basic statistics\nprint(\"\\n\" + \"=\"*70)\nprint(\"🎯 TRAIN-TEST COMPARISON\")\nprint(\"=\"*70)\nprint(f\"Training proteins:        {len(train_seqs):,}\")\nprint(f\"Test proteins:            {len(test_df):,}\")\nprint(f\"Test/Train ratio:         {len(test_df)/len(train_seqs):.2f}x\")\nprint(\"=\"*70)\n\n# Protein ID overlap\ntrain_ids = set(train_seqs['EntryID'])\ntest_ids = set(test_df['EntryID'])\noverlap = train_ids.intersection(test_ids)\n\nprint(f\"\\n📊 Protein ID Overlap:\")\nprint(f\"   Overlapping proteins:     {len(overlap):,}\")\nprint(f\"   % of train in test:       {len(overlap)/len(train_ids)*100:.2f}%\")\nprint(f\"   % of test in train:       {len(overlap)/len(test_ids)*100:.2f}%\")\nprint(f\"   New proteins in test:     {len(test_ids - train_ids):,}\")\n\n# Sequence length comparison\nprint(f\"\\n📏 Sequence Length Comparison:\")\nprint(f\"{'Metric':<20}{'Train':<15}{'Test':<15}{'Difference'}\")\nprint(\"=\"*70)\nprint(f\"{'Mean':<20}{train_seqs['length'].mean():<15.1f}{test_df['length'].mean():<15.1f}{test_df['length'].mean() - train_seqs['length'].mean():.1f}\")\nprint(f\"{'Median':<20}{train_seqs['length'].median():<15.0f}{test_df['length'].median():<15.0f}{test_df['length'].median() - train_seqs['length'].median():.0f}\")\nprint(f\"{'Std Dev':<20}{train_seqs['length'].std():<15.1f}{test_df['length'].std():<15.1f}{test_df['length'].std() - train_seqs['length'].std():.1f}\")\nprint(f\"{'Min':<20}{train_seqs['length'].min():<15}{test_df['length'].min():<15}{test_df['length'].min() - train_seqs['length'].min()}\")\nprint(f\"{'Max':<20}{train_seqs['length'].max():<15,}{test_df['length'].max():<15,}{test_df['length'].max() - train_seqs['length'].max():,}\")\n\n# Visualization\nfig = go.Figure()\n\nfig.add_trace(go.Histogram(\n    x=train_seqs['length'],\n    name='Train',\n    opacity=0.7,\n    marker_color='lightblue',\n    nbinsx=100\n))\n\nfig.add_trace(go.Histogram(\n    x=test_df['length'],\n    name='Test',\n    opacity=0.7,\n    marker_color='lightcoral',\n    nbinsx=100\n))\n\nfig.update_layout(\n    title='📊 Sequence Length Distribution: Train vs Test',\n    xaxis_title='Sequence Length',\n    yaxis_title='Count',\n    barmode='overlay',\n    height=500\n)\n\nfig.show()\n\nprint(\"\\n✅ Good news: Distributions are very similar!\")\nprint(\"   → No severe distribution shift\")\nprint(\"   → Model should generalize well\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-04T12:53:21.031061Z","iopub.execute_input":"2025-11-04T12:53:21.031899Z","iopub.status.idle":"2025-11-04T12:53:23.427852Z","shell.execute_reply.started":"2025-11-04T12:53:21.031870Z","shell.execute_reply":"2025-11-04T12:53:23.426667Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<div style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px; border-radius: 15px; text-align: center; color: white; margin: 40px 0;\">\n    <h1 style=\"margin: 0; font-size: 2.5em;\">🎯 Key Findings Summary</h1>\n    <p style=\"font-size: 1.2em; margin-top: 15px;\">Data-driven insights for building effective prediction models</p>\n</div>\n\n---\n\n## 📊 Critical Discoveries\n\n| Challenge | What We Found | Potential Approaches |\n|-----------|---------------|---------------------|\n| 🔴 **Class Imbalance** | ~50% of GO terms appear ≤5 times. Top 20% terms = 80% of annotations | Focal loss, class weighting, sampling strategies |\n| ⚖️ **IA Weighting** | Negative correlation between frequency and IA (r = -0.097) | IA-aware thresholds, ensemble weighting |\n| 🌳 **DAG Structure** | 40K+ terms, 77K+ relationships. Median depth: 4-5 levels | Hierarchical propagation, parent-aware loss |\n| 🧬 **Sequences** | Length range: 3-35,213 AA. Median: 409 AA | Protein language models (ESM2, ProtBERT) |\n| 🌐 **Multi-label** | Avg 6.5 labels/protein. Strong co-occurrence patterns | Multi-task learning, attention mechanisms |\n\n---\n\n## 💡 Modeling Considerations\n\n<div style=\"background: #fffbeb; border-left: 5px solid #f59e0b; padding: 20px; border-radius: 5px; margin: 25px 0;\">\n    <h3 style=\"color: #92400e; margin-top: 0;\">⚠️ Important Notes</h3>\n    <ul style=\"color: #78350f; line-height: 1.8;\">\n        <li><strong>Prospective Evaluation:</strong> Test proteins currently lack annotations - only those gaining experimental evidence during the competition will be scored</li>\n        <li><strong>Hierarchical Consistency:</strong> Predictions must respect the True Path Rule (if a child term is predicted, all ancestor terms must also be predicted)</li>\n        <li><strong>IA Focus:</strong> The evaluation metric heavily weights rare, specific terms - a model predicting only common terms will score poorly</li>\n        <li><strong>Validation Strategy:</strong> Random splits may not reflect real-world performance - consider taxonomy-based or time-based splits</li>\n    </ul>\n</div>\n\n---\n\n## 🏆 Suggested Next Steps\n\n**1️⃣ Baseline**\n- Start with simple frequency-based or k-NN baselines to establish performance floor\n\n**2️⃣ Embeddings**\n- Extract features using pre-trained protein language models (ESM2, ProtT5)\n\n**3️⃣ Architecture**\n- Experiment with multi-task heads, hierarchical classifiers, attention mechanisms\n\n**4️⃣ Optimization**\n- Tune thresholds per ontology, apply DAG constraints, ensemble diverse models\n\n---\n\n<div style=\"background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px; border-radius: 15px; text-align: center; color: white; margin: 40px 0;\">\n    <h2 style=\"margin: 0; font-size: 2em;\">🙏 Thank You!</h2>\n    <p style=\"font-size: 1.1em; margin: 20px 0; line-height: 1.6;\">\n        If this analysis helped you understand the data better,<br>\n        please consider giving it an <strong>upvote</strong> 👍\n    </p>\n    <p style=\"font-size: 0.9em; margin: 25px 0 0 0;\">💬 Questions? Comments? Share your thoughts below!</p>\n</div>\n\n<div style=\"text-align: center; padding: 20px; color: #64748b;\">\n    <p>Created with 💜 for the CAFA6 community</p>\n    <p>Good luck with your models! 🚀🧬</p>\n</div>","metadata":{}}]}