Metadatos y mapas de metadatos¶
Traducción asistida por IA - más información y sugerencias
En cualquier análisis científico, rara vez trabajamos solo con los archivos de datos en bruto. Cada archivo viene con su propia información adicional: qué es, de dónde proviene y qué lo hace especial. Esta información extra es lo que llamamos metadatos.
Los metadatos son datos que describen otros datos. Los metadatos rastrean detalles importantes sobre archivos y condiciones experimentales, y ayudan a adaptar los análisis a las características únicas de cada conjunto de datos.
Piense en ello como un catálogo de biblioteca: mientras que los libros contienen el contenido real (datos en bruto), las fichas del catálogo proporcionan información esencial sobre cada libro—cuándo fue publicado, quién lo escribió, dónde encontrarlo (metadatos). En los pipelines de Nextflow, los metadatos se pueden usar para:
- Rastrear información específica de archivos a lo largo del workflow
- Configurar procesos basándose en las características de los archivos
- Agrupar archivos relacionados para análisis conjunto
Objetivos de aprendizaje¶
En esta misión secundaria, exploraremos cómo manejar metadatos en workflows. Comenzando con una hoja de datos simple (a menudo llamada samplesheet en bioinformática) que contiene información básica de archivos, aprenderá a:
- Leer y procesar metadatos de archivos desde archivos CSV
- Crear y manipular mapas de metadatos
- Agregar nuevos campos de metadatos durante la ejecución del workflow
- Usar metadatos para personalizar el comportamiento de los procesos
Estas habilidades lo ayudarán a construir pipelines más robustos y flexibles que puedan manejar relaciones complejas de archivos y requisitos de procesamiento.
Requisitos previos¶
Antes de embarcarse en esta misión secundaria, debería:
- Haber completado el tutorial Hello Nextflow o un curso para principiantes equivalente.
- Sentirse cómodo usando conceptos y mecanismos básicos de Nextflow (procesos, canales, operadores)
0. Primeros pasos¶
Abrir el codespace de entrenamiento¶
Si aún no lo ha hecho, asegúrese de abrir el entorno de entrenamiento como se describe en Configuración del Entorno.
Moverse al directorio del proyecto¶
Vamos a movernos al directorio donde se encuentran los archivos para este tutorial.
Puede configurar VSCode para enfocarse en este directorio:
Revisar los materiales¶
Encontrará un archivo de workflow principal y un directorio data que contiene una hoja de datos y un puñado de archivos de datos.
Contenido del directorio
El workflow en el archivo main.nf es un esqueleto que expandirá gradualmente en un workflow completamente funcional.
La hoja de datos enumera las rutas a los archivos de datos y algunos metadatos asociados, organizados en 3 columnas:
id: autoexplicativo, un ID asignado al archivocharacter: un nombre de personaje, que usaremos más tarde para dibujar diferentes criaturasdata: rutas a archivos.txtque contienen saludos en diferentes idiomas
id,character,recording
sampleA,squirrel,/workspaces/training/side-quests/metadata/data/bonjour.txt
sampleB,tux,/workspaces/training/side-quests/metadata/data/guten_tag.txt
sampleC,sheep,/workspaces/training/side-quests/metadata/data/hallo.txt
sampleD,turkey,/workspaces/training/side-quests/metadata/data/hello.txt
sampleE,stegosaurus,/workspaces/training/side-quests/metadata/data/hola.txt
sampleF,moose,/workspaces/training/side-quests/metadata/data/salut.txt
sampleG,turtle,/workspaces/training/side-quests/metadata/data/ciao.txt
Cada archivo de datos contiene texto de saludo en uno de cinco idiomas (fr: francés, de: alemán, es: español, it: italiano, en: inglés).
También le proporcionaremos una herramienta de análisis de lenguaje en contenedor llamada langid.
Revisar la asignación¶
Su desafío es escribir un workflow de Nextflow que:
- Identifique el idioma en cada archivo automáticamente
- Agrupe archivos por familia de idiomas (idiomas germánicos vs románicos)
- Personalice el procesamiento para cada archivo basándose en su idioma y metadatos
- Organice las salidas por grupo de idiomas
Esto representa un patrón típico de workflow donde los metadatos específicos de archivos impulsan las decisiones de procesamiento; exactamente el tipo de problema que los mapas de metadatos resuelven elegantemente.
Lista de verificación de preparación¶
¿Cree que está listo para comenzar?
- Entiendo el objetivo de este curso y sus requisitos previos
- Mi codespace está en funcionamiento
- He configurado mi directorio de trabajo apropiadamente
- Entiendo la asignación
Si puede marcar todas las casillas, está listo para comenzar.
1. Cargar metadatos desde una hoja de datos¶
Abra el archivo de workflow main.nf para examinar el esqueleto de workflow que le proporcionamos como punto de partida.
| main.nf | |
|---|---|
Puede ver que hemos configurado un factory de canal básico para cargar la hoja de datos de ejemplo como un archivo, pero eso aún no leerá el contenido del archivo. Comencemos agregando eso.
1.1. Leer el contenido con splitCsv¶
Necesitamos elegir un operador que procese el contenido del archivo apropiadamente con el mínimo esfuerzo de nuestra parte.
Como nuestra hoja de datos está en formato CSV, este es un trabajo para el operador splitCsv, que carga cada fila en el archivo como un elemento en el canal.
Realice los siguientes cambios para agregar una operación splitCsv() al código de construcción del canal, más una operación view() para verificar que el contenido del archivo se esté cargando en el canal correctamente.
Tenga en cuenta que estamos usando la opción header: true para indicarle a Nextflow que lea la primera fila del archivo CSV como la fila de encabezado.
Veamos qué sale de eso, ¿de acuerdo? Ejecute el workflow:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [exotic_albattani] DSL2 - revision: c0d03cec83
[id:sampleA, character:squirrel, recording:/workspaces/training/side-quests/metadata/data/bonjour.txt]
[id:sampleB, character:tux, recording:/workspaces/training/side-quests/metadata/data/guten_tag.txt]
[id:sampleC, character:sheep, recording:/workspaces/training/side-quests/metadata/data/hallo.txt]
[id:sampleD, character:turkey, recording:/workspaces/training/side-quests/metadata/data/hello.txt]
[id:sampleE, character:stegosaurus, recording:/workspaces/training/side-quests/metadata/data/hola.txt]
[id:sampleF, character:moose, recording:/workspaces/training/side-quests/metadata/data/salut.txt]
[id:sampleG, character:turtle, recording:/workspaces/training/side-quests/metadata/data/ciao.txt]
Podemos ver que el operador ha construido un mapa de pares clave-valor para cada fila en el archivo CSV, con los encabezados de columna como claves para los valores correspondientes.
Cada entrada de mapa corresponde a una columna en nuestra hoja de datos:
idcharacterrecording
¡Esto es genial! Facilita el acceso a campos específicos de cada archivo.
Por ejemplo, podríamos acceder al ID del archivo con id o a la ruta del archivo txt con recording.
(Opcional) Más sobre mapas
En Groovy, el lenguaje de programación sobre el que se construye Nextflow, un mapa es una estructura de datos clave-valor similar a los diccionarios en Python, objetos en JavaScript o hashes en Ruby.
Aquí hay un script ejecutable que muestra cómo puede definir un mapa y acceder a su contenido en la práctica:
#!/usr/bin/env nextflow
// Crear un mapa simple
def my_map = [id:'sampleA', character:'squirrel']
// Imprimir todo el mapa
println "map: ${my_map}"
// Acceder a valores individuales usando notación de punto
println "id: ${my_map.id}"
println "character: ${my_map.character}"
Aunque no tiene un bloque workflow apropiado, Nextflow puede ejecutar esto como si fuera un workflow:
Y aquí está lo que puede esperar ver en la salida:
1.2. Seleccionar campos específicos con map¶
Digamos que queremos acceder a la columna character de la hoja de datos e imprimirla.
Podemos usar el operador map de Nextflow para iterar sobre cada elemento en nuestro canal y específicamente seleccionar la entrada character del objeto mapa.
Realice las siguientes ediciones al workflow:
Ahora ejecute el workflow nuevamente:
Salida del comando
¡Éxito! Hemos aprovechado la estructura de mapa derivada de nuestra hoja de datos para acceder a los valores de columnas individuales para cada fila.
Ahora que hemos leído exitosamente la hoja de datos y tenemos acceso a los datos en cada fila, podemos comenzar a implementar nuestra lógica de pipeline.
1.3. Organizar los metadatos en un 'mapa de metadatos'¶
En el estado actual del workflow, los archivos de entrada (bajo la clave recording) y los metadatos asociados (id, character) están todos al mismo nivel, como si estuvieran todos en una gran bolsa.
La consecuencia práctica es que cada proceso que consuma este canal necesitaría configurarse con esta estructura en mente:
Eso está bien siempre que el número de columnas en la hoja de datos no cambie. Sin embargo, si agrega incluso solo una columna a la hoja de datos, la forma del canal ya no coincidirá con lo que el proceso espera, y el workflow producirá errores. También hace que el proceso sea difícil de compartir con otros que podrían tener datos de entrada ligeramente diferentes, y podría terminar teniendo que codificar variables en el proceso que no son necesarias para el bloque de script.
Para evitar este problema, necesitamos encontrar una forma de mantener la estructura del canal consistente independientemente de cuántas columnas contenga esa hoja de datos.
Podemos hacer eso recopilando todos los metadatos en un elemento dentro de la tupla, que llamaremos el mapa de metadatos, o más simplemente 'mapa de metadatos'.
Realice las siguientes ediciones a la operación map:
Hemos reestructurado nuestros elementos de canal en una tupla que consta de dos elementos, el mapa de metadatos y el objeto archivo correspondiente.
Ejecutemos el workflow:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [lethal_booth] DSL2 - revision: 0d8f844c07
[[id:sampleA, character:squirrel], /workspaces/training/side-quests/metadata/data/bonjour.txt]
[[id:sampleB, character:tux], /workspaces/training/side-quests/metadata/data/guten_tag.txt]
[[id:sampleC, character:sheep], /workspaces/training/side-quests/metadata/data/hallo.txt]
[[id:sampleD, character:turkey], /workspaces/training/side-quests/metadata/data/hello.txt]
[[id:sampleE, character:stegosaurus], /workspaces/training/side-quests/metadata/data/hola.txt]
[[id:sampleF, character:moose], /workspaces/training/side-quests/metadata/data/salut.txt]
[[id:sampleG, character:turtle], /workspaces/training/side-quests/metadata/data/ciao.txt]
Ahora, cada elemento en el canal contiene primero el mapa de metadatos y segundo el objeto archivo correspondiente:
[
[id:sampleA, character:squirrel],
/workspaces/training/side-quests/metadata/data/bonjour.txt
]
Como resultado, agregar más columnas en la hoja de datos hará que más metadatos estén disponibles en el mapa meta, pero no cambiará la forma del canal.
Esto nos permite escribir procesos que consuman el canal sin tener que codificar los elementos de metadatos en la especificación de entrada:
Esta es una convención ampliamente utilizada para organizar metadatos en workflows de Nextflow.
Conclusión¶
En esta sección, ha aprendido:
- Por qué los metadatos son importantes: Mantener los metadatos con sus datos preserva información importante de archivos a lo largo del workflow.
- Cómo leer hojas de datos: Usar
splitCsvpara leer archivos CSV con información de encabezado y transformar filas en datos estructurados - Cómo crear un mapa de metadatos: Separar metadatos de datos de archivos usando la estructura de tupla
[ [id:value, ...], file ]
2. Manipulando metadatos¶
Ahora que tenemos nuestros metadatos cargados, ¡hagamos algo con ellos!
Vamos a usar una herramienta llamada langid para identificar el idioma contenido en cada archivo de grabación de la criatura.
La herramienta viene pre-entrenada con un conjunto de idiomas, y dado un fragmento de texto, producirá una predicción de idioma y una puntuación de probabilidad asociada, ambas a stdout.
2.1. Importar el proceso y examinar el código¶
Le proporcionamos un módulo de proceso pre-escrito llamado IDENTIFY_LANGUAGE que envuelve la herramienta langid, por lo que solo necesita agregar una declaración de inclusión antes del bloque de workflow.
Realice la siguiente edición al workflow:
Puede abrir el archivo del módulo para examinar su código:
Como puede ver, la definición de entrada usa la misma estructura tuple val(meta), path(file) que acabamos de aplicar a nuestro canal de entrada.
La definición de salida está estructurada como una tupla con una estructura similar a la entrada, excepto que también contiene stdout como tercer elemento.
Este patrón tuple val(meta), path(file), <output> mantiene los metadatos asociados tanto con los datos de entrada como con las salidas mientras fluye a través del pipeline.
Tenga en cuenta que estamos usando el calificador de salida stdout de Nextflow aquí porque la herramienta imprime su salida directamente a la consola en lugar de escribir un archivo; y usamos sed en la línea de comando para eliminar la puntuación de probabilidad, limpiar la cadena eliminando caracteres de nueva línea y devolver solo la predicción de idioma.
2.2. Agregar una llamada a IDENTIFY_LANGUAGE¶
Ahora que el proceso está disponible para el workflow, podemos agregar una llamada al proceso IDENTIFY_LANGUAGE para ejecutarlo en el canal de datos.
Realice las siguientes ediciones al workflow:
| main.nf | |
|---|---|
Tenga en cuenta que hemos eliminado la operación .view() original en la construcción del canal.
Ahora podemos ejecutar el workflow:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [voluminous_mcnulty] DSL2 - revision: f9bcfebabb
executor > local (7)
[4e/f722fe] IDENTIFY_LANGUAGE (7) [100%] 7 of 7 ✔
[[id:sampleA, character:squirrel], /workspaces/training/side-quests/metadata/work/eb/f7148ebdd898fbe1136bec6a714acb/bonjour.txt, fr]
[[id:sampleB, character:tux], /workspaces/training/side-quests/metadata/work/16/71d72410952c22cd0086d9bca03680/guten_tag.txt, de]
[[id:sampleD, character:turkey], /workspaces/training/side-quests/metadata/work/c4/b7562adddc1cc0b7d414ec45d436eb/hello.txt, en]
[[id:sampleC, character:sheep], /workspaces/training/side-quests/metadata/work/ea/04f5d979429e4455e14b9242fb3b45/hallo.txt, de]
[[id:sampleF, character:moose], /workspaces/training/side-quests/metadata/work/5a/6c2b84bf8fadb98e28e216426be079/salut.txt, fr]
[[id:sampleE, character:stegosaurus], /workspaces/training/side-quests/metadata/work/af/ee7c69bcab891c40d0529305f6b9e7/hola.txt, es]
[[id:sampleG, character:turtle], /workspaces/training/side-quests/metadata/work/4e/f722fe47271ba7ebcd69afa42964ca/ciao.txt, it]
¡Excelente! Ahora tenemos una predicción de qué idioma habla cada personaje.
Y como se señaló anteriormente, también hemos incluido el archivo de entrada y el mapa de metadatos en la salida, lo que significa que ambos permanecen asociados con la nueva información que acabamos de producir. Esto resultará útil en el siguiente paso.
Note
De manera más general, este patrón de mantener el mapa de metadatos asociado con los resultados facilita la asociación de resultados relacionados que comparten los mismos identificadores.
Como ya habrá aprendido, no puede confiar en el orden de los elementos en los canales para emparejar resultados entre ellos. En su lugar, debe usar claves para asociar datos correctamente, y los mapas de metadatos proporcionan una estructura ideal para este propósito.
Exploramos este caso de uso en detalle en la misión secundaria Dividir y Agrupar.
2.3. Aumentar metadatos con salidas de procesos¶
Dado que los resultados que acabamos de producir son en sí mismos una forma de metadatos sobre el contenido de los archivos, sería útil agregarlos a nuestro mapa de metadatos.
Sin embargo, no queremos modificar el mapa de metadatos existente en su lugar. Desde un punto de vista técnico, es posible hacer eso, pero no es seguro.
Así que en su lugar, crearemos un nuevo mapa de metadatos que contenga el contenido del mapa de metadatos existente más un nuevo par clave-valor lang: lang_id que contenga la nueva información, usando el operador + (una característica de Groovy).
Y combinaremos esto con una operación map para reemplazar el mapa antiguo con el nuevo.
Aquí están las ediciones que necesita hacer al workflow:
Si aún no está familiarizado con el operador +, o si esto parece confuso, tome unos minutos para revisar la explicación detallada a continuación.
Creación del nuevo mapa de metadatos usando el operador +
Primero, necesita saber que podemos fusionar el contenido de dos mapas usando el operador + de Groovy.
Digamos que tenemos los siguientes mapas:
Podemos fusionarlos así:
El contenido de new_map será:
¡Genial!
¿Pero qué pasa si necesita agregar un campo que aún no es parte de un mapa?
Digamos que comienza nuevamente desde map1, pero la predicción de idioma no está en su propio mapa (no hay map2).
En su lugar, se mantiene en una variable llamada lang_id, y sabe que quiere almacenar su valor ('fr') con la clave lang.
En realidad puede hacer lo siguiente:
Aquí, [lang: new_info] crea un nuevo mapa sin nombre sobre la marcha, y map1 + fusiona map1 con el nuevo mapa sin nombre, produciendo el mismo contenido new_map que antes.
Ordenado, ¿verdad?
Ahora transpongamos eso al contexto de una operación channel.map() de Nextflow.
El código se convierte en:
Esto hace lo siguiente:
map1, lang_id ->toma los dos elementos en la tupla[map1 + [lang: lang_id]]crea el nuevo mapa como se detalló arriba
La salida es un solo mapa sin nombre con el mismo contenido que new_map en nuestro ejemplo anterior.
Entonces efectivamente hemos transformado:
en:
Con suerte puede ver que si cambiamos map1 a meta, eso es básicamente todo lo que necesitamos para agregar la predicción de idioma a nuestro mapa de metadatos en nuestro workflow.
¡Excepto por una cosa!
En el caso de nuestro workflow, también necesitamos tener en cuenta la presencia del objeto file en la tupla, que está compuesta de meta, file, lang_id.
Entonces el código aquí se convertiría en:
Si está teniendo dificultades para entender por qué el file parece estar moviéndose en la operación map, imagine que en lugar de [meta + [lang: lang_id], file], esa línea lee [new_map, file].
Esto debería dejar más claro que simplemente estamos dejando el file en su lugar original en segunda posición en la tupla. Simplemente hemos tomado el valor new_info y lo hemos incorporado al mapa que está en primera posición.
¡Y esto nos lleva de vuelta a la estructura de canal tuple val(meta), path(file)!
Una vez que esté seguro de entender qué está haciendo este código, ejecute el workflow para ver si funcionó:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [cheeky_fermat] DSL2 - revision: d096281ee4
[4e/f722fe] IDENTIFY_LANGUAGE (7) [100%] 7 of 7, cached: 7 ✔
[[id:sampleA, character:squirrel, lang:fr], /workspaces/training/side-quests/metadata/work/eb/f7148ebdd898fbe1136bec6a714acb/bonjour.txt]
[[id:sampleB, character:tux, lang:de], /workspaces/training/side-quests/metadata/work/16/71d72410952c22cd0086d9bca03680/guten_tag.txt]
[[id:sampleC, character:sheep, lang:de], /workspaces/training/side-quests/metadata/work/ea/04f5d979429e4455e14b9242fb3b45/hallo.txt]
[[id:sampleD, character:turkey, lang:en], /workspaces/training/side-quests/metadata/work/c4/b7562adddc1cc0b7d414ec45d436eb/hello.txt]
[[id:sampleF, character:moose, lang:fr], /workspaces/training/side-quests/metadata/work/5a/6c2b84bf8fadb98e28e216426be079/salut.txt]
[[id:sampleE, character:stegosaurus, lang:es], /workspaces/training/side-quests/metadata/work/af/ee7c69bcab891c40d0529305f6b9e7/hola.txt]
[[id:sampleG, character:turtle, lang:it], /workspaces/training/side-quests/metadata/work/4e/f722fe47271ba7ebcd69afa42964ca/ciao.txt]
Sí, ¡eso lo confirma!
Hemos reorganizado ordenadamente la salida del proceso de meta, file, lang_id para que lang_id sea ahora una de las claves en el mapa de metadatos, y las tuplas del canal se ajusten al modelo meta, file una vez más.
2.4. Asignar un grupo de idiomas usando condicionales¶
Ahora que tenemos nuestras predicciones de idioma, usemos la información para asignar algunas agrupaciones nuevas.
En nuestros datos de ejemplo, los idiomas usados por nuestros personajes pueden agruparse en idiomas germánicos (inglés, alemán) e idiomas románicos (francés, español, italiano). Podría ser útil tener esa clasificación fácilmente disponible en algún lugar más adelante en el pipeline, así que agreguemos esa información en el mapa de metadatos.
Y, buenas noticias, ¡este es otro caso que se presta perfectamente para usar el operador map!
Específicamente, vamos a definir una variable llamada lang_group, usar algo de lógica condicional simple para determinar qué valor asignar al lang_group para cada pieza de datos.
La sintaxis general se verá así:
.map { meta, file ->
// la lógica condicional que define lang_group va aquí
[meta + [lang_group: lang_group], file]
}
Puede ver que esto es muy similar a la operación de fusión de mapa sobre la marcha que usamos en el paso anterior. Solo necesitamos escribir las declaraciones condicionales.
Aquí está la lógica condicional que queremos aplicar:
- Definir una variable llamada
lang_groupcon valor predeterminado'unknown'. - Si
langes alemán ('de') o inglés ('en'), cambiarlang_groupagermanic. - De lo contrario, si
langestá incluido en una lista que contiene francés ('fr'), español ('es') e italiano ('it'), cambiarlang_grouparomance.
Intente escribirlo usted mismo si ya sabe cómo escribir declaraciones condicionales en Nextflow.
Tip
Puede acceder al valor de lang dentro de la operación map con meta.lang.
Debería terminar haciendo los siguientes cambios al workflow:
Aquí están los puntos clave:
- Usamos
def lang_group = "unknown"para crear la variablelang_groupcon valor predeterminado establecido enunknown. - Usamos una estructura
if {} else if {}para la lógica condicional, con pruebas alternativas.equals()para los dos idiomas germánicos, y una prueba de existencia en una lista para los tres idiomas románicos. - Usamos la operación de fusión
meta + [lang_group:lang_group]como anteriormente para generar el mapa de metadatos actualizado.
Una vez que todo tenga sentido, ejecute el workflow nuevamente para ver el resultado:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `main.nf` [wise_almeida] DSL2 - revision: 46778c3cd0
[da/652cc6] IDENTIFY_LANGUAGE (7) [100%] 7 of 7, cached: 7 ✔
[[id:sampleA, character:squirrel, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/data/bonjour.txt]
[[id:sampleB, character:tux, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/data/guten_tag.txt]
[[id:sampleC, character:sheep, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/data/hallo.txt]
[[id:sampleD, character:turkey, lang:en, lang_group:germanic], /workspaces/training/side-quests/metadata/data/hello.txt]
[[id:sampleE, character:stegosaurus, lang:es, lang_group:romance], /workspaces/training/side-quests/metadata/data/hola.txt]
[[id:sampleF, character:moose, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/data/salut.txt]
[[id:sampleG, character:turtle, lang:it, lang_group:romance], /workspaces/training/side-quests/metadata/data/ciao.txt]
Como puede ver, los elementos del canal mantienen su estructura [meta, file], pero el mapa de metadatos ahora incluye esta nueva clasificación.
Conclusión¶
En esta sección, ha aprendido cómo:
- Aplicar metadatos de entrada a canales de salida: Copiar metadatos de esta manera nos permite asociar resultados más adelante basándonos en el contenido de los metadatos.
- Crear claves personalizadas: Creó dos nuevas claves en su mapa de metadatos, fusionándolas con
meta + [new_key:value]en el mapa de metadatos existente. Una basada en un valor calculado de un proceso, y una basada en una condición que estableció en el operadormap.
Estos le permiten asociar metadatos nuevos y existentes con archivos a medida que avanza a través de su pipeline. Incluso si no está usando metadatos como parte de un proceso, mantener el mapa de metadatos asociado con los datos de esta manera facilita mantener toda la información relevante junta.
3. Usando información del mapa de metadatos en un proceso¶
Ahora que sabe cómo crear y actualizar el mapa de metadatos, podemos llegar a la parte realmente divertida: usar realmente los metadatos en un proceso.
Más específicamente, vamos a agregar un segundo paso a nuestro workflow para dibujar cada animal como arte ASCII y hacer que diga el texto grabado en una burbuja de diálogo.
Vamos a hacer esto usando una herramienta llamada cowpy.
¿Qué hace cowpy?
cowpy es una herramienta de línea de comandos que genera arte ASCII para mostrar entradas de texto arbitrarias de una manera divertida.
Es una implementación en Python de la herramienta clásica cowsay de Tony Monroe.
______________________________________________________
< Hello Nextflow >
------------------------------------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
Opcionalmente, puede seleccionar un personaje (o 'cowacter') para usar en lugar de la vaca predeterminada.
Si trabajó en el curso Hello Nextflow, ya ha visto esta herramienta en acción. Si no, no se preocupe; cubriremos todo lo que necesita saber a medida que avancemos.
3.1. Importar el proceso y examinar el código¶
Le proporcionamos un módulo de proceso pre-escrito llamado COWPY que envuelve la herramienta cowpy, por lo que solo necesita agregar una declaración de inclusión antes del bloque de workflow.
Realice la siguiente edición al workflow:
Puede abrir el archivo del módulo para examinar su código:
Como puede ver, este proceso está actualmente diseñado para tomar un archivo de entrada (que contiene el texto a mostrar) y un valor que especifica el personaje que debe dibujarse en arte ASCII, generalmente proporcionado a nivel de workflow por un parámetro de línea de comandos.
3.2. Pasar un campo del mapa de metadatos como entrada¶
Cuando usamos la herramienta cowpy en el curso Hello Nextflow, usamos un parámetro de línea de comandos para determinar qué personaje usar para dibujar la imagen final.
Eso tenía sentido, porque solo estábamos generando una imagen por ejecución del pipeline.
Sin embargo, en este tutorial, queremos generar una imagen apropiada para cada sujeto que estamos procesando, por lo que usar un parámetro de línea de comandos sería demasiado limitante.
Buenas noticias: tenemos una columna character en nuestra hoja de datos y por lo tanto, en nuestro mapa de metadatos.
Usemos eso para establecer el personaje que el proceso debería usar para cada entrada.
Para ese fin, necesitaremos hacer tres cosas:
- Dar un nombre al canal de salida que sale del proceso anterior para que podamos operar en él más convenientemente.
- Determinar cómo acceder a la información de interés
- Agregar una llamada al segundo proceso y alimentar la información apropiadamente.
Comencemos.
3.2.1. Nombrar el canal de salida anterior¶
Aplicamos las manipulaciones anteriores directamente en el canal de salida del primer proceso, IDENTIFY_LANGUAGE.out.
Para alimentar el contenido de ese canal al siguiente proceso (y hacerlo de una manera que sea clara y fácil de leer) queremos darle su propio nombre, ch_languages.
Podemos hacer eso usando el operador set.
En el workflow principal, reemplace el operador .view() con .set { ch_languages }, y agregue una línea probando que podemos referirnos al canal por nombre.
Ejecutemos esto:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `./main.nf` [friendly_austin] DSL2 - revision: 3dbe460fd6
[36/cca6a7] IDENTIFY_LANGUAGE (7) | 7 of 7 ✔
[[id:sampleB, character:tux, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/work/e2/6db2402d83cf72081bcd2d11784714/guten_tag.txt]
[[id:sampleA, character:squirrel, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/work/6c/114c818317d169457d6e7336d5d55b/bonjour.txt]
[[id:sampleC, character:sheep, lang:de, lang_group:germanic], /workspaces/training/side-quests/metadata/work/55/68c69c5efb527f3604ddb3daab8057/hallo.txt]
[[id:sampleD, character:turkey, lang:en, lang_group:germanic], /workspaces/training/side-quests/metadata/work/2a/4752055ccb5d1370b0ef9da41d3993/hello.txt]
[[id:sampleE, character:stegosaurus, lang:es, lang_group:romance], /workspaces/training/side-quests/metadata/work/f4/fcd3186dc666d5d239ffa6c37d125d/hola.txt]
[[id:sampleF, character:moose, lang:fr, lang_group:romance], /workspaces/training/side-quests/metadata/work/c3/3b2627f733f278a7088332a5806108/salut.txt]
[[id:sampleG, character:turtle, lang:it, lang_group:romance], /workspaces/training/side-quests/metadata/work/36/cca6a7dbfa26ac24f9329787a32e9d/ciao.txt]
Esto confirma que ahora podemos referirnos al canal por nombre.
3.2.2. Acceder al archivo y metadatos del personaje¶
Sabemos por mirar el código del módulo que el proceso COWPY espera que se le proporcione un archivo de texto y un valor character.
Para escribir la llamada al proceso COWPY, solo necesitamos saber cómo extraer el objeto archivo correspondiente y los metadatos de cada elemento en el canal.
Como suele ser el caso, la forma más simple de hacer eso es usar una operación map.
Nuestro canal contiene tuplas estructuradas como [meta, file], por lo que podemos acceder al objeto file directamente, y podemos acceder al valor character almacenado dentro del mapa de metadatos refiriéndonos a él como meta.character.
En el workflow principal, realice los siguientes cambios de código:
Tenga en cuenta que estamos usando closures (como { file -> "Archivo: " + file }) para hacer la salida de las operaciones .view más legible.
Ejecutemos esto:
Salida del comando
N E X T F L O W ~ version 25.10.2
Launching `./main.nf` [cheesy_cantor] DSL2 - revision: 15af9c1ec7
[43/05df08] IDENTIFY_LANGUAGE (7) [100%] 7 of 7, cached: 7 ✔
Personaje: squirrel
Archivo: /workspaces/training/side-quests/metadata/work/8d/4b9498bbccb7a74f04e41877cdc3e5/bonjour.txt
Archivo: /workspaces/training/side-quests/metadata/work/d3/604274985406e40d79021dea658e60/guten_tag.txt
Personaje: tux
Personaje: turkey
Archivo: /workspaces/training/side-quests/metadata/work/d4/fafcc9415b61d2b0fea872e6a05e8a/hello.txt
Archivo: /workspaces/training/side-quests/metadata/work/02/468ac9efb27f636715e8144b37e9a7/hallo.txt
Personaje: sheep
Personaje: moose
Personaje: stegosaurus
Archivo: /workspaces/training/side-quests/metadata/work/d4/61a7e1188b4f2742bc72004e226eca/salut.txt
Archivo: /workspaces/training/side-quests/metadata/work/ae/68364be238c11149c588bf6fc858b1/hola.txt
Archivo: /workspaces/training/side-quests/metadata/work/43/05df081af5d879ab52e5828fa0357e/ciao.txt
Personaje: turtle
Las rutas de archivo y valores de personaje pueden aparecer en un orden diferente en su salida.
Esto confirma que podemos acceder al archivo y al personaje para cada elemento en el canal.
3.2.3. Llamar al proceso COWPY¶
Ahora pongamos todo junto y llamemos realmente al proceso COWPY en el canal ch_languages.
En el workflow principal, realice los siguientes cambios de código:
Ve que simplemente copiamos las dos operaciones map (menos las declaraciones .view()) como las entradas para la llamada al proceso.
¡Solo asegúrese de no olvidar la coma entre ellas!
Es un poco torpe, pero veremos cómo mejorar eso en la siguiente sección.
Ejecutemos esto:
Salida del comando
Si mira en el directorio de resultados, debería ver los archivos individuales que contienen el arte ASCII de cada saludo hablado por el personaje correspondiente.
Directorio y contenido de archivo de ejemplo
Esto muestra que pudimos usar la información en el mapa de metadatos para parametrizar el comando en el segundo paso del pipeline.
Sin embargo, como se señaló anteriormente, parte del código involucrado fue un poco torpe, ya que tuvimos que desempaquetar metadatos mientras todavía estábamos en el contexto del cuerpo del workflow. Ese enfoque funciona bien para usar un pequeño número de campos del mapa de metadatos, pero escalaría pobremente si quisiéramos usar muchos más.
Hay otro operador llamado multiMap() que nos permite optimizar esto un poco, pero incluso entonces no es ideal.
(Opcional) Versión alternativa con multiMap()
En caso de que se lo esté preguntando, no podíamos simplemente escribir una sola operación map() que produjera tanto el file como el character, porque eso los devolvería como una tupla.
Tuvimos que escribir dos operaciones map() separadas para alimentar los elementos file y character al proceso por separado.
Técnicamente hay otra forma de hacer esto a través de una sola operación de mapeo, usando el operador multiMap(), que es capaz de emitir múltiples canales.
Por ejemplo, podría reemplazar la llamada a COWPY anterior con el siguiente código:
Esto produce exactamente el mismo resultado.
En cualquier caso, es incómodo que tengamos que hacer algo de desempaquetado a nivel de workflow.
Sería mejor si pudiéramos alimentar todo el mapa de metadatos en el proceso y seleccionar lo que necesitamos una vez allí.
3.3. Pasar y usar todo el mapa de metadatos¶
El punto del mapa de metadatos es después de todo pasar todos los metadatos juntos como un paquete. La única razón por la que no pudimos hacer eso anteriormente es que el proceso no está configurado para aceptar un mapa de metadatos. Pero como controlamos el código del proceso, podemos cambiar eso.
Modifiquemos el proceso COWPY para aceptar la estructura de tupla [meta, file] que usamos en el primer proceso para poder optimizar el workflow.
Para ese fin, necesitaremos hacer tres cosas:
- Modificar las definiciones de entrada del módulo de proceso
COWPY - Actualizar el comando del proceso para usar el mapa de metadatos
- Actualizar la llamada al proceso en el cuerpo del workflow
¿Listo? ¡Vamos!
3.3.1. Modificar la entrada del módulo COWPY¶
Realice las siguientes ediciones al archivo de módulo cowpy.nf:
Esto nos permite usar la estructura de tupla [meta, file] que cubrimos anteriormente en el tutorial.
Tenga en cuenta que no actualizamos la definición de salida del proceso para producir el mapa de metadatos, con el fin de mantener el tutorial simplificado, pero siéntase libre de hacerlo usted mismo como ejercicio siguiendo el modelo del proceso IDENTIFY_LANGUAGE.
3.3.2. Actualizar el comando para usar el campo del mapa de metadatos¶
Todo el mapa de metadatos ahora está disponible dentro del proceso, por lo que podemos referirnos a la información que contiene directamente desde dentro del bloque de comando.
Realice las siguientes ediciones al archivo de módulo cowpy.nf:
Hemos reemplazado la referencia al valor character previamente pasado como una entrada independiente con el valor contenido en el mapa de metadatos, al que nos referimos usando meta.character.
Ahora actualicemos la llamada al proceso en consecuencia.
3.3.3. Actualizar la llamada al proceso y ejecutarlo¶
El proceso ahora espera que su entrada use la estructura de tupla [meta, file], que es lo que produce el proceso anterior, por lo que simplemente podemos pasar todo el canal ch_languages al proceso COWPY.
Realice las siguientes ediciones al workflow principal: