{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Primero voy a dividir el archivo en archivos más pequeños para poder trabajar con chatgpt que me dará nuevos insights","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport os\n\ndef split_csv(filename, lines_per_file=10000):\n    \"\"\"\n    Divide un archivo CSV en múltiples archivos más pequeños.\n    \n    Parámetros:\n        filename (str): Nombre del archivo CSV a dividir.\n        lines_per_file (int): Número de líneas por cada archivo dividido.\n\n    Retorna:\n        None. Los archivos divididos se guardarán en el directorio de trabajo actual.\n    \"\"\"\n    # Carga el archivo CSV\n    data = pd.read_csv(filename)\n    \n    # Obtiene el nombre base del archivo sin la extensión\n    base_name = os.path.basename(filename).split('.')[0]\n    \n    # Calcula el número total de archivos que se crearán\n    num_files = len(data) // lines_per_file + 1\n    \n    for i in range(num_files):\n        # Calcula el inicio y el final del índice para cada archivo dividido\n        start = i * lines_per_file\n        end = (i + 1) * lines_per_file\n        \n        # Divide el dataframe\n        subset = data[start:end]\n        \n        # Guarda el archivo dividido en el directorio de trabajo actual\n        subset.to_csv(f\"./{base_name}_part_{i+1}.csv\", index=False)\n        print(f\"Guardado {base_name}_part_{i+1}.csv\")\n\n# Uso del código\nsplit_csv('/kaggle/input/stanford-ribonanza-rna-folding/train_data.csv', 100000)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"No me deja, necesita mucha memoria. Voy a dividirlo 1 vez en una primera porción.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport os\n\ndef split_first_portion(filename, lines_per_file=10000):\n    \"\"\"\n    Divide y guarda la primera porción de un archivo CSV.\n    \n    Parámetros:\n        filename (str): Nombre del archivo CSV a dividir.\n        lines_per_file (int): Número de líneas de la porción.\n\n    Retorna:\n        None. La primera porción se guardará en el directorio de trabajo actual.\n    \"\"\"\n    # Carga el archivo CSV\n    data = pd.read_csv(filename)\n    \n    # Obtiene el nombre base del archivo sin la extensión\n    base_name = os.path.basename(filename).split('.')[0]\n    \n    # Toma la primera porción del dataframe\n    subset = data[:lines_per_file]\n    \n    # Guarda la primera porción en el directorio de trabajo actual\n    subset.to_csv(f\"./{base_name}_part_1.csv\", index=False)\n    print(f\"Guardado {base_name}_part_1.csv\")\n\n# Uso del código\nsplit_first_portion('/kaggle/input/stanford-ribonanza-rna-folding/train_data.csv', 200000)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Empezamos","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n# Cargamos solo las primeras filas para examinar su contenido\nsample_data = pd.read_csv('/kaggle/input/stanford-ribonanza-rna-folding/train_data.csv')\nsample_data.head()","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:29:47.078594Z","iopub.execute_input":"2023-10-18T08:29:47.079265Z","iopub.status.idle":"2023-10-18T08:31:35.787544Z","shell.execute_reply.started":"2023-10-18T08:29:47.079225Z","shell.execute_reply":"2023-10-18T08:31:35.786197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"1. Manejo de valores nulos: Dado que los valores nulos (NaN) en reactivity_0001, reactivity_0002,... representan posiciones que no se pueden sondear debido a razones técnicas o que van más allá de la longitud de la secuencia, podemos tratar estos valores nulos como 0. Sin embargo, es importante tener en cuenta que estos 0 no deben ser interpretados como \"falta de reactividad\", sino simplemente como un marcador de ausencia de información.\n\n2. Codificación one-hot para la secuencia de ARN: Convertiremos la secuencia de ARN (compuesta por las bases A, C, G y U) en una representación numérica utilizando la codificación one-hot. Por ejemplo, la base A se convertiría en [1, 0, 0, 0], C en [0, 1, 0, 0], G en [0, 0, 1, 0] y U en [0, 0, 0, 1].","metadata":{}},{"cell_type":"code","source":"# 1. Manejo de valores nulos\ncolumns_reactivity = [col for col in sample_data.columns if 'reactivity' in col and not 'error' in col]\nsample_data[columns_reactivity] = sample_data[columns_reactivity].fillna(0)\n\n# 2. Codificación one-hot para la secuencia de ARN\ndef one_hot_encoding(sequence):\n    mapping = {'A': [1, 0, 0, 0],\n               'C': [0, 1, 0, 0],\n               'G': [0, 0, 1, 0],\n               'U': [0, 0, 0, 1]}\n    \n    return [mapping[base] for base in sequence]\n\nsample_data['sequence_encoded'] = sample_data['sequence'].apply(one_hot_encoding)\n\nsample_data[['sequence', 'sequence_encoded']].head()\n","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:31:35.789522Z","iopub.execute_input":"2023-10-18T08:31:35.789949Z","iopub.status.idle":"2023-10-18T08:32:19.13883Z","shell.execute_reply.started":"2023-10-18T08:31:35.789919Z","shell.execute_reply":"2023-10-18T08:32:19.137733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 1. Análisis Exploratorio de Datos","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Cargar el conjunto de datos nuevamente\ndf = pd.read_csv('/kaggle/working/train_data_part_1.csv')\n\n# Estadísticas descriptivas del conjunto de datos\ndata_description = df.describe()\n\n# Distribución del tipo de experimento\nexperiment_type_distribution = df['experiment_type'].value_counts()\n\n# Visualización de la distribución del tipo de experimento\nplt.figure(figsize=(8, 6))\nsns.countplot(data=df, x='experiment_type')\nplt.title('Distribución del tipo de experimento')\nplt.show()\n\ndata_description, experiment_type_distribution\n","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:32:19.140332Z","iopub.execute_input":"2023-10-18T08:32:19.140679Z","iopub.status.idle":"2023-10-18T08:32:34.993819Z","shell.execute_reply.started":"2023-10-18T08:32:19.140647Z","shell.execute_reply":"2023-10-18T08:32:34.992521Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Ingeniería de características:\n\n    Longitud de la secuencia: Si bien todas las secuencias pueden tener la misma longitud en este conjunto de datos, podríamos considerar agregar la longitud de cada secuencia como una característica, ya que la longitud podría ser relevante en otros contextos.\n    Conteo de nucleótidos: Podemos contar la frecuencia de cada nucleótido (A, C, G, U) en cada secuencia y usar estas frecuencias como características.\n\nVisualización:\n\n    Distribución de la longitud de las secuencias: Esto nos ayudará a entender la variedad de longitudes en nuestro conjunto de datos.\n    Distribución de reactividades: Podemos visualizar la distribución de las reactividades para comprender mejor su rango y características.\n    Frecuencia de nucleótidos: Visualizar la frecuencia de cada nucleótido en todas las secuencias.","metadata":{}},{"cell_type":"markdown","source":"La idea detrás de las características adicionales y las visualizaciones es la siguiente:\n\n    Longitud de la secuencia: Si bien todas las secuencias en este conjunto de datos podrían tener la misma longitud, la longitud de una secuencia puede ser una característica relevante en otros contextos. Las secuencias más largas podrían tener propiedades diferentes en comparación con las más cortas.\n\n    Conteo de nucleótidos: La composición de bases en una secuencia de ARN puede influir en su estructura y función. Al contar la frecuencia de cada nucleótido, obtenemos una idea de la composición de bases de cada secuencia.\n\n    Distribución de la longitud de las secuencias: Esta visualización nos permitiría entender la diversidad en términos de longitud en nuestro conjunto de datos.\n\n    Distribución de reactividades: Comprender la distribución de las reactividades nos permite identificar si hay valores atípicos o si la mayoría de las secuencias tienen reactividades similares.\n\n    Frecuencia de nucleótidos: Esta visualización nos da una idea de qué bases son más prevalentes en el conjunto de datos.","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\n# 1. Longitud de la secuencia\nsample_data['sequence_length'] = sample_data['sequence'].apply(len)\n\n# 2. Conteo de nucleótidos\nfor base in ['A', 'C', 'G', 'U']:\n    sample_data[f'count_{base}'] = sample_data['sequence'].apply(lambda x: x.count(base))\n\n# Visualización\nfig, axs = plt.subplots(3, 1, figsize=(12, 15))\n\n# Distribución de la longitud de las secuencias\nsns.histplot(sample_data['sequence_length'], kde=True, ax=axs[0])\naxs[0].set_title('Distribución de la longitud de las secuencias')\naxs[0].set_xlabel('Longitud de la secuencia')\naxs[0].set_ylabel('Frecuencia')\n\n# Distribución de reactividades\nsns.histplot(sample_data['reactivity_0001'], kde=True, ax=axs[1], label='reactivity_0001')\nsns.histplot(sample_data['reactivity_0002'], kde=True, ax=axs[1], color='red', label='reactivity_0002')\naxs[1].set_title('Distribución de reactividades')\naxs[1].set_xlabel('Reactividad')\naxs[1].set_ylabel('Frecuencia')\naxs[1].legend()\n\n# Frecuencia de nucleótidos\nbases = ['A', 'C', 'G', 'U']\ncounts = [sample_data[f'count_{base}'].sum() for base in bases]\nsns.barplot(x=bases, y=counts, ax=axs[2])\naxs[2].set_title('Frecuencia de nucleótidos')\naxs[2].set_xlabel('Nucleótido')\naxs[2].set_ylabel('Conteo total')\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:32:34.997519Z","iopub.execute_input":"2023-10-18T08:32:34.998503Z","iopub.status.idle":"2023-10-18T08:32:52.541953Z","shell.execute_reply.started":"2023-10-18T08:32:34.99845Z","shell.execute_reply":"2023-10-18T08:32:52.54107Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 3.1 Contenido GC\nsample_data['GC_content'] = (sample_data['count_G'] + sample_data['count_C']) / sample_data['sequence_length']\n\n# 3.2 Posición de nucleótidos\nfor base in ['A', 'C', 'G', 'U']:\n    sample_data[f'first_pos_{base}'] = sample_data['sequence'].apply(lambda x: x.find(base))\n    sample_data[f'last_pos_{base}'] = sample_data['sequence'].apply(lambda x: x.rfind(base))\n\nsample_data[['sequence', 'GC_content', 'first_pos_A', 'last_pos_A', 'first_pos_C', 'last_pos_C', 'first_pos_G', 'last_pos_G', 'first_pos_U', 'last_pos_U']].head()\n","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:32:52.543197Z","iopub.execute_input":"2023-10-18T08:32:52.543667Z","iopub.status.idle":"2023-10-18T08:32:58.451554Z","shell.execute_reply.started":"2023-10-18T08:32:52.543638Z","shell.execute_reply":"2023-10-18T08:32:58.450477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"1. Dividir los datos en conjuntos de entrenamiento y validación\n2. Normalizar o escalar las características numéricas:\n3. Formatear los datos de entrada para que sean adecuados para una RNN","metadata":{}},{"cell_type":"markdown","source":"Por lo general, los datos de entrada a una RNN tienen la forma (número de muestras, longitud de la secuencia, número de características por paso de tiempo). En nuestro caso, la longitud de la secuencia es la longitud de la secuencia de ARN y el número de características por paso de tiempo es 4 (debido a la codificación one-hot). Si añadimos características adicionales, este número aumentará.","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import MinMaxScaler\nimport numpy as np\n\n# 1. Dividir los datos en conjuntos de entrenamiento y validación\ntrain_data, validation_data = train_test_split(sample_data, test_size=0.2, random_state=42)\n\n# 3. Formatear los datos de entrada para que sean adecuados para una RNN\n\n# Preparar las secuencias para el entrenamiento\nX_train = np.array(train_data['sequence_encoded'].tolist())\nX_val = np.array(validation_data['sequence_encoded'].tolist())\n\n# Preparar los objetivos para el entrenamiento\ny_columns = [col for col in train_data.columns if 'reactivity' in col and not 'error' in col]\ny_train = train_data[y_columns].values\ny_val = validation_data[y_columns].values\n\nX_train.shape, X_val.shape, y_train.shape, y_val.shape","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:32:58.453285Z","iopub.execute_input":"2023-10-18T08:32:58.45375Z","iopub.status.idle":"2023-10-18T08:33:18.828432Z","shell.execute_reply.started":"2023-10-18T08:32:58.453722Z","shell.execute_reply":"2023-10-18T08:33:18.827262Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.sequence import pad_sequences\n\n# Pad the sequences to have the same length\nX_train_padded = pad_sequences(X_train, padding='post', dtype='float32')\nX_val_padded = pad_sequences(X_val, padding='post', dtype='float32')\n\nX_train_padded.shape, X_val_padded.shape","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:33:18.829741Z","iopub.execute_input":"2023-10-18T08:33:18.83009Z","iopub.status.idle":"2023-10-18T08:35:45.873238Z","shell.execute_reply.started":"2023-10-18T08:33:18.830059Z","shell.execute_reply":"2023-10-18T08:35:45.871971Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"1.     Modelo LSTM básico.\n1.     Modelo LSTM con más capas.\n1.     Modelo GRU básico.\n1.     Modelo Bidireccional LSTM.\n1.     Modelo combinado con LSTM y capas densas.","metadata":{}},{"cell_type":"markdown","source":"1. Modelo LSTM Básico","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import LSTM, Dense\n\ndef create_basic_lstm_model(input_shape):\n    model = Sequential()\n    model.add(LSTM(64, input_shape=input_shape, return_sequences=True))\n    model.add(Dense(64, activation='relu'))\n    model.add(Dense(5, activation='linear'))\n    return model\n\nbasic_lstm_model = create_basic_lstm_model((206, 4))\nbasic_lstm_model.compile(optimizer='adam', loss='mse', metrics=['mae'])\nhistory_basic_lstm = basic_lstm_model.fit(X_train_padded, y_train, epochs=50, batch_size=32, validation_data=(X_val_padded, y_val))","metadata":{"execution":{"iopub.status.busy":"2023-10-18T08:35:45.874874Z","iopub.execute_input":"2023-10-18T08:35:45.875966Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2. Modelo LSTM con más capas","metadata":{}},{"cell_type":"code","source":"def create_deep_lstm_model(input_shape):\n    model = Sequential()\n    model.add(LSTM(128, input_shape=input_shape, return_sequences=True))\n    model.add(LSTM(64, return_sequences=True))\n    model.add(Dense(64, activation='relu'))\n    model.add(Dense(5, activation='linear'))\n    return model\n\ndeep_lstm_model = create_deep_lstm_model((X_train.shape[1], X_train.shape[2]))\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"3. Modelo GRU Básico","metadata":{}},{"cell_type":"code","source":"def create_basic_gru_model(input_shape):\n    model = Sequential()\n    model.add(tf.keras.layers.GRU(64, input_shape=input_shape, return_sequences=True))\n    model.add(Dense(64, activation='relu'))\n    model.add(Dense(5, activation='linear'))\n    return model\n\ngru_model = create_basic_gru_model((X_train.shape[1], X_train.shape[2]))\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"4. Modelo Bidireccional LSTM","metadata":{}},{"cell_type":"code","source":"def create_bidirectional_lstm_model(input_shape):\n    model = Sequential()\n    model.add(tf.keras.layers.Bidirectional(LSTM(64, return_sequences=True), input_shape=input_shape))\n    model.add(Dense(64, activation='relu'))\n    model.add(Dense(5, activation='linear'))\n    return model\n\nbi_lstm_model = create_bidirectional_lstm_model((X_train.shape[1], X_train.shape[2]))\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"5. Modelo combinado con LSTM y capas densas","metadata":{}},{"cell_type":"code","source":"def create_combined_model(input_shape):\n    model = Sequential()\n    model.add(LSTM(64, input_shape=input_shape, return_sequences=True))\n    model.add(Dense(128, activation='relu'))\n    model.add(Dense(64, activation='relu'))\n    model.add(Dense(5, activation='linear'))\n    return model\n\ncombined_model = create_combined_model((X_train.shape[1], X_train.shape[2]))\n","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Comparar modelos","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef compare_models(histories, model_names):\n    '''\n    Esta función traza las curvas de pérdida de validación para todos los modelos.\n    \n    Parámetros:\n        histories (list): Lista de objetos de historial devueltos por model.fit() para cada modelo.\n        model_names (list): Lista de nombres de modelos para etiquetar las curvas en la gráfica.\n    '''\n    plt.figure(figsize=(12, 6))\n    \n    for history, model_name in zip(histories, model_names):\n        val_loss = history.history['val_loss']\n        plt.plot(val_loss, label=model_name)\n    \n    plt.title('Comparación de la pérdida de validación entre modelos')\n    plt.xlabel('Épocas')\n    plt.ylabel('Pérdida de validación')\n    plt.legend()\n    plt.grid(True)\n    plt.show()\n\n# Ejemplo de uso:\n# compare_models([history_basic_lstm, history_deep_lstm, history_gru, history_bi_lstm, history_combined],\n#                ['Basic LSTM', 'Deep LSTM', 'Basic GRU', 'Bidirectional LSTM', 'Combined LSTM'])\n","metadata":{},"execution_count":null,"outputs":[]}]}