{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":87793,"databundleVersionId":11228175,"sourceType":"competition"}],"dockerImageVersionId":30918,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### This notebook is rewritten from [Olaf's starter notebook](https://www.kaggle.com/code/olaflundstrom/stanford-rna-3d-folding-competition-notebook)","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dataset Overview","metadata":{}},{"cell_type":"code","source":"train_labels = pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/train_labels.csv\")\ntrain_sequence = pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/train_sequences.csv\")\nval_labels = pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/validation_labels.csv\")\nval_sequence = pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/validation_sequences.csv\")\ntest_sequence = pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/test_sequences.csv\")\n\nprint(\"Train Seq: \" + str(train_sequence.shape))\nprint(\"Train Label: \" + str(train_labels.shape))\nprint(\"Validation Seq: \" + str(val_sequence.shape))\nprint(\"Validation Label: \" + str(val_labels.shape))\nprint(\"Test: \"+str(test_sequence.shape))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:35.652057Z","iopub.execute_input":"2025-03-02T05:00:35.652422Z","iopub.status.idle":"2025-03-02T05:00:35.955755Z","shell.execute_reply.started":"2025-03-02T05:00:35.652388Z","shell.execute_reply":"2025-03-02T05:00:35.954799Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_labels.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:35.957341Z","iopub.execute_input":"2025-03-02T05:00:35.957743Z","iopub.status.idle":"2025-03-02T05:00:35.970232Z","shell.execute_reply.started":"2025-03-02T05:00:35.957705Z","shell.execute_reply":"2025-03-02T05:00:35.969054Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_labels.fillna(0, inplace=True)\nval_labels.fillna(0, inplace=True)\ntrain_labels.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:35.972179Z","iopub.execute_input":"2025-03-02T05:00:35.972534Z","iopub.status.idle":"2025-03-02T05:00:36.039155Z","shell.execute_reply.started":"2025-03-02T05:00:35.972505Z","shell.execute_reply":"2025-03-02T05:00:36.038092Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualization\n- use plotly for interactive plots\n- target is the first label in labels","metadata":{}},{"cell_type":"code","source":"import plotly.express as px\nimport plotly.io as pio\npio.renderers.default = 'iframe'\n\nplot_labels = train_labels[['ID', 'x_1', 'y_1', 'z_1']].copy()\nplot_labels['label'] = plot_labels.ID.str.rsplit('_', n=1, expand=True).iloc[:,0]\n\ntarget = '1SCL_A'\n\nfig = px.line_3d(\n    plot_labels.loc[plot_labels.label==target],\n    x='x_1', y='y_1', z='z_1',\n    title=f'{target}'\n)\n\nfig.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:36.040873Z","iopub.execute_input":"2025-03-02T05:00:36.041322Z","iopub.status.idle":"2025-03-02T05:00:36.973607Z","shell.execute_reply.started":"2025-03-02T05:00:36.041265Z","shell.execute_reply":"2025-03-02T05:00:36.972366Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_sequence.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:36.975104Z","iopub.execute_input":"2025-03-02T05:00:36.975532Z","iopub.status.idle":"2025-03-02T05:00:36.987782Z","shell.execute_reply.started":"2025-03-02T05:00:36.975501Z","shell.execute_reply":"2025-03-02T05:00:36.986748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_labels.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:36.989285Z","iopub.execute_input":"2025-03-02T05:00:36.989739Z","iopub.status.idle":"2025-03-02T05:00:37.030076Z","shell.execute_reply.started":"2025-03-02T05:00:36.989671Z","shell.execute_reply":"2025-03-02T05:00:37.028801Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocessing","metadata":{}},{"cell_type":"code","source":"seq_dict = {'A': 1, 'C': 2, 'G': 3, 'U': 4}\ndef seq_map(seq):\n    return [seq_dict.get(char, 0) for char in seq]\n\ntrain_sequence['encoded_seq'] = train_sequence['sequence'].apply(seq_map)\ntest_sequence['encoded_seq'] = test_sequence['sequence'].apply(seq_map)\nval_sequence['encoded_seq'] = val_sequence['sequence'].apply(seq_map)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:37.031235Z","iopub.execute_input":"2025-03-02T05:00:37.031700Z","iopub.status.idle":"2025-03-02T05:00:37.072958Z","shell.execute_reply.started":"2025-03-02T05:00:37.031666Z","shell.execute_reply":"2025-03-02T05:00:37.071177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_label_coord(df):\n    result = {}\n    df[\"label\"] = df.ID.str.rsplit('_', n=1, expand=True).iloc[:,0]\n    for _, row in df.iterrows():\n        label = row['label']\n        resid = row['resid']\n        if label not in result:\n            result[label] = []\n        else:\n            coord = np.array([row['x_1'], row['y_1'], row['z_1']], dtype=np.float32)\n            result[label].append((resid, coord))\n    for key in result:\n        coords = np.stack([c for r, c in result[key]])\n        result[key] = coords\n    return result\n\ntrain_stacked_coords = generate_label_coord(train_labels)\nval_stacked_coords = generate_label_coord(val_labels)\ntrain_stacked_coords[list(train_stacked_coords.keys())[0]]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:37.076439Z","iopub.execute_input":"2025-03-02T05:00:37.076842Z","iopub.status.idle":"2025-03-02T05:00:46.588229Z","shell.execute_reply.started":"2025-03-02T05:00:37.076815Z","shell.execute_reply":"2025-03-02T05:00:46.587118Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Create Dataset for CNN","metadata":{}},{"cell_type":"code","source":"def generate_dataset(seq, stacked_coords):\n    X, y, tids = [], [], []\n    for idx, row in seq.iterrows():\n        tid = row['target_id']\n        if tid in stacked_coords:\n            X.append(row['encoded_seq'])\n            y.append(stacked_coords[tid])\n            tids.append(tid)\n    return X, y, tids\n\ntrain_X, train_y, train_tids = generate_dataset(train_sequence, train_stacked_coords)\nval_X, val_y, val_tids = generate_dataset(val_sequence, val_stacked_coords)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:46.589817Z","iopub.execute_input":"2025-03-02T05:00:46.590211Z","iopub.status.idle":"2025-03-02T05:00:46.647712Z","shell.execute_reply.started":"2025-03-02T05:00:46.590173Z","shell.execute_reply":"2025-03-02T05:00:46.646608Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- coordinates and seqences are padded to avoid different lengths, all with constant 0","metadata":{}},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Embedding, Conv1D, BatchNormalization, Dropout\nfrom tensorflow.keras.preprocessing.sequence import pad_sequences\nfrom tensorflow.keras.callbacks import EarlyStopping\n\nmax_len = max(len(seq) for seq in train_X)\n\ntrain_X_pad = pad_sequences(train_X, maxlen=max_len, padding='post', value=0)\nval_X_pad = pad_sequences(val_X, maxlen=max_len, padding='post', value=0)\ntest_X = test_sequence['encoded_seq'].tolist()\ntest_X_pad = pad_sequences(test_X, maxlen=max_len, padding='post', value=0)\ntrain_X_pad.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:46.648867Z","iopub.execute_input":"2025-03-02T05:00:46.649302Z","iopub.status.idle":"2025-03-02T05:00:46.674061Z","shell.execute_reply.started":"2025-03-02T05:00:46.649237Z","shell.execute_reply":"2025-03-02T05:00:46.672745Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def pad_coords(coords, max_len):\n    L = coords.shape[0]\n    if L < max_len:\n        pad_width = ((0, max_len-L), (0, 0)) # pad only vertically\n        return np.pad(coords, pad_width, mode='constant', constant_values = 0)\n    else:\n        return coords\n\ntrain_y_pad = np.array([pad_coords(y, max_len) for y in train_y])\nval_y_pad = np.array([pad_coords(y, max_len) for y in val_y])\ntrain_y_pad.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:46.674983Z","iopub.execute_input":"2025-03-02T05:00:46.675314Z","iopub.status.idle":"2025-03-02T05:00:46.749477Z","shell.execute_reply.started":"2025-03-02T05:00:46.675277Z","shell.execute_reply":"2025-03-02T05:00:46.748414Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## CNN model\n- first conv and second conv layers are seperated for clarity\n- main hyperparams are defined in self.config\n- a model must first be built then trained","metadata":{}},{"cell_type":"code","source":"class CNN:\n    def __init__(self, X_train_pad, y_train_pad, X_val_pad, y_val_pad):\n        self.x_train = X_train_pad\n        self.y_train = y_train_pad\n        self.x_val = X_val_pad\n        self.y_val = y_val_pad\n        self.config = {\n            'seq_mapping_size':max(seq_dict.values()) + 1,\n            'embedding_dim':16,\n            'num_filters':64,\n            'kernel_size':3,\n            'drop_rate':0.2,\n            'train_epochs':50,\n            'batch_size':16\n        }\n        self.model= None\n    def first_conv(self, max_length):\n        input_seq = Input(shape=(max_len,), name='input_seq')\n        x_cnn = Embedding(input_dim=self.config[\"seq_mapping_size\"],\n                         output_dim=self.config[\"embedding_dim\"],\n                         mask_zero=True,\n                         name='embedding')(input_seq)\n        x_cnn = Conv1D(filters=self.config[\"num_filters\"],\n                      kernel_size=self.config[\"kernel_size\"],\n                      padding='same',\n                      activation='relu',\n                      name='conv1')(x_cnn)\n        x_cnn = BatchNormalization(name='norm1')(x_cnn)\n        x_cnn = Dropout(self.config['drop_rate'], name='drop1')(x_cnn)\n        \n        return input_seq, x_cnn\n        \n    def second_conv_full_c(self, x_cnn):\n        x_cnn = Conv1D(filters=self.config[\"num_filters\"],\n                      kernel_size=self.config[\"kernel_size\"],\n                      padding='same',\n                      activation='relu',\n                      name='conv2')(x_cnn)\n        x_cnn = BatchNormalization(name='norm2')(x_cnn)\n        x_cnn = Dropout(self.config['drop_rate'], name='drop2')(x_cnn)\n        full_c = Conv1D(filters=3, kernel_size=1,\n                       padding='same',\n                       activation='linear',\n                       name='predicted_coords')(x_cnn)\n        return full_c\n    def build_model(self, max_length):\n        input_seq, x_cnn = self.first_conv(max_length)\n        full_c = self.second_conv_full_c(x_cnn)\n        model = Model(inputs=input_seq,\n                     outputs=full_c)\n        model.compile(optimizer='adam', loss='mae')\n        self.model=model\n        return model.summary()\n        \n    def train(self):\n        history_cnn = self.model.fit(\n            self.x_train,\n            self.y_train,\n            validation_data=(self.x_val, self.y_val),\n            epochs=self.config[\"train_epochs\"],\n            batch_size=self.config[\"batch_size\"],\n            verbose=1\n        )\n        return self.model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:46.750339Z","iopub.execute_input":"2025-03-02T05:00:46.750620Z","iopub.status.idle":"2025-03-02T05:00:46.762224Z","shell.execute_reply.started":"2025-03-02T05:00:46.750597Z","shell.execute_reply":"2025-03-02T05:00:46.760700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cnn = CNN(train_X_pad, train_y_pad, val_X_pad, val_y_pad)\ncnn.build_model(max_len)\nmodel = cnn.train()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:00:46.763340Z","iopub.execute_input":"2025-03-02T05:00:46.763772Z","iopub.status.idle":"2025-03-02T05:01:13.795441Z","shell.execute_reply.started":"2025-03-02T05:00:46.763725Z","shell.execute_reply":"2025-03-02T05:01:13.794368Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred = model.predict(test_X_pad)\npred.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:01:13.797158Z","iopub.execute_input":"2025-03-02T05:01:13.797579Z","iopub.status.idle":"2025-03-02T05:01:14.017405Z","shell.execute_reply.started":"2025-03-02T05:01:13.797547Z","shell.execute_reply":"2025-03-02T05:01:14.016390Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Submission\n- the target 3 dimensional coordinates are duplicated 5 times","metadata":{}},{"cell_type":"code","source":"submission_rows = []\nfor idx, row in test_sequence.iterrows():\n    target_id = row['target_id']\n    coords = pred[idx]\n    seq_length = len(row['encoded_seq'])\n    coords = coords[:seq_length, :]\n    for i in range(seq_length):\n        x, y, z = coords[i, :]\n        submission_rows.append(\n        {\n            'ID':f\"{target_id}_{i+1}\",\n            'resname':row['sequence'][i],\n            'resid':i+1,\n             **{f\"x_{j+1}\": x for j in range(5)},\n             **{f\"y_{j+1}\": y for j in range(5)},\n             **{f\"z_{j+1}\": z for j in range(5)}\n            \n        }\n        )\nsubmission_df = pd.DataFrame(submission_rows)\nsubmission_df.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:06:51.211841Z","iopub.execute_input":"2025-03-02T05:06:51.212416Z","iopub.status.idle":"2025-03-02T05:06:51.274723Z","shell.execute_reply.started":"2025-03-02T05:06:51.212376Z","shell.execute_reply":"2025-03-02T05:06:51.273843Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\", index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-02T05:10:08.645787Z","iopub.execute_input":"2025-03-02T05:10:08.646157Z","iopub.status.idle":"2025-03-02T05:10:08.705561Z","shell.execute_reply.started":"2025-03-02T05:10:08.646132Z","shell.execute_reply":"2025-03-02T05:10:08.704389Z"}},"outputs":[],"execution_count":null}]}