{"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":118765,"databundleVersionId":15231210,"isSourceIdPinned":false}],"dockerImageVersionId":31286,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nfrom pathlib import Path\nfrom sklearn.ensemble import HistGradientBoostingRegressor\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.multioutput import MultiOutputRegressor\nimport warnings\nwarnings.filterwarnings('ignore')\n\nBASE = \"/kaggle/input/competitions/stanford-rna-3d-folding-2\"\n\ndef safe_read_csv(path, default=None):\n    try:\n        return pd.read_csv(path)\n    except FileNotFoundError:\n        print(f\"Warning: {path} not found\")\n        return default if default is not None else pd.DataFrame()\n\ntest_sequences = safe_read_csv(f\"{BASE}/test_sequences.csv\")\nsample_sub = safe_read_csv(f\"{BASE}/sample_submission.csv\")\nval_sequences = safe_read_csv(f\"{BASE}/validation_sequences.csv\")\nval_labels = safe_read_csv(f\"{BASE}/validation_labels.csv\")\n\ndef generate_helical_prediction(sub_df):\n    predictions = []\n    for idx, row in sub_df.iterrows():\n        target_id, resid = row['ID'].split('_')\n        resid = int(resid)\n        seq_row = test_sequences[test_sequences['target_id'] == target_id]\n        sequence = seq_row.iloc[0]['sequence'] if not seq_row.empty else ''\n        seq_len = len(sequence) if isinstance(sequence, str) else 0\n        \n        radius = 9.0\n        rise = 2.56\n        twist = 32.73\n        angle = resid * np.radians(twist)\n        \n        base_x = radius * np.cos(angle)\n        base_y = radius * np.sin(angle)\n        base_z = resid * rise\n        \n        coords = []\n        for i in range(5):\n            offset = i * 0.5\n            coords.extend([\n                base_x + offset,\n                base_y - offset,\n                base_z + offset\n            ])\n        predictions.append(coords)\n    sub_df.iloc[:, 3:] = np.array(predictions)\n    return sub_df\n\nif val_labels.empty or val_sequences.empty:\n    sample_sub = generate_helical_prediction(sample_sub)\n    sample_sub.to_csv(\"/kaggle/working/submission.csv\", index=False)\n    import sys\n    sys.exit(0)\n\ndef one_hot_nt(nt):\n    return [1 if nt == c else 0 for c in ['A','U','G','C']]\n\ndef extract_positional_features(sequence, resid):\n    features = {}\n    seq_len = len(sequence)\n    pos = resid - 1\n    \n    current = sequence[pos] if 0 <= pos < seq_len else ''\n    features['current_nt'] = one_hot_nt(current) if current else [0]*4\n    \n    prev_pos = pos - 1\n    prev = sequence[prev_pos] if 0 <= prev_pos < seq_len else ''\n    features['prev_nt'] = one_hot_nt(prev) if prev else [0]*4\n    \n    next_pos = pos + 1\n    next_ = sequence[next_pos] if 0 <= next_pos < seq_len else ''\n    features['next_nt'] = one_hot_nt(next_) if next_ else [0]*4\n    \n    features['pos_in_seq'] = resid/seq_len if seq_len > 0 else 0\n    features['gc_content'] = (sequence.count('G') + sequence.count('C'))/seq_len if seq_len > 0 else 0.5\n    \n    return features\n\ntrain_features = []\ntrain_coords = []\n\nfor idx, row in val_labels.iterrows():\n    try:\n        target_id, resid = row['ID'].split('_')\n        resid = int(resid)\n        seq_row = val_sequences[val_sequences['target_id'] == target_id]\n        if seq_row.empty:\n            continue\n            \n        sequence = seq_row.iloc[0]['sequence']\n        if not isinstance(sequence, str) or len(sequence) == 0:\n            continue\n            \n        seq_len = len(sequence)\n        if resid < 1 or resid > seq_len:\n            continue\n\n        pos_features = extract_positional_features(sequence, resid)\n        coord_cols = [f'{ax}_{i}' for i in range(1,6) for ax in ['x','y','z']]\n        coords = row[coord_cols].values.astype(np.float32)\n        \n        if np.any(np.isnan(coords)):\n            continue\n            \n        features = [\n            pos_features['pos_in_seq'],\n            pos_features['gc_content'],\n            *pos_features['current_nt'],\n            *pos_features['prev_nt'],\n            *pos_features['next_nt']\n        ]\n        \n        train_features.append(features)\n        train_coords.append(coords)\n    except:\n        continue\n\nif not train_features:\n    sample_sub = generate_helical_prediction(sample_sub)\nelse:\n    X = np.array(train_features)\n    y = np.array(train_coords)\n    \n    imputer = SimpleImputer()\n    scaler = StandardScaler()\n    X = imputer.fit_transform(X)\n    X = scaler.fit_transform(X)\n    \n    model = MultiOutputRegressor(HistGradientBoostingRegressor(random_state=42))\n    model.fit(X, y)\n    \n    test_features = []\n    fallback_indices = []\n    for idx, row in sample_sub.iterrows():\n        try:\n            target_id, resid = row['ID'].split('_')\n            resid = int(resid)\n            seq_row = test_sequences[test_sequences['target_id'] == target_id]\n            if seq_row.empty:\n                fallback_indices.append(idx)\n                test_features.append([0]*13)\n                continue\n                \n            sequence = seq_row.iloc[0]['sequence']\n            if not isinstance(sequence, str) or len(sequence) == 0:\n                fallback_indices.append(idx)\n                test_features.append([0]*13)\n                continue\n                \n            pos_features = extract_positional_features(sequence, resid)\n            features = [\n                pos_features['pos_in_seq'],\n                pos_features['gc_content'],\n                *pos_features['current_nt'],\n                *pos_features['prev_nt'],\n                *pos_features['next_nt']\n            ]\n            test_features.append(features)\n        except:\n            fallback_indices.append(idx)\n            test_features.append([0]*13)\n    \n    X_test = imputer.transform(test_features)\n    X_test = scaler.transform(X_test)\n    predictions = model.predict(X_test)\n    \n    helical_sub = generate_helical_prediction(sample_sub.copy())\n    for idx in fallback_indices:\n        predictions[idx] = helical_sub.iloc[idx, 3:].values\n        \n    sample_sub.iloc[:, 3:] = np.nan_to_num(predictions, nan=0.0)\n\nsample_sub.to_csv(\"/kaggle/working/submission.csv\", index=False)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-18T06:03:36.621706Z","iopub.execute_input":"2026-03-18T06:03:36.622059Z","iopub.status.idle":"2026-03-18T06:03:57.466618Z","shell.execute_reply.started":"2026-03-18T06:03:36.622026Z","shell.execute_reply":"2026-03-18T06:03:57.465675Z"}},"outputs":[],"execution_count":null}]}