Índice
1 análisis de datos genómicos
Considera los datos1 disponibles mediante SSH en carleos2.epv.uniovi.es:/home/manadine/dat/jacatón
## Tienes varias opciones para manejar los datos: ## - Trabajar remotamente usando por ejemplo ## - el comando «ssh» ## - PuTTY ## - Descargar los ficheros en tu ordenador local mediante ## - el comando «scp» ## - el comando «sftp» ## - Filezilla ## - WinSCP ## Por ejemplo: scp -r alumno@carleos2.epv.uniovi.es:/home/manadine/dat/jacatón ~/Documentos
Elabora un informe que incluya explicaciones someras sobre los siguientes pasos:
- Describe los datos.
tamaños de los ficheros
## Si lo quieres hacer directamente en R: ped <- read.table ("~/Documentos/jacatón/pedigriHackathon.txt") met <- read.csv ("~/Documentos/jacatón/MetadatosHackathon.csv") tig <- read.csv ("~/Documentos/jacatón/ContigsTPM.csv") keg <- read.csv ("~/Documentos/jacatón/keggTPM.csv", sep=";") keg <- read.csv2 ("~/Documentos/jacatón/keggTPM.csv", dec=".") # equivale a la anterior
Si los ficheros son demasiado grandes, puedes explorarlos en Bash:
cd ~/Documentos/jacatón head * | cut -c -100 # el kegg parece con sólo una línea file * # indica CR como salto de renglón (archivo de Mac) recode /CR.. keggTPM.csv # cambia CR por LF; si no tienes "recode": cat keggTPM.csv | tr '\r' '\n' > keggTPM-LF.csv wc * # 3 ficheros con 341 renglones (uno de cabecera), 1 fichero con 2996 awk '{print NF}' pedigriHackathon.txt | uniq # se ve a ojo awk -F ',' '{print NF}' ContigsTPM.csv | uniq awk -F ';' '{print NF}' keggTPM.csv | uniq awk -F ',' '{print NF}' MetadatosHack* | uniq # también a ojo ## Dividimos en bloques de 20000 variables para hacer más manejable el fichero de cóntigos: cut -d ',' -f -20000 ContigsTPM.csv > Contigs10000a.csv cut -d ',' -f 20001- ContigsTPM.csv > Contigs10000b.csv
- tipos de variables y relaciones entre ellas
- pedigrí
- 3 variables con números enteros = identificadores de individuo
- animal
- padre
- madre
- el valor 0 indica dato desconocido
- 3 variables con números enteros = identificadores de individuo
- metadatos
- x = identificador en el pedrigrí
- reb = rebaño
- RecordingYear = año
- week = semana (junto con el año, darían la fecha de medición)
- NLACTA = número de parto o lactación
- DIASLE = número de días en la lactación
- PGRASADC = porcentaje de grasa; variable respuesta
- cóntigos
- primera columna sin nombre = identificador del pedigrí
- el resto de columnas son cuantificaciones de la presencia de cada cóntigo (tig000…)
- keg
- ID = identificador en el pedigrí
- el resto de columnas son cuantificaciones de la presencia de cada función keg (K000…)
- pedigrí
Consigue un único dataframe a partir de todas las tablas disponibles.
mettigkeg <- cbind(met,tig,keg) todos <- merge (ped, mettigkeg, by.x = "V1", by.y = "x", all = TRUE)
- Construye un modelo de predicción para PGRASADC.
explora variables individualmente
r2 <- sapply (setdiff (names(todos), "PGRASADC"), #quito la variable dependiente function (variable) try (summary (lm (paste ("PGRASADC ~", variable), todos)) $ r.squared)) mode(r2) <- "numeric" # permite conservar las etiquetas del vector
- obtenemos un vector de \(R^2\)
- usamos
trypara evitar que un error impida la creación del resultado- consecuencia: si hay algún error, el resultado deja de ser numérico y pasa a ser un vector de cadenas (
character) - podríamos usar
as.numericpara convertir a numérico, pero se perderían las etiquetas podríamos recuperarlas así:
R2 <- as.numeric(r2); names(R2) <- names(r2) #+end_src R - para hacerlo de un solo paso, cambiamos directamene el modo: #+begin_src R mode(r2) <- "numeric"
- consecuencia: si hay algún error, el resultado deja de ser numérico y pasa a ser un vector de cadenas (
- podemos intentar acelerar el proceso usando paralelismo
- usaremos la función
mclapplydel paqueteparallel - simplemente sustituyendo
sapplypormclapplyse trabaja con dos hilos de ejecucición (valor por omisión de la opciónmc.cores) detectCores()nos dice cuántos hilos en paralelo se podrían ejecutarrecomendación: dejar un hilo libre, para que el sistema responda sin problemas a la interacción con el usuario
library (parallel) # para mclapply nombres <- setdiff (names(todos), "PGRASADC") r2 <- as.numeric (mclapply (nombres, function (variable) try (summary (lm (paste ("PGRASADC ~", variable), todos)) $ r.squared), mc.cores = detectCores() - 1)) names (r2) <- nombres # mclapply no crea etiquetas a partir de su primer argumento
- si necesitas medir el tiempo, puedes usar
system.timesystem.time ( {instrucción1 ; instrucción2 ; ...} )proc.timecomienzo <- proc.time() instrucción1 ; instrucción2 ; ... proc.time() - comienzo
- usaremos la función
exploramos los valores altos
max (r2) # hay un ajuste perfecto which.max (r2) # variable «Unclassified.» summary (todos$Unclassified.) # factor pero debería ser numérica
Vamos a intentar corregir la anomalía:
niveles <- levels (todos$Unclassified.) niveles [is.na (as.numeric (niveles)) # qué valores no pueden convertirse en números ## vemos que se trata de valores con un punto al final; ## podemos pensar en varias opciones: ## - eliminar el punto final ## - quedarnos solamente con cuatro decimales ## vamos a desarrollar ambas ## eliminar punto final, versión 1: niveles.nuevos <- ifelse (is.na (as.numeric (niveles)), substring (niveles, 1, nchar(niveles)-1), niveles) ## eliminar punto final, versión 2: niveles.nuevos <- gsub ("\\.$", "", niveles) ## mantener solo cuatro decimales, suponiendo que el punto decimal siempre está en la posición 7 all (substring (niveles, 7, 7) == ".") # comprobación niveles.nuevos <- substring (niveles, 1, 11) ## mantener solo cuatro decimales, versión general 1: niveles.nuevos <- substring (niveles, 1, 4 + regexpr ("\\.", niveles)) ## mantener solo cuatro decimales, versión general 2: niveles.nuevos <- gsub ("(.*\\..{4}).*", "\\1", niveles) ## en cualquier caso, hay que asignarlos levels (todos$Unclassified.) <- niveles.nuevos todos$inclasificables <- as.numeric (as.character (todos$Unclassified.)) # equivale a la siguiente todos$inclasificables <- as.numeric (niveles.nuevos) [todos$Unclassified.] # recomendado por ?factor r2[which.max(r2)] <- summary (lm (PGRASADC ~ inclasificables, todos)) $ r.squared
construye un modelo con las mejores variables (explora cuántas puede asimilar tu ordenata)
tail (sort(r2), 100) names (tail (sort(r2), 100)) paste (names(tail(sort(r2),100)), collapse="+") paste ("PGRASADC~", paste(names(tail(sort(r2),100)),collapse="+")) lm (paste("PGRASADC~",paste(names(tail(sort(r2),100)),collapse="+")), todos) step (lm(paste("PGRASADC~",paste(names(tail(sort(r2),100)),collapse="+")),todos)) -> modelo
Describe la calidad del modelo.
summary (modelo)
Lo que nos interesa no es tanto la calidad del modelo en sí, sino la de la técnica para obtenerlo. Vamos a considerar solamente los registros con dato de PGRASADC.
válidos <- todos [!is.na(todos$PGRASADC), ]usa la misma técnica para calcular un modelo con el 80% de los datos
entrena <- sample (nrow(válidos), 0.8*nrow(válidos)) # índices de los datos de entrenamiento modelo80 <- step (lm(paste("PGRASADC~",paste(names(tail(sort(r2),100)), collapse="+")),válidos[entrena,])) # ^^^^^^^^^^^^^^^^^ summary (modelo80) # calidad similar a «modelo»
estima el 20% restante
estimas <- predict (modelo80, válidos[-entrena,])compara con datos reales
reales <- válidos[-entrena,"PGRASADC"] plot (reales ~ estimas) cor (estimas, reales) ^ 2 summary (lm (reales ~ estimas))
repite eso varias veces y obtén valores medios
correlas <- replicate (10, { entrena <- sample (nrow(válidos), 0.8*nrow(válidos)) # índices de los datos de entrenamiento modelo80 <- step (lm(paste("PGRASADC~",paste(names(tail(sort(r2),100)), collapse="+")),válidos[entrena,]), trace = 0) estimas <- predict (modelo80, válidos[-entrena,]) reales <- válidos[-entrena,"PGRASADC"] cor (estimas, reales) ^ 2 }) summary (correlas)
2 otra forma de combinar los dataframes
2.1 homogeneizar el nombre de la variable identificadora
names(met)[1] <- "ID" names(tig)[1] <- "ID" names(ped) <- c("ID","progenitor1","progenitor2")
comprobamos el resultado mirando los nombres de las tres primeras variables de cada dataframe:
sapply (list (keg, met, tig, ped),
function (x) names(x)[1:3])
si quisiéramos generalizarlo, podríamos aplicarlo a cada dataframe en memoria cuyo nombre conste de sólo tres letras:
nombres3 <- ls() [sapply(ls(),nchar)==3] #nombres de variables con 3 letras nombres.df <- ls() [sapply(sapply(ls(),get), is.data.frame)] #nombres de # variables que son dataframes; otra forma: nombres.df <- Filter (function(x) is.data.frame(get(x)), ls()) ## nombres de los dataframes de tres letras: nombres3df <- intersect (nombres3, nombres.df) ## en un renglón: nombres3df <- ls()[sapply(ls(), nchar) == 3 & sapply (sapply(ls(),get), is.data.frame)] ## convierto las cadenas en los dataframes correspondientes: df3 <- lapply (nombres3df, get) ## aplico a cada dataframe una función que extrae los nombres de sus tres primeras columnas: sapply (df3, function(x) names(x)[1:3])
así comprobamos que el identificador es "ID" en cada uno de ellos
2.2 combinar dos a dos los dataframes
como el identificador es constante, no necesito usar las opciones "by.x" y "by.y" de "merge":
kegmet <- merge (keg, met) kegmettig <- merge (kegmet, tig) kegmettigped <- merge (kegmettig, ped)
luego puedo realizar los tres merges en una sola operación:
kegmettigped <- Reduce (merge, list (keg, met, tig, ped)) ## o bien, si quisiera la solución general plantead arriba: kegmettigped <- Reduce (merge, df3)
para conservar todos los registros:
mezclar <- function (x, y) merge (x, y, all=TRUE) kegmettigped <- Reduce (mezclar, list (keg, met, tig, ped))
ojo porque, según el orden de los dataframes, la operación puede llevar de pocos segundos a varios minutos:
> system.time (print (dim (Reduce (mezclar, list(tig,keg,met,ped))))) [1] 2996 41058 user system elapsed 10.957 0.012 10.971 > system.time (print (dim (Reduce (mezclar, list(tig,keg,ped,met))))) [1] 2996 41058 user system elapsed 12.472 0.163 12.638 > system.time (print (dim (Reduce (mezclar, list(met,ped,tig,keg))))) [1] 2996 41058 user system elapsed 273.368 0.352 274.310 > system.time (print (dim (Reduce (mezclar, list(tig,met,ped,keg))))) [1] 2996 41058 user system elapsed 13.306 0.000 13.307 > system.time (print (dim (Reduce (mezclar, list(tig,ped,met,keg))))) [1] 2996 41058 user system elapsed 14.695 0.000 14.696 > system.time (print (dim (Reduce (mezclar, list(tig,ped,keg,met))))) [1] 2996 41058 user system elapsed 15.221 0.008 15.231 > system.time (print (dim (Reduce (mezclar, list(keg,tig,met,ped))))) [1] 2996 41058 user system elapsed 10.991 0.000 10.991 > system.time (print (dim (Reduce (mezclar, list(keg,met,tig,ped))))) [1] 2996 41058 user system elapsed 9.062 0.000 9.063 > system.time (print (dim (Reduce (mezclar, list(keg,met,ped,tig))))) [1] 2996 41058 user system elapsed 269.692 0.356 270.558 > system.time (print (dim (Reduce (mezclar, list(met,tig,ped,keg))))) [1] 2996 41058 user system elapsed 13.380 0.000 13.382 > system.time (print (dim (Reduce (mezclar, list(keg,tig,ped,met))))) [1] 2996 41058 user system elapsed 12.441 0.120 12.565
parece que conviene poner "tig" (el de más columnas) antes que "ped" (el de más filas)
Nota al pie de página:
Gonzalez-Recio, O., Gutierrez-Rivas M., Atxaerandio R., Goiri I., Rey J., A. Garcia-Rodriguez A. (2019) Rumen metagenome of 340 dairy cows for the SUPREMET Hackathon: “Supercomputacion para la prediccion de enfermedades y caracteres complejos usando informacion del metagenoma”. (Dataset) Version 31 July 2019. Repositorio de datos de Investigacion Agraria. http://data.inia.es/dataset/supremet2019