{"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}],"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\nfrom difflib import SequenceMatcher\nfrom tqdm import tqdm\n\n# 1. LOAD\ntrain_seq = pd.read_csv(\n    \"/kaggle/input/competitions/stanford-rna-3d-folding-2/train_sequences.csv\"\n)\n\ntrain_labels = pd.read_csv(\n    \"/kaggle/input/competitions/stanford-rna-3d-folding-2/train_labels.csv\",\n    low_memory=False\n)\n\ntest_seq = pd.read_csv(\n    \"/kaggle/input/competitions/stanford-rna-3d-folding-2/test_sequences.csv\"\n)\n\nsample_sub = pd.read_csv(\n    \"/kaggle/input/competitions/stanford-rna-3d-folding-2/sample_submission.csv\"\n)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:24:52.068376Z","iopub.execute_input":"2026-03-16T16:24:52.068858Z","iopub.status.idle":"2026-03-16T16:25:09.007658Z","shell.execute_reply.started":"2026-03-16T16:24:52.068827Z","shell.execute_reply":"2026-03-16T16:25:09.006373Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 2. PREP LABELS\ntrain_labels[\"target_id\"] = train_labels[\"ID\"].str.split(\"_\").str[0]\ntrain_labels[\"resid_from_id\"] = train_labels[\"ID\"].str.split(\"_\").str[1].astype(int)\ntrain_labels = train_labels.sort_values([\"target_id\", \"resid\"])\n\ntemplate_coords = {}\n\nfor target_id, group in train_labels.groupby(\"target_id\"):\n    coords_df = group[[\"x_1\", \"y_1\", \"z_1\"]].copy()\n\n    coords_df = coords_df.apply(pd.to_numeric, errors=\"coerce\")\n\n    coords_df = coords_df.interpolate(method=\"linear\", axis=0, limit_direction=\"both\")\n\n    coords_df = coords_df.bfill().ffill()\n\n    coords_df = coords_df.fillna(0.0)\n\n    template_coords[target_id] = coords_df.values.astype(np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:25:09.009897Z","iopub.execute_input":"2026-03-16T16:25:09.010259Z","iopub.status.idle":"2026-03-16T16:25:51.753589Z","shell.execute_reply.started":"2026-03-16T16:25:09.01023Z","shell.execute_reply":"2026-03-16T16:25:51.751735Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3. TRAIN DICT\ntrain_dict = {}\nfor _, row in train_seq.iterrows():\n    tid = row[\"target_id\"]\n    if tid in template_coords:\n        train_dict[tid] = row[\"sequence\"]\n\ntrain_by_len = [(tid, seq, len(seq)) for tid, seq in train_dict.items()]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:25:51.755166Z","iopub.execute_input":"2026-03-16T16:25:51.75548Z","iopub.status.idle":"2026-03-16T16:25:52.08605Z","shell.execute_reply.started":"2026-03-16T16:25:51.755453Z","shell.execute_reply":"2026-03-16T16:25:52.085066Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 4. HELPERS\ndef seq_similarity(seq1, seq2):\n    return SequenceMatcher(None, seq1, seq2).ratio()\n\ndef find_top_templates_fast(query_seq, train_by_len, top_k=5, max_candidates=200):\n    qlen = len(query_seq)\n\n    candidates = sorted(\n        train_by_len,\n        key=lambda x: abs(x[2] - qlen)\n    )[:max_candidates]\n\n    scores = []\n    for tid, tmpl_seq, tlen in candidates:\n        score = seq_similarity(query_seq, tmpl_seq)\n        len_penalty = abs(tlen - qlen) / max(qlen, 1)\n        final_score = score - 0.15 * len_penalty\n        scores.append((tid, final_score))\n\n    scores.sort(key=lambda x: x[1], reverse=True)\n    return scores[:top_k]\n\ndef fit_template_to_length(template_coords_array, target_len):\n    arr = np.array(template_coords_array, dtype=np.float32, copy=True)\n\n    arr = np.nan_to_num(arr, nan=0.0, posinf=0.0, neginf=0.0)\n\n    L_template = len(arr)\n\n    if L_template == 0:\n        return np.zeros((target_len, 3), dtype=np.float32)\n\n    if L_template == target_len:\n        return arr\n\n    out = np.zeros((target_len, 3), dtype=np.float32)\n\n    min_len = min(L_template, target_len)\n    out[:min_len] = arr[:min_len]\n\n    if target_len > L_template:\n        out[min_len:] = arr[-1]\n\n    return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:25:52.087386Z","iopub.execute_input":"2026-03-16T16:25:52.087779Z","iopub.status.idle":"2026-03-16T16:25:52.09883Z","shell.execute_reply.started":"2026-03-16T16:25:52.087748Z","shell.execute_reply":"2026-03-16T16:25:52.097692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 5. PREDICT PER TARGET\npred_map = {}\n\nfor test_row in tqdm(test_seq.itertuples(index=False), total=len(test_seq)):\n    target_id = test_row.target_id\n    query_seq = test_row.sequence\n    target_len = len(query_seq)\n\n    top_templates = find_top_templates_fast(query_seq, train_by_len, top_k=5, max_candidates=200)\n\n    predicted_structures = []\n    for rank in range(5):\n        if rank < len(top_templates):\n            tmpl_id = top_templates[rank][0]\n            tmpl_coords = template_coords[tmpl_id]\n            pred_coords = fit_template_to_length(tmpl_coords, target_len)\n        else:\n            pred_coords = np.zeros((target_len, 3), dtype=np.float32)\n\n        predicted_structures.append(pred_coords)\n\n    pred_map[target_id] = {\n        \"sequence\": query_seq,\n        \"structures\": predicted_structures,\n    }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:25:52.10122Z","iopub.execute_input":"2026-03-16T16:25:52.101551Z","iopub.status.idle":"2026-03-16T16:25:57.141162Z","shell.execute_reply.started":"2026-03-16T16:25:52.101524Z","shell.execute_reply":"2026-03-16T16:25:57.140045Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6. FILL SAMPLE SUBMISSION\nsubmission = sample_sub.copy()\nsubmission[\"target_id\"] = submission[\"ID\"].str.split(\"_\").str[0]\nsubmission[\"resid_from_id\"] = submission[\"ID\"].str.split(\"_\").str[1].astype(int)\n\ncoord_cols = []\nfor k in range(1, 6):\n    coord_cols += [f\"x_{k}\", f\"y_{k}\", f\"z_{k}\"]\n\nsubmission[coord_cols] = submission[coord_cols].apply(pd.to_numeric, errors=\"coerce\")\nsubmission[coord_cols] = submission[coord_cols].fillna(0.0).astype(np.float32)\n\nsubmission[\"resid\"] = pd.to_numeric(submission[\"resid\"], errors=\"coerce\")\nsubmission[\"resid\"] = submission[\"resid\"].fillna(submission[\"resid_from_id\"]).astype(int)\n\nfor idx, row in tqdm(submission.iterrows(), total=len(submission)):\n    target_id = row[\"target_id\"]\n    resid = row[\"resid_from_id\"] - 1\n\n    if target_id not in pred_map:\n        continue\n\n    seq = pred_map[target_id][\"sequence\"]\n    structs = pred_map[target_id][\"structures\"]\n\n    if 0 <= resid < len(seq):\n        submission.at[idx, \"resname\"] = seq[resid]\n        submission.at[idx, \"resid\"] = resid + 1\n\n        for k in range(5):\n            submission.at[idx, f\"x_{k+1}\"] = np.float32(structs[k][resid, 0])\n            submission.at[idx, f\"y_{k+1}\"] = np.float32(structs[k][resid, 1])\n            submission.at[idx, f\"z_{k+1}\"] = np.float32(structs[k][resid, 2])\n\nsubmission = submission.drop(columns=[\"target_id\", \"resid_from_id\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:33:52.994383Z","iopub.execute_input":"2026-03-16T16:33:52.99478Z","iopub.status.idle":"2026-03-16T16:33:58.026063Z","shell.execute_reply.started":"2026-03-16T16:33:52.994685Z","shell.execute_reply":"2026-03-16T16:33:58.024192Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 7. CHECKS\nassert list(submission.columns) == list(sample_sub.columns), \"Kolumny nie zgadzają się z sample_submission\"\nassert len(submission) == len(sample_sub), \"Liczba wierszy nie zgadza się z sample_submission\"\nassert submission.isna().sum().sum() == 0, \"Są NaNy w submission\"\n\nsubmission.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"Saved:\", submission.shape)\nprint(submission.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-16T16:30:04.305236Z","iopub.execute_input":"2026-03-16T16:30:04.305617Z","iopub.status.idle":"2026-03-16T16:30:04.578147Z","shell.execute_reply.started":"2026-03-16T16:30:04.305589Z","shell.execute_reply":"2026-03-16T16:30:04.57689Z"}},"outputs":[],"execution_count":null}]}