Análisis evolutivo y funcional de genes asociados al complejo TORC1 en Saccharomyces cerevisiae mediante técnicas de inteligencia artificial
1. Introducción
La Saccharomyces cerevisiae es un hongo unicelular de gran relevancia tanto en la investigación científica como en la industria. Este organismo es ampliamente conocido por su papel clave en la producción de bebidas fermentadas, tales como vino, cerveza, sidra y sake, además de ser indispensable en la elaboración de productos de panadería y repostería. Su versatilidad en procesos industriales se debe, en gran medida, a su capacidad para realizar la fermentación alcohólica, un proceso que convierte los azúcares en etanol y dióxido de carbono, lo cual no solo es fundamental para la producción de alcohol, sino también para dar textura y sabor a productos horneados.
A nivel celular, las quinasas TOR (Target of Rapamycin) desempeñan un rol central en la regulación del crecimiento celular y el metabolismo en respuesta a la disponibilidad de nutrientes. Estas quinasas forman dos complejos funcionales: TORC1 y TORC2, cada uno con funciones específicas y no redundantes. En particular, el complejo TORC1 es crítico para la respuesta al nitrógeno, un nutriente clave que regula una variedad de procesos celulares. En presencia de nitrógeno, TORC1 se activa, desencadenando una cascada de señales que promueven la biosíntesis de proteínas y ribosomas. Este proceso es esencial para mantener la proliferación celular y asegurar una fermentación eficiente, lo que resulta en una mejora significativa en la calidad de los productos fermentados.
La activación de TORC1 no solo permite un crecimiento celular óptimo, sino que también regula procesos anabólicos como la producción de lípidos y la síntesis de nucleótidos, lo que es vital para soportar la demanda metabólica durante la fermentación. A pesar de su importancia, los mecanismos precisos mediante los cuales TORC1 detecta y responde a las fluctuaciones en los niveles de nitrógeno permanecen en gran parte desconocidos. Comprender estos mecanismos podría ofrecer oportunidades para optimizar procesos industriales y mejorar la eficiencia en la producción de alimentos y bebidas fermentadas.
Investigadores(as) del Centro de Estudios en Ciencia y Tecnología de la Universidad de Santiago de Chile han identificado varios SNPs (polimorfismos de un solo nucleótido) asociados a diferentes Open Reading Frames (ORFs) o marcos de lectura abiertos (127) de los 6,015 que están vinculados a esta quinasa en 270 cepas vínicas. En los casos donde se detectaron SNPs en regiones no codificantes, se registraron los más cercanos a los ORFs, considerando ambas direcciones. Los ORFs identificados son los siguientes:
La historia evolutiva de estos ORFs se estudiará a través de análisis filogenéticos con el fin de determinar si presentan trayectorias evolutivas similares. Inicialmente, se analizarán las 270 cepas vínicas, para luego extender el estudio a la totalidad de las cepas, que suman 1.011.
2. Análisis filogenético
El análisis comenzará con el estudio de 270 cepas vínicas y 231 ORFs asociados a estas cepas. Este conjunto inicial permitirá explorar la diversidad genética y evaluar patrones evolutivos específicos entre las cepas vínicas. Posteriormente, se ampliará el análisis para incluir todas las cepas disponibles. El conjunto de datos original incluye un listado completo de las 1.011 cepas, diferenciadas en diploides-euploides (641), haploides-euploides (112), y las vínicas asociadas a esta etapa del estudio (270). La tabla asociada es la siguiente:
2.1 Creación de árboles filogenéticos
El primer paso, después de cargar los archivos, es la creación de 6,015 árboles filogenéticos, uno por cada ORF. El proceso se desarrollará de la siguiente manera.
Lectura secuencial de los 6,015 ORFs. Las secuencias alineadas de cada ORF se leerán una a una, tomando en cuenta las 1.011 cepas disponibles.
Evaluación de la distancia de Hamming. Para cada ORF, se calculará la distancia de Hamming entre las secuencias de las 1.011 cepas. Este cálculo será usado para construir un árbol filogenético específico para ese ORF, que representará las relaciones evolutivas entre las cepas.
Filtrado de cepas vínicas (270). De las secuencias alineadas de las 1.011 cepas, se filtrarán las 270 cepas vínicas. A partir de estas cepas filtradas, se construirá un árbol filogenético específico para las 270 cepas de ese ORF.
Acumulación de la matriz de distancias. Paralelamente, para cada ORF, se acumularán las matrices de distancia calculadas. Esto permitirá generar los árboles de consenso de evidencia total al finalizar el procesamiento de los 6,015 ORFs. En concreto, se generarán los siguientes árboles.
- Árbol TE 1.011. Un árbol de consenso de evidencia total basado en todas las secuencias de las 1.011 cepas.
- Árbol TE 270. Un árbol de consenso de evidencia total para las 270 cepas vínicas.
Filtrado de ORFs codificantes y no codificantes. Además, se aplicará un filtrado adicional para distinguir entre ORFs codificantes y ORFs no codificantes. Esto permitirá construir cuatro árboles de consenso adicionales.
- Árbol TE 1.011 COD. Un árbol de consenso de evidencia total para los ORFs codificantes de las 1.011 cepas.
- Árbol TE 1.011 NO COD. Un árbol de consenso para los ORFs no codificantes de las 1.011 cepas.
- Árbol TE 270 COD. Un árbol de consenso para los ORFs codificantes de las 270 cepas vínicas.
- Árbol TE 270 NO COD. Un árbol de consenso para los ORFs no codificantes de las 270 cepas vínicas.
Almacenamiento de los árboles. Dado que este proceso implicará varias horas de cálculo, todos los árboles generados (tanto los específicos por ORF como los de consenso) se almacenarán en archivos separados, listos para su uso en los siguientes pasos del análisis.
# Carga de bibliotecas necesarias
library(stringr) # Manipulación de cadenas de texto
library(phangorn) # Análisis y construcción de árboles filogenéticos
library(plotly) # Creación de gráficos interactivos en R
library(reshape2) # Transformación de datos entre formatos largos y anchos
# Definición de las cepas y lectura de secuencias
# Definir las 270 cepas, eliminando entradas vacías en la lista
strains_270 <- list_270strains[which(list_270strains != "")]
# Obtener el listado de ORFs (6015 archivos de secuencias alineadas presentes en el directorio)
ORFs_list <- dir("BD/Secuencias_alineadas/")
# Inicialización de listas y matrices para almacenar árboles filogenéticos y matrices de distancias
# Inicializar listas para almacenar los árboles filogenéticos para 270 y 1011 cepas
trees_270strains <- list() # Árboles filogenéticos para las 270 cepas
trees_allstrains <- list() # Árboles filogenéticos para las 1011 cepas
# Inicialización de matrices de Evidencia Total (TE) para las 1011 cepas
# Matriz TE para todas las cepas (general)
distance_matrix_TE_allstrains <- matrix(0, ncol=1011, nrow=1011)
# Matriz TE para ORFs codificantes
distance_matrix_TE_allstrainsCOD <- matrix(0, ncol=1011, nrow=1011)
# Matriz TE para ORFs no codificantes
distance_matrix_TE_allstrainsNOCOD <- matrix(0, ncol=1011, nrow=1011)
# Inicialización de matrices de Evidencia Total (TE) para las 270 cepas
# Matriz TE para todas las cepas (general)
distance_matrix_TE_270strains <- matrix(0, ncol=length(strains_270), nrow=length(strains_270)) # Inicializar matriz de distancias para 270 cepas
# Matriz TE para ORFs codificantes
distance_matrix_TE_270strainsCOD <- matrix(0, ncol=length(strains_270), nrow=length(strains_270))
# Matriz TE para ORFs no codificantes
distance_matrix_TE_270strainsNOCOD <- matrix(0, ncol=length(strains_270), nrow=length(strains_270))
# Iteración sobre cada ORF para generar árboles filogenéticos y actualizar las matrices de distancia
for (i in seq_along(ORFs_list)) {
# Mostrar mensaje de progreso para cada ORF procesado
message(sprintf("\t- Procesando árbol %d de %d...", i, length(ORFs_list)))
# Leer las secuencias alineadas de las 1011 cepas para el ORF actual
sequence_strain <- read.phyDat(paste0("BD/Secuencias_alineadas/", ORFs_list[i]))
# Calcular las distancias de Hamming para todas las cepas (1011 cepas)
distHamming_all <- dist.hamming(sequence_strain)
# Actualizar la matriz de Evidencia Total (TE) para todas las cepas
distance_matrix_TE_allstrains <- distance_matrix_TE_allstrains + distHamming_all
colnames(distance_matrix_TE_allstrains) = rownames(distance_matrix_TE_allstrains) = names(sequence_strain)
# Actualizar la matriz TE solo para los ORFs codificantes
if (gsub(".fasta.phylip", "", ORFs_list[i]) %in% coding_ORFs) {
distance_matrix_TE_allstrainsCOD <- distance_matrix_TE_allstrainsCOD + distHamming_all
colnames(distance_matrix_TE_allstrainsCOD) = rownames(distance_matrix_TE_allstrainsCOD) = names(sequence_strain)
}
# Actualizar la matriz TE solo para los ORFs no codificantes
if (gsub(".fasta.phylip", "", ORFs_list[i]) %in% non_coding_ORFs) {
distance_matrix_TE_allstrainsNOCOD <- distance_matrix_TE_allstrainsNOCOD + distHamming_all
colnames(distance_matrix_TE_allstrainsNOCOD) = rownames(distance_matrix_TE_allstrainsNOCOD) = names(sequence_strain)
}
# Inferencia filogenética para las 1011 cepas usando Neighbor Joining (NJ)
tree_all <- NJ(distHamming_all) # Aplicar algoritmo NJ para generar el árbol
tree_all <- midpoint(multi2di(tree_all)) # Enraizar el árbol en el punto medio
trees_allstrains[[i]] <- tree_all # Guardar el árbol en la lista correspondiente
# Filtrar las secuencias solo para las 270 cepas específicas
idx_strains <- match(strains_270, names(sequence_strain)) # Obtener los índices de las 270 cepas
sequence_strain_filtered <- sequence_strain[idx_strains, ] # Filtrar las secuencias para las 270 cepas
# Calcular distancias de Hamming para las 270 cepas
distHamming_270 <- dist.hamming(sequence_strain_filtered)
# Actualizar la matriz de Evidencia Total (TE) para las 270 cepas
distance_matrix_TE_270strains <- distance_matrix_TE_270strains + distHamming_270
colnames(distance_matrix_TE_270strains) = rownames(distance_matrix_TE_270strains) = names(sequence_strain_filtered)
# Actualizar la matriz TE para ORFs codificantes en las 270 cepas
if (gsub(".fasta.phylip", "", ORFs_list[i]) %in% coding_ORFs) {
distance_matrix_TE_270strainsCOD <- distance_matrix_TE_270strainsCOD + distHamming_270
colnames(distance_matrix_TE_270strainsCOD) = rownames(distance_matrix_TE_270strainsCOD) = names(sequence_strain_filtered)
}
# Actualizar la matriz TE para ORFs no codificantes en las 270 cepas
if (gsub(".fasta.phylip", "", ORFs_list[i]) %in% non_coding_ORFs) {
distance_matrix_TE_270strainsNOCOD <- distance_matrix_TE_270strainsNOCOD + distHamming_270
colnames(distance_matrix_TE_270strainsNOCOD) = rownames(distance_matrix_TE_270strainsNOCOD) = names(sequence_strain_filtered)
}
# Inferencia filogenética para las 270 cepas usando Neighbor Joining (NJ)
tree_270 <- NJ(distHamming_270) # Aplicar NJ para las 270 cepas
tree_270 <- midpoint(multi2di(tree_270)) # Enraizar el árbol en el punto medio
trees_270strains[[i]] <- tree_270 # Guardar el árbol en la lista
} # Fin del bucle for
# Asignar nombres a los árboles generados
# Eliminar el sufijo ".fasta.phylip" de los nombres de los ORFs para los árboles
tree_names <- gsub(".fasta.phylip", "", ORFs_list)
# Asignar los nombres de los ORFs a las listas de árboles generadas
names(trees_270strains) <- tree_names
names(trees_allstrains) <- tree_names
# Guardar los árboles filogenéticos generados en archivos
# Guardar los árboles de las 270 cepas
write.tree(trees_270strains, "BD/Trees/trees_270Strains_6015ORFs.tree", tree.names = TRUE)
# Guardar los árboles de las 1011 cepas
write.tree(trees_allstrains, "BD/Trees/trees_1011Strains_6015ORFs.tree", tree.names = TRUE)
# Creación de árboles de consenso de Evidencia Total (TE)
# Crear árbol de Evidencia Total (TE) para las 1011 cepas
tree_TE1011 <- NJ(distance_matrix_TE_allstrains)
tree_TE1011 <- midpoint(tree_TE1011) # Enraizar en el punto medio
write.tree(tree_TE1011, "BD/Trees/tree_TE1011.tree")
# Crear árbol TE para las 1011 cepas (solo ORFs codificantes)
tree_TE1011COD <- NJ(distance_matrix_TE_allstrainsCOD)
tree_TE1011COD <- midpoint(tree_TE1011COD)
write.tree(tree_TE1011COD, "BD/Trees/tree_TE1011COD.tree")
# Crear árbol TE para las 1011 cepas (solo ORFs no codificantes)
tree_TE1011NOCOD <- NJ(distance_matrix_TE_allstrainsNOCOD)
tree_TE1011NOCOD <- midpoint(tree_TE1011NOCOD)
write.tree(tree_TE1011NOCOD, "BD/Trees/tree_TE1011NOCOD.tree")
# Crear árbol de Evidencia Total (TE) para las 270 cepas
tree_TE270 <- NJ(distance_matrix_TE_270strains)
tree_TE270 <- midpoint(tree_TE270)
write.tree(tree_TE270, "BD/Trees/tree_TE270.tree")
# Crear árbol TE para las 270 cepas (solo ORFs codificantes)
tree_TE270COD <- NJ(distance_matrix_TE_270strainsCOD)
tree_TE270COD <- midpoint(tree_TE270COD)
write.tree(tree_TE270COD, "BD/Trees/tree_TE270COD.tree")
# Crear árbol TE para las 270 cepas (solo ORFs no codificantes)
tree_TE270NOCOD <- NJ(distance_matrix_TE_270strainsNOCOD)
tree_TE270NOCOD <- midpoint(tree_TE270NOCOD)
write.tree(tree_TE270NOCOD, "BD/Trees/tree_TE270NOCOD.tree")
2.2 Cálculo de distancias filogenéticas
Una vez que se han creado los árboles filogenéticos, el siguiente paso es calcular las distancias filogenéticas utilizando la métrica Robinson-Foulds (RF), que mide la diferencia entre dos árboles. El proceso para este cálculo se desarrollará de la siguiente manera.
Lectura de los árboles generados. Los árboles filogenéticos generados previamente, tanto para las 1,011 cepas como para las 270 cepas vínicas, se leerán desde sus archivos almacenados.
Cálculo de la distancia Robinson-Foulds (RF). Para cada par de árboles, se calculará la distancia RF, que mide las diferencias topológicas entre los árboles. Este cálculo se llevará a cabo tanto para los árboles de 1,011 cepas como para los árboles de 270 cepas.
Normalización de las distancias RF. Para hacer comparaciones más claras, se normalizarán las distancias RF, dividiendo las distancias calculadas entre el número total de biparticiones posibles para cada par de árboles. Esto permitirá interpretar de manera más sencilla las diferencias entre los árboles.
Creación de una matriz de distancias. Todas las distancias RF calculadas entre los pares de árboles se almacenarán en una matriz de distancias. Se generarán dos matrices: una para las 1,011 cepas y otra para las 270 cepas.
Almacenamiento de las matrices de distancias. Al finalizar el cálculo, las matrices de distancias se guardarán en archivos, lo que permitirá usarlas en análisis posteriores. Esto incluirá tanto la matriz para las 1,011 cepas como la matriz para las 270 cepas.
# Cargar la biblioteca necesaria para cálculos de distancia filogenética
library(phangorn)
# Preparación de datos
# Obtener la lista de archivos de secuencias alineadas (ORFs)
ORFs_list <- dir("BD/Secuencias_alineadas/")
# Eliminar el sufijo '.fasta.phylip' de los nombres de los archivos
tree_names <- gsub(".fasta.phylip", "", ORFs_list)
# Lectura de árboles para las 1,011 cepas
# Leer los árboles generados previamente con las 1,011 cepas y 6,015 ORFs
trees_allstrains <- read.tree("BD/Trees/trees_1011Strains_6015ORFs.tree", tree.names = tree_names)
# Leer el árbol de consenso de evidencia total para las 1,011 cepas
tree_TE1011 <- read.tree("BD/Trees/tree_TE1011.tree", tree.names = "TE_1011")
# Leer el árbol de consenso con ORFs codificantes para las 1,011 cepas
tree_TE1011COD <- read.tree("BD/Trees/tree_TE1011COD.tree", tree.names = "TE_1011COD")
# Leer el árbol de consenso con ORFs no codificantes para las 1,011 cepas
tree_TE1011NOCOD <- read.tree("BD/Trees/tree_TE1011NOCOD.tree", tree.names = "TE_1011NOCOD")
# Combinar todos los árboles en una lista
trees_allstrains <- c(tree_TE1011, tree_TE1011COD, tree_TE1011NOCOD, trees_allstrains)
# Asignar nombres a los árboles en la lista
names(trees_allstrains) <- c("TE_1011", "TE_1011COD", "TE_1011NOCOD", tree_names)
# Cambiar la clase de la lista a "multiPhylo" para manejo eficiente
class(trees_allstrains) <- "multiPhylo"
# Lectura de árboles para las 270 cepas
# Leer los árboles generados previamente con las 270 cepas y 6,015 ORFs
trees_270strains <- read.tree("BD/Trees/trees_270Strains_6015ORFs.tree", tree.names = tree_names)
# Leer el árbol de consenso de evidencia total para las 270 cepas
tree_TE270 <- read.tree("BD/Trees/tree_TE270.tree", tree.names = "TE_270")
# Leer el árbol de consenso con ORFs codificantes para las 270 cepas
tree_TE270COD <- read.tree("BD/Trees/tree_TE270COD.tree", tree.names = "TE_270COD")
# Leer el árbol de consenso con ORFs no codificantes para las 270 cepas
tree_TE270NOCOD <- read.tree("BD/Trees/tree_TE270NOCOD.tree", tree.names = "TE_270NOCOD")
# Combinar todos los árboles en una lista
trees_270strains <- c(tree_TE270, tree_TE270COD, tree_TE270NOCOD, trees_270strains)
# Asignar nombres a los árboles en la lista
names(trees_270strains) <- c("TE_270", "TE_270COD", "TE_270NOCOD", tree_names)
# Cambiar la clase de la lista a "multiPhylo" para manejo eficiente
class(trees_270strains) <- "multiPhylo"
# Cálculo de distancias RF normalizadas
# Calcular la distancia RF normalizada para las 1,011 cepas
distance_trees_norm_1011 <- RF.dist(trees_allstrains, normalize = TRUE, check.labels = TRUE)
# Redondear las distancias a dos decimales para mayor claridad
distance_trees_norm_1011 <- round(distance_trees_norm_1011, 5)
# Convertir las distancias en una matriz
distance_trees_norm_1011 <- as.matrix(distance_trees_norm_1011)
# Calcular la distancia RF normalizada para las 270 cepas
distance_trees_norm_270 <- RF.dist(trees_270strains, normalize = TRUE, check.labels = TRUE)
# Redondear las distancias a dos decimales para mayor claridad
distance_trees_norm_270 <- round(distance_trees_norm_270, 5)
# Convertir las distancias en una matriz
distance_trees_norm_270 <- as.matrix(distance_trees_norm_270)
# Resultado
# Mostrar la matriz de distancias normalizadas para las 1,011 cepas
write.table(distance_trees_norm_1011, file = "distance_trees_norm_1011.txt", row.names = T, col.names = T)
# Mostrar la matriz de distancias normalizadas para las 270 cepas
write.table(distance_trees_norm_270, file = "distance_trees_norm_270.txt", row.names = T, col.names = T)2.3 Agrupamiento de los árboles filogenéticos
## Warning: package 'mstknnclust' was built under R version 4.3.3
#matriz <- read.csv("Analisis_270/Analisis_270/distance_trees_norm_270.txt",header=T,sep=" ")
#results <- mst.knn(as.matrix(matriz))
#igraph::V(results$network)$label.cex <- seq(0.6,0.6,length.out=vcount(results$network))
#plot(results$network, vertex.size=2, label.cex = 0,
#vertex.color=igraph::clusters(results$network)$membership,
#layout=igraph::layout.fruchterman.reingold(results$network, niter=10000),
#main=paste("MST-kNN \n Clustering solution \n Number of clusters=",results$cnumber,sep="" ))