1 Objetivo

En este documento se presenta el flujo de trabajo para obtener un archivo con coordenadas de trayectorias espermáticas, formateado para servir como entrada para el jupyter notebook que permite construir imágenes individuales de las trayectorias espermáticas (llamado traj- ah-6-full-0.ipynb, disponible en 10.7910/DVN/CBMKVA); estas imágenes sirven como entrada para el algoritmo de machine learning de agrupamiento jerárquico, implementado en el artículo de Rodríguez-Martínez et al (2023).

2 Introducción

Algunos sistemas CASA (Computer Assisted Sperm Analysis) permiten recuperar los datos de cada espermatozoide analizado en una rutina de captura. Por rutina de captura me refiero a la captura de una secuencia de video por un tiempo definido (generalmente uno o dos segundos). Entre los datos que se pueden recuperar se encuentran los tradicionales parámetros cinemáticos o de movilidad (VCL, VAP, VSL, LIN, STR, BCF y ALH), aunque pueden obtenerse otros en función del fabricante y de la versión del software utilizado por el sistema CASA.

Los parametros de movilidad son calculados internamente por el software del sistema CASA, y puede existir variación en los algoritmos de cálculo (Amann and Waberski, 2014). Sin embargo, los datos de entrada para los algoritmos que calculan las velocidades (VCL, VAP y VSL) se basan en el rastreo de la cabeza del espermatozoide. El rastreo se realiza gracias al procesamiento de cada una de las imágenes que componen a la secuencia de video, y se refiere a la localización (coordenadas) de la cabeza del espermatozoide a lo largo del tiempo de captura. Con algunos equipos, además de los parametros de movilidad, se pueden recuperar las coordenadas de rastreo.

Un caso notable de software para sistemas CASA es el plugin CASA (Wilson-Leedy and Ingermann, 2007) para ImageJ (Rasband, 1997). Este plugin ha sufrido algunas variaciones, lo que ha permitido adaptarlo a para evaluar espermatozoides de diferentes especies (Giaretta et al., 2017; Rivas et al., 2022). Con este software es posible recuperar las coordenadas de los espermatozoides analizados.

Puesto que los parámetros de movilidad se calculan a partir de las coordenadas, entonces estos parámetros representan una medida condensada del comportamiento cinemático de los espermatozoides. Entonces, existe una gran cantidad de información en las coordenadas, ya que con ellas, además de los parámetros de movilidad, es posible reconstruir la trayectoria seguida por cada espermatozoide analizado. Y estas trayectorias es imposible reconstruirlas a partir de los parámetros de movilidad; es por eso que decimos que cada trayectoria tiene un conjunto asociado de parámetros de movilidad (Rodríguez-Martínez et al., 2023). Pese a la información subyacente en las coordenadas, hasta la fecha, las aproximaciones realizadas para identificar subpoblaciones cinemáticas en un dataset, unicamente han hecho uso de los parámetros de movilidad (Ramón and Martínez-Pastor, 2018).

Las coordenadas pueden utilizarse para reconstruir la imagen de cada trayectoria seguida por los espermatozoides analizados en un sistema CASA. Estas imágenes pueden servir, entonces, como entrada para algoritmos de machine learning que agrupen a las imágenes, para después describir estadísticamente esos grupos (subpoblaciones) con base en los parámetros de movilidad asociados .

En este documento describiremos la forma en como nosotros construimos un dataset de coordenadas, que servirá como entrada para algoritmos de machine learning, en particular el implementado por Rodríguez-Martínez et al (2023). Los datos corresponden a coordenadas de espermatozoides de hamster, analizados en un CASA (SMAS Version 3.18); la velocidad de captura fue a 50 fps durante un segundo (Fujinoki M, Comunicación personal). Del sistema SMAS se obtuvo un par de archivos después de cada rutina de captura, el primer archivo contiene los datos de los parámetros de movilidad, mientras que el segundo contiene las coordenadas de los espermatozoides detectados.

El procedimiento consta de tres etapas: La adquisición y las primeras modificaciones, el agregado de identificadores y, la construcción del objeto final. Nosotros guardamos los archivos con la extensión “ods” (aplicación Calc de LibreOffice), y utilizamos la librería readODS para leerlos.

2.1 Requisitos

Se requieren dos archivos: El primero debe ser el archivo con las coordenadas de las trayectorias (Veáse la figura 1); el segundo archivo debe de ser un archivo que contenga los ID de los espermatozoides analizados (Véase la Figura 2). El segundo archivo se puede generar a partir del archivo que contiene los parámetros de movilidad (Véase la figura 3). Todos los archivos necesario se pueden descargar de. https://doi.org/10.7910/DVN/JAN8RE. En la Figura 1 se puede apreciar la estructura de los datos en el archivo ods original. Puede observarse que la primera columna contiene los identificadores del espermatozoide y la coordenada respectiva (x ó y); el número corresponde al identificador y la x o y entre parentesis indica el valor de la coordenada respectiva.

Figura 1. Captura de pantalla de una hoja de calculo Calc de LibreOffice con datos de coordenadas. El archivo traj_data_test.ods tiene 20 filas y 150 columnas. Cada fila corresponde a una coordenada de un espermatozoide. La primera columna contiene el identificador del espermatozoide, el número es el indicador y la letra entre paréntesis indica la coordenada (x o y). Las restantes 150 columnas contienen el valor de coordenada de cada espermatozoide, en cada frame analizado por el sistema CASA. Es posible que haya filas en donde se encuentren ceros en lugar de valor de coordenada, esto es así debido a que es posible que algún o algunos espermatozoides no hayan sido detectados durante toda la rutina de captura.
Figura 1. Captura de pantalla de una hoja de calculo Calc de LibreOffice con datos de coordenadas. El archivo traj_data_test.ods tiene 20 filas y 150 columnas. Cada fila corresponde a una coordenada de un espermatozoide. La primera columna contiene el identificador del espermatozoide, el número es el indicador y la letra entre paréntesis indica la coordenada (x o y). Las restantes 150 columnas contienen el valor de coordenada de cada espermatozoide, en cada frame analizado por el sistema CASA. Es posible que haya filas en donde se encuentren ceros en lugar de valor de coordenada, esto es así debido a que es posible que algún o algunos espermatozoides no hayan sido detectados durante toda la rutina de captura.
Figura 2. Captura de pantalla de una hoja de calculo Calc de LibreOffice con datos de identificadores de espermatozoides. El archivo tiene 11 filas, la primera línea tiene los nombres de columna, las restantes 10 filas tiene datos de identificadores. Los nombres de las columnas corresponden a dos identificadores distintos, llamados ID1 e ID5
Figura 2. Captura de pantalla de una hoja de calculo Calc de LibreOffice con datos de identificadores de espermatozoides. El archivo tiene 11 filas, la primera línea tiene los nombres de columna, las restantes 10 filas tiene datos de identificadores. Los nombres de las columnas corresponden a dos identificadores distintos, llamados ID1 e ID5
Figura 3. Captura de pantalla del archivo mr_data_test ods. Corresponde a un archivo con datos de parámetros de movilidad. El archivo tiene 11 filas; la primera tiene los nombres de las columnas (parámetrode. movilidad), y las restantes 10 lineas tienen los datos de los distintos parámetros de movilidad evaluados. Las primeras cuatro columnas corresponden a identificadores de los espermatozoides analizados.
Figura 3. Captura de pantalla del archivo mr_data_test ods. Corresponde a un archivo con datos de parámetros de movilidad. El archivo tiene 11 filas; la primera tiene los nombres de las columnas (parámetrode. movilidad), y las restantes 10 lineas tienen los datos de los distintos parámetros de movilidad evaluados. Las primeras cuatro columnas corresponden a identificadores de los espermatozoides analizados.

3 Procedimiento

Todos los archivos utilizados en este flujo de trabajo pueden descargarse desde nuestra cuenta en el sitio de Harvard Dataverse https://dataverse.harvard.edu, como se indicó arriba, o directamente con el siguiente código:

download.file(url = "https://dataverse.harvard.edu/api/access/datafile/8076976", destfile="traj_data_test.ods")
download.file(url = "https://dataverse.harvard.edu/api/access/datafile/8076975", destfile="ID5_data_test.ods")
download.file(url = "https://dataverse.harvard.edu/api/access/datafile/8076974", destfile="mr_data_test.ods")

3.1 Etapa 1, Adquisición y primeras modificaciones

Cargamos la librería necesaria para leer archivos ods:

library(readODS)

Definimos la carpeta de trabajo:

setwd("/Users/andresammx/Documents/RStudio/Markdown/Coordenadas_espermaticas_formato")

Se crea un objeto llamado coord, en este objeto meteremos el contenido del archivo de trabajo que contiene las coordenadas. Se lee el archivo de origen con la instrucción read_ods. El argumento col_names=FALSE indica que la primera línea del archivo no debe interpretarse como los nombres de columna. El argumento as_tibble=FALSE indica que se desea obtener un objeto con la estructura dataframe, y no del tipo tibble.

coord<-read_ods("traj_data_test.ods", col_names=FALSE, as_tibble=FALSE)
## New names:
## • `` -> `...1`
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...11`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`
## • `` -> `...15`
## • `` -> `...16`
## • `` -> `...17`
## • `` -> `...18`
## • `` -> `...19`
## • `` -> `...20`
## • `` -> `...21`
## • `` -> `...22`
## • `` -> `...23`
## • `` -> `...24`
## • `` -> `...25`
## • `` -> `...26`
## • `` -> `...27`
## • `` -> `...28`
## • `` -> `...29`
## • `` -> `...30`
## • `` -> `...31`
## • `` -> `...32`
## • `` -> `...33`
## • `` -> `...34`
## • `` -> `...35`
## • `` -> `...36`
## • `` -> `...37`
## • `` -> `...38`
## • `` -> `...39`
## • `` -> `...40`
## • `` -> `...41`
## • `` -> `...42`
## • `` -> `...43`
## • `` -> `...44`
## • `` -> `...45`
## • `` -> `...46`
## • `` -> `...47`
## • `` -> `...48`
## • `` -> `...49`
## • `` -> `...50`
## • `` -> `...51`
## • `` -> `...52`
## • `` -> `...53`
## • `` -> `...54`
## • `` -> `...55`
## • `` -> `...56`
## • `` -> `...57`
## • `` -> `...58`
## • `` -> `...59`
## • `` -> `...60`
## • `` -> `...61`
## • `` -> `...62`
## • `` -> `...63`
## • `` -> `...64`
## • `` -> `...65`
## • `` -> `...66`
## • `` -> `...67`
## • `` -> `...68`
## • `` -> `...69`
## • `` -> `...70`
## • `` -> `...71`
## • `` -> `...72`
## • `` -> `...73`
## • `` -> `...74`
## • `` -> `...75`
## • `` -> `...76`
## • `` -> `...77`
## • `` -> `...78`
## • `` -> `...79`
## • `` -> `...80`
## • `` -> `...81`
## • `` -> `...82`
## • `` -> `...83`
## • `` -> `...84`
## • `` -> `...85`
## • `` -> `...86`
## • `` -> `...87`
## • `` -> `...88`
## • `` -> `...89`
## • `` -> `...90`
## • `` -> `...91`
## • `` -> `...92`
## • `` -> `...93`
## • `` -> `...94`
## • `` -> `...95`
## • `` -> `...96`
## • `` -> `...97`
## • `` -> `...98`
## • `` -> `...99`
## • `` -> `...100`
## • `` -> `...101`
## • `` -> `...102`
## • `` -> `...103`
## • `` -> `...104`
## • `` -> `...105`
## • `` -> `...106`
## • `` -> `...107`
## • `` -> `...108`
## • `` -> `...109`
## • `` -> `...110`
## • `` -> `...111`
## • `` -> `...112`
## • `` -> `...113`
## • `` -> `...114`
## • `` -> `...115`
## • `` -> `...116`
## • `` -> `...117`
## • `` -> `...118`
## • `` -> `...119`
## • `` -> `...120`
## • `` -> `...121`
## • `` -> `...122`
## • `` -> `...123`
## • `` -> `...124`
## • `` -> `...125`
## • `` -> `...126`
## • `` -> `...127`
## • `` -> `...128`
## • `` -> `...129`
## • `` -> `...130`
## • `` -> `...131`
## • `` -> `...132`
## • `` -> `...133`
## • `` -> `...134`
## • `` -> `...135`
## • `` -> `...136`
## • `` -> `...137`
## • `` -> `...138`
## • `` -> `...139`
## • `` -> `...140`
## • `` -> `...141`
## • `` -> `...142`
## • `` -> `...143`
## • `` -> `...144`
## • `` -> `...145`
## • `` -> `...146`
## • `` -> `...147`
## • `` -> `...148`
## • `` -> `...149`
## • `` -> `...150`
## • `` -> `...151`

La salida que se obtiene al ejecutar la función indica que R ha asignado un nombre-numérico a cada columna de nuestro archivo.

Ahora revisamos la estructura de los datos en el objeto coord:

str(coord)
## 'data.frame':    20 obs. of  151 variables:
##  $ ...1  : chr  "2(x)" "2(y)" "3(x)" "3(y)" ...
##  $ ...2  : num  1054.1 59.1 0 0 556.9 ...
##  $ ...3  : num  1054 59 0 0 557 ...
##  $ ...4  : num  1054.1 58.9 0 0 557 ...
##  $ ...5  : num  1053.9 58.8 0 0 557 ...
##  $ ...6  : num  1053.9 58.8 0 0 557.1 ...
##  $ ...7  : num  1053.8 58.7 0 0 557 ...
##  $ ...8  : num  1053.5 58.7 0 0 556.8 ...
##  $ ...9  : num  1053.7 58.7 0 0 556.9 ...
##  $ ...10 : num  1053.6 58.5 0 0 557 ...
##  $ ...11 : num  1053.5 58.4 0 0 556.9 ...
##  $ ...12 : num  1053.5 58.3 0 0 557 ...
##  $ ...13 : num  1053.5 58.4 0 0 557 ...
##  $ ...14 : num  1053.5 58.2 0 0 556.8 ...
##  $ ...15 : num  1053.5 58.2 1821 76.7 556.7 ...
##  $ ...16 : num  1053.5 58.4 1815.1 82.9 556.6 ...
##  $ ...17 : num  1053.6 58.4 1806.9 86.4 556.6 ...
##  $ ...18 : num  1053.6 58.1 1798.5 82.3 556.8 ...
##  $ ...19 : num  1053.7 58.1 1793.4 79.3 556.6 ...
##  $ ...20 : num  1053.5 58 1789.5 76.6 556.6 ...
##  $ ...21 : num  1053.3 57.8 1787.1 74.7 556.6 ...
##  $ ...22 : num  1053.3 57.8 1787.2 82.1 556.5 ...
##  $ ...23 : num  1053.3 57.6 1793.8 82.6 556.6 ...
##  $ ...24 : num  1053.3 57.6 1800.7 79.9 556.7 ...
##  $ ...25 : num  1053 57.4 1805.8 76.5 556.5 ...
##  $ ...26 : num  1052.9 57.3 1807.2 72 556.4 ...
##  $ ...27 : num  1053.2 57.2 1807.7 67.4 556.5 ...
##  $ ...28 : num  1053.1 57.1 1807.5 63.1 556.7 ...
##  $ ...29 : num  1053.3 56.9 1806.7 59.6 556.6 ...
##  $ ...30 : num  1053 57 1807 56 557 ...
##  $ ...31 : num  1053.2 56.8 1807.8 54.2 556.6 ...
##  $ ...32 : num  1053.3 56.7 1807.1 54.2 556.5 ...
##  $ ...33 : num  1053 56.5 1807.2 52.3 556.5 ...
##  $ ...34 : num  1053.1 56.4 1811.1 42.5 556.5 ...
##  $ ...35 : num  1053.3 56.1 1822 56.3 556.6 ...
##  $ ...36 : num  1053.3 56.2 1832.9 70 556.5 ...
##  $ ...37 : num  1053.2 56.2 1843.8 83.8 556.4 ...
##  $ ...38 : num  1053.2 56 1835.8 89.5 556.4 ...
##  $ ...39 : num  1053.1 55.9 1830.9 93.3 556.4 ...
##  $ ...40 : num  1052.9 55.9 1822.9 94.7 556.4 ...
##  $ ...41 : num  1052.9 55.8 1815.2 93.8 556.4 ...
##  $ ...42 : num  1052.9 55.8 1809.9 92.3 556.4 ...
##  $ ...43 : num  1052.8 55.7 1808.7 91.9 556.4 ...
##  $ ...44 : num  1052.4 55.7 1815.9 93 556.4 ...
##  $ ...45 : num  1052.4 55.6 1823.7 92.9 556.3 ...
##  $ ...46 : num  1052.4 55.5 1828.1 90 556.5 ...
##  $ ...47 : num  1052.4 55.5 1830 86 556.4 ...
##  $ ...48 : num  1052.5 55.2 1830.1 81.8 556.4 ...
##  $ ...49 : num  1052.5 55 1829.9 77.1 556.5 ...
##  $ ...50 : num  1052.3 55 1830 72.8 556.5 ...
##  $ ...51 : num  1052.3 54.9 1828.6 70.4 556.5 ...
##  $ ...52 : num  1052.4 54.7 1827.7 68.7 556.4 ...
##  $ ...53 : num  1052.3 54.7 1827.5 68.7 556.4 ...
##  $ ...54 : num  1052.1 54.7 1827 66.1 556.3 ...
##  $ ...55 : num  1052.1 54.7 1828.4 57.1 556.4 ...
##  $ ...56 : num  1051.9 54.6 1836.3 66.6 556.4 ...
##  $ ...57 : num  1051.9 54.3 1844.1 76.2 556.2 ...
##  $ ...58 : num  1052.1 54.2 1852 85.8 556.4 ...
##  $ ...59 : num  1051.9 54.1 1859.9 95.4 556.2 ...
##  $ ...60 : num  1052 54.1 1855.6 100.8 556.2 ...
##  $ ...61 : num  1052 53.9 1849.1 103.1 556.1 ...
##  $ ...62 : num  1052 53.8 1839.2 102.1 556.2 ...
##  $ ...63 : num  1052 53.7 1831.6 100.5 556 ...
##  $ ...64 : num  1052 53.8 1829.4 100.6 556.1 ...
##  $ ...65 : num  1051.9 53.8 1833.8 102.7 556 ...
##  $ ...66 : num  1052 53.8 1842.8 103.5 556.1 ...
##  $ ...67 : num  1051.7 53.6 1848.3 100.6 556.2 ...
##  $ ...68 : num  1051.5 53.4 1851.2 96.3 556.3 ...
##  $ ...69 : num  1051.7 53.4 1851.6 92 556.2 ...
##  $ ...70 : num  1051.6 53.6 1849.9 87.7 556.1 ...
##  $ ...71 : num  1051.5 53.2 1847.8 85 556.1 ...
##  $ ...72 : num  1051.7 53.1 1846.1 81.8 556 ...
##  $ ...73 : num  1051.7 53 1844.2 79.1 555.8 ...
##  $ ...74 : num  1051.9 53 1843.1 76.6 555.8 ...
##  $ ...75 : num  1052 53 1844.3 74.2 555.9 ...
##  $ ...76 : num  1051.9 53.1 1843.9 64.3 555.7 ...
##  $ ...77 : num  1051.8 53 1853.2 73.9 555.9 ...
##  $ ...78 : num  1051.9 52.9 1862.6 83.5 555.9 ...
##  $ ...79 : num  1051.9 52.8 1871.9 93.1 555.9 ...
##  $ ...80 : num  1051.9 52.8 1881.3 102.7 555.9 ...
##  $ ...81 : num  1051.9 52.8 1873 105.4 555.8 ...
##  $ ...82 : num  1051.9 52.8 1865.4 107.8 555.8 ...
##  $ ...83 : num  1051.9 52.8 1855.8 106.4 555.8 ...
##  $ ...84 : num  1051.7 52.8 1848.2 104 555.7 ...
##  $ ...85 : num  1051.6 52.8 1845.2 102.6 555.8 ...
##  $ ...86 : num  1051.6 52.8 1848 104.9 555.7 ...
##  $ ...87 : num  1051.7 52.8 1857.1 106.3 555.8 ...
##  $ ...88 : num  1051.7 52.8 1861.8 104.1 555.7 ...
##  $ ...89 : num  1051.9 52.9 1864.8 100.5 555.7 ...
##  $ ...90 : num  1051.9 52.8 1866 96.7 555.8 ...
##  $ ...91 : num  1051.9 52.9 1865.7 93.2 555.8 ...
##  $ ...92 : num  1051.5 52.9 1864.1 90.7 555.7 ...
##  $ ...93 : num  1051.5 52.8 1862.9 87.9 555.7 ...
##  $ ...94 : num  1051.5 52.7 1861.6 85.3 555.6 ...
##  $ ...95 : num  1051.6 52.8 1861.3 83.4 555.5 ...
##  $ ...96 : num  1051.3 52.7 1860.7 82.7 555.6 ...
##  $ ...97 : num  1051.4 52.7 1860.2 81.3 555.6 ...
##  $ ...98 : num  1051.4 52.6 1857.6 68.8 555.6 ...
##  $ ...99 : num  1051.4 52.6 1860 84.7 555.7 ...
##   [list output truncated]

Se puede observar que coord tiene 20 observaciones (filas) de 151 variables (columnas). Así, el objeto coord tiene 151 columnas, en donde la primera corresponde a los identificadores de los espermatozoides y el resto corresponde a los valores de las coordenadas x o y.

El siguiente paso es transponer las filas en columnas. Se aplica la función t a cada elemento de coord (sapply). Como argumento a sapply se indica que el resultado debe ser numérico. Se crea un nuevo objeto llamado coord2 con las indicaciones previas y se le da la estructura de dataframe:

coord2<-t(sapply(coord, as.numeric))
## Warning in lapply(X = X, FUN = FUN, ...): NAs introduced by coercion
coord2<-as.data.frame(coord2)

Se observa una advertencia de que la función ha introducido NAs, por lo que procedemos a verificar la estructura de coord2:

str(coord2)
## 'data.frame':    151 obs. of  20 variables:
##  $ V1 : num  NA 1054 1054 1054 1054 ...
##  $ V2 : num  NA 59.1 59 58.9 58.8 ...
##  $ V3 : num  NA 0 0 0 0 0 0 0 0 0 ...
##  $ V4 : num  NA 0 0 0 0 0 0 0 0 0 ...
##  $ V5 : num  NA 557 557 557 557 ...
##  $ V6 : num  NA 257 256 256 256 ...
##  $ V7 : num  NA 1558 1562 1565 1566 ...
##  $ V8 : num  NA 456 456 455 453 ...
##  $ V9 : num  NA 1418 1419 1419 1420 ...
##  $ V10: num  NA 630 629 627 627 ...
##  $ V11: num  NA 1508 1509 1510 1508 ...
##  $ V12: num  NA 0 0 0 0 ...
##  $ V13: num  NA 0 0 0 0 ...
##  $ V14: num  NA 660 660 659 659 ...
##  $ V15: num  NA 549 548 551 550 ...
##  $ V16: num  NA 735 736 736 736 ...
##  $ V17: num  NA 0 0 0 0 0 0 0 0 0 ...
##  $ V18: num  NA 0 0 0 0 0 0 0 0 0 ...
##  $ V19: num  NA 1793 1793 1793 1793 ...
##  $ V20: num  NA 930 930 930 931 ...

Se aprecia que ahora tenemos un dataframe con 151 observaciones (filas) y 20 variables (columnas). La primera fila contiene NAs. Verificamos el inicio del objeto

head(coord2)
##            V1       V2 V3 V4       V5       V6       V7       V8       V9
## ...1       NA       NA NA NA       NA       NA       NA       NA       NA
## ...2 1054.062 59.09278  0  0 556.8834 256.5092 1557.705 455.9799 1417.506
## ...3 1053.990 58.95833  0  0 556.9383 256.4753 1561.764 455.6351 1418.618
## ...4 1054.083 58.87629  0  0 557.0184 256.3190 1564.940 455.1192 1419.067
## ...5 1053.896 58.82292  0  0 557.0000 256.2822 1565.549 452.6732 1420.282
## ...6 1053.937 58.78947  0  0 557.0975 256.3232 1566.778 450.3038 1420.919
##           V10      V11 V12 V13      V14      V15      V16 V17 V18      V19
## ...1       NA       NA  NA  NA       NA       NA       NA  NA  NA       NA
## ...2 629.9663 1508.214   0   0 659.8587 549.4211 735.4737   0   0 1792.862
## ...3 628.6404 1508.910   0   0 659.7297 548.4578 735.6265   0   0 1793.273
## ...4 627.4832 1509.527   0   0 659.3681 550.9625 735.5875   0   0 1793.246
## ...5 626.8588 1508.331   0   0 659.2418 550.0617 736.4691   0   0 1793.362
## ...6 626.3837 1508.648   0   0 658.7337 553.6901 735.1549   0   0 1793.853
##           V20
## ...1       NA
## ...2 930.0461
## ...3 930.0606
## ...4 930.2308
## ...5 930.5797
## ...6 930.0735

Y vemos que efectivamente al inicio del objeto coord2 se inserto una fila que contiene NAs. Para limpiar el objeto seleccionamos unicamente las filas que requerimos:

coord2<-coord2[2:151,]
head(coord2)
##            V1       V2 V3 V4       V5       V6       V7       V8       V9
## ...2 1054.062 59.09278  0  0 556.8834 256.5092 1557.705 455.9799 1417.506
## ...3 1053.990 58.95833  0  0 556.9383 256.4753 1561.764 455.6351 1418.618
## ...4 1054.083 58.87629  0  0 557.0184 256.3190 1564.940 455.1192 1419.067
## ...5 1053.896 58.82292  0  0 557.0000 256.2822 1565.549 452.6732 1420.282
## ...6 1053.937 58.78947  0  0 557.0975 256.3232 1566.778 450.3038 1420.919
## ...7 1053.844 58.73958  0  0 556.9684 256.1202 1568.443 451.9823 1421.352
##           V10      V11 V12 V13      V14      V15      V16 V17 V18      V19
## ...2 629.9663 1508.214   0   0 659.8587 549.4211 735.4737   0   0 1792.862
## ...3 628.6404 1508.910   0   0 659.7297 548.4578 735.6265   0   0 1793.273
## ...4 627.4832 1509.527   0   0 659.3681 550.9625 735.5875   0   0 1793.246
## ...5 626.8588 1508.331   0   0 659.2418 550.0617 736.4691   0   0 1793.362
## ...6 626.3837 1508.648   0   0 658.7337 553.6901 735.1549   0   0 1793.853
## ...7 625.9091 1511.569   0   0 663.0067 556.5714 735.1948   0   0 1794.060
##           V20
## ...2 930.0461
## ...3 930.0606
## ...4 930.2308
## ...5 930.5797
## ...6 930.0735
## ...7 930.0150

En coord2, entonces, las columnas impares corresponden a x, mientras que las columnas pares corresponden a y. Se ha mantenido el orden, pero se han perdido los indicadores de los espermatozoides.

La siguiente tarea es dar formato largo a coord2. Para ello, la columna 3 debe quedar debajo de la columna 1, la columna cinco debe quedar debajo de la columna 3 y así sucesivamente. Lo mismo debe de ocurrir para las columnas pares. Para lograr esto debemos primero crearemos un objeto que llamaremos col_odd, el cual tendrá la misma longitud que coord y contendrá los indicadores de las columnas impares:

col_odd<-seq_len(ncol(coord2)) %% 2

Enseguida crearemos dos objetos llamados only_x y only_y, en ellos meteremos las columnas impares (que corresponden a x) y las columnas pares (que corresponden a y):

only_x<-coord2[, col_odd == 1]
only_y<-coord2[, col_odd == 0]

Verificamos el contenido de cada objeto:

head(only_x)
##            V1 V3       V5       V7       V9      V11 V13      V15 V17      V19
## ...2 1054.062  0 556.8834 1557.705 1417.506 1508.214   0 549.4211   0 1792.862
## ...3 1053.990  0 556.9383 1561.764 1418.618 1508.910   0 548.4578   0 1793.273
## ...4 1054.083  0 557.0184 1564.940 1419.067 1509.527   0 550.9625   0 1793.246
## ...5 1053.896  0 557.0000 1565.549 1420.282 1508.331   0 550.0617   0 1793.362
## ...6 1053.937  0 557.0975 1566.778 1420.919 1508.648   0 553.6901   0 1793.853
## ...7 1053.844  0 556.9684 1568.443 1421.352 1511.569   0 556.5714   0 1794.060
head(only_y)
##            V2 V4       V6       V8      V10 V12      V14      V16 V18      V20
## ...2 59.09278  0 256.5092 455.9799 629.9663   0 659.8587 735.4737   0 930.0461
## ...3 58.95833  0 256.4753 455.6351 628.6404   0 659.7297 735.6265   0 930.0606
## ...4 58.87629  0 256.3190 455.1192 627.4832   0 659.3681 735.5875   0 930.2308
## ...5 58.82292  0 256.2822 452.6732 626.8588   0 659.2418 736.4691   0 930.5797
## ...6 58.78947  0 256.3232 450.3038 626.3837   0 658.7337 735.1549   0 930.0735
## ...7 58.73958  0 256.1202 451.9823 625.9091   0 663.0067 735.1948   0 930.0150

Ahora se deben crear dos nuevos objetos, los cuales contendrán los datos apilados de las columnas x o de las columnas y:

only_x<-data.frame(x=unlist(only_x, use.names=FALSE))
only_y<-data.frame(y=unlist(only_y, use.names=FALSE))

Ahora crearemos un nuevo objeto llamado traj, que estará conformado por dos columnas, la primera columna será aquella con el contenido del objeto only_x y la segunda columna será aquella con el contenido de only_y:

traj<-cbind(only_x, only_y)

Verificamos la estructura del objeto traj:

str(traj)
## 'data.frame':    1500 obs. of  2 variables:
##  $ x: num  1054 1054 1054 1054 1054 ...
##  $ y: num  59.1 59 58.9 58.8 58.8 ...

Se observa que nuestro objeto traj tiene ahora dos variables (las columnas x y y) y 1500 observaciones o líneas de datos, en total el dataframe tiene 3000 datos. Este numero se obtiene multiplicando los 150 pares de coordenadas multiplicado por 10 observaciones (espermatozoides) por 2 coordenadas.

3.2 Etapa 2, Creación de los identificadores

Al objeto traj se le deben agregar columnas con los identificadores de cada espermatozoide. Estos identificadores son de tipo string. Cada columna se debe nombrar de ID1 a ID5. El contenido de cada columna puede cambiar de acuerdo al experimento realizado. En este ejemplo el contenido de cada ID es el siguiente: 1. ID1, es la clave de la rutina de captura 2. ID2, es el numero de experimento realizado (o el número de semental o unidad experimental) 3. ID3, es el tratamiento experimental (o nivel de factor) 4. ID4, es el tiempo de incubación 5. ID5, es el ID del espermatozoide en la rutina de captura. Este ID debe concordar exactamente con el archivo de parámetros de movilidad

Se hacer notar que el contenido de las columnas indentificadoras cambiará en función de los archivos de entrada.

Ahora se debe crear un archivo con las coordenadas de todo el experimento. Se debe preparar cuidadosamente un archivo con el orden de los id de los espermatozoides analizados. Se abre ese archivo en R y de allí se obtendrán los objetos ID1 e ID5.

Se crea un objeto que contenga los ID faltantes. Este objeto contendrá los datos presentados en la Figura 2.

ID_faltantes<-read_ods("ID5_data_test.ods", col_names=TRUE, as_tibble=FALSE)

Verificamos sus contenido:

str(ID_faltantes)
## 'data.frame':    10 obs. of  2 variables:
##  $ ID1 : chr  "obs1_1" "obs1_1" "obs1_1" "obs1_1" ...
##  $ ID.5: num  2 3 5 7 9 10 11 13 14 15

Como el objeto traj tiene 150 coordenadas para cada espermatozoide, entonces debemos tener 150 veces cada identificador; así que del objeto ID_faltantes tomamos la columna 1 y la metemos en un nuevo objeto que llamaremos ID1. Enseguida crearemos 150 repeticiones de cada valor en ID1:

ID1<-ID_faltantes[,1]
ID1<-rep(ID1, each=150)
length(ID1)
## [1] 1500

Puesto que cada archivo que se utilice como entrada para este flujo de trabajo puede tener un número distinto de líneas (espermatozoides analizados), ahora crearemos un objeto llamado num_row. Este objeto tendrá el número de líneas necesario para cada archivo de entrada en particular.

num_row<-(ncol(coord2)/2)*150
num_row
## [1] 1500

El objeto num_row nos servirá para generar tantas líneas como sea necesario para el ID2, el ID3 y el ID4

ID2<-rep("exp1", num_row)
ID3<-rep("tratamiento1", num_row)
ID4<-rep("0h", num_row)

Para el crear el objeto ID5, tomaremos el contenido de la columna 2 del objeto ID_faltantes y lo multiplicaremos por 150.

ID5<-ID_faltantes[,2]
ID5<-rep(ID5, each=150)

3.3 Etapa 3, Creación del objeto final

Finalmente, creamos un nuevo objeto, llamado traj2, con la unión de las columnas identificadoras y traj. Note que los identificadores de los espermatozoides cambian a lo largo del dataframe.

traj2<-as.data.frame(cbind(ID1, ID2, ID3, ID4, ID5, traj))
str(traj2)
## 'data.frame':    1500 obs. of  7 variables:
##  $ ID1: chr  "obs1_1" "obs1_1" "obs1_1" "obs1_1" ...
##  $ ID2: chr  "exp1" "exp1" "exp1" "exp1" ...
##  $ ID3: chr  "tratamiento1" "tratamiento1" "tratamiento1" "tratamiento1" ...
##  $ ID4: chr  "0h" "0h" "0h" "0h" ...
##  $ ID5: num  2 2 2 2 2 2 2 2 2 2 ...
##  $ x  : num  1054 1054 1054 1054 1054 ...
##  $ y  : num  59.1 59 58.9 58.8 58.8 ...
head(traj2)
##      ID1  ID2          ID3 ID4 ID5        x        y
## 1 obs1_1 exp1 tratamiento1  0h   2 1054.062 59.09278
## 2 obs1_1 exp1 tratamiento1  0h   2 1053.990 58.95833
## 3 obs1_1 exp1 tratamiento1  0h   2 1054.083 58.87629
## 4 obs1_1 exp1 tratamiento1  0h   2 1053.896 58.82292
## 5 obs1_1 exp1 tratamiento1  0h   2 1053.937 58.78947
## 6 obs1_1 exp1 tratamiento1  0h   2 1053.844 58.73958
tail(traj2)
##         ID1  ID2          ID3 ID4 ID5        x        y
## 1495 obs1_1 exp1 tratamiento1  0h  15 1805.636 931.2121
## 1496 obs1_1 exp1 tratamiento1  0h  15 1805.554 931.2769
## 1497 obs1_1 exp1 tratamiento1  0h  15 1805.403 931.4478
## 1498 obs1_1 exp1 tratamiento1  0h  15 1804.460 930.5397
## 1499 obs1_1 exp1 tratamiento1  0h  15 1804.113 931.2903
## 1500 obs1_1 exp1 tratamiento1  0h  15 1803.900 931.3500

En función de los settings del sistema CASA, es posible que algunos espermatozoides no hayan sido detectados en los 150 frames, por lo que se encontrarán valores de cero. Esos valores de cero representarán un problema al momento de reconstruir las imágenes de las trayectorias, por lo que debemos eliminarlos. Este proceso ocurre en dos pasos, primero substituimos todos los ceros con NAs:

traj2[traj2 == 0]<-NA

Y después, con ayuda de la función drop_na de la librería tidyr, eliminamos todas las filas con NAs:

library(tidyr)
traj2<- drop_na(traj2)

Verificamos nuestro objeto traj2:

str(traj2)
## 'data.frame':    1379 obs. of  7 variables:
##  $ ID1: chr  "obs1_1" "obs1_1" "obs1_1" "obs1_1" ...
##  $ ID2: chr  "exp1" "exp1" "exp1" "exp1" ...
##  $ ID3: chr  "tratamiento1" "tratamiento1" "tratamiento1" "tratamiento1" ...
##  $ ID4: chr  "0h" "0h" "0h" "0h" ...
##  $ ID5: num  2 2 2 2 2 2 2 2 2 2 ...
##  $ x  : num  1054 1054 1054 1054 1054 ...
##  $ y  : num  59.1 59 58.9 58.8 58.8 ...

Y vemos que todas las filas con ceros han sido eliminadas:

head(traj2)
##      ID1  ID2          ID3 ID4 ID5        x        y
## 1 obs1_1 exp1 tratamiento1  0h   2 1054.062 59.09278
## 2 obs1_1 exp1 tratamiento1  0h   2 1053.990 58.95833
## 3 obs1_1 exp1 tratamiento1  0h   2 1054.083 58.87629
## 4 obs1_1 exp1 tratamiento1  0h   2 1053.896 58.82292
## 5 obs1_1 exp1 tratamiento1  0h   2 1053.937 58.78947
## 6 obs1_1 exp1 tratamiento1  0h   2 1053.844 58.73958

Verificamos que los id de los espermatozoides del archivo traj2 correspondan con los id de las filas en el archivo de parámetros de movilidad:

unique(traj2$ID5)
##  [1]  2  3  5  7  9 10 11 13 14 15

En este punto podemos crear un archivo csv para usarlo posteriormente:

write.csv(traj2, "traj_exp1.csv")

O bien, podemos crear un nuevo objeto con un nombre distinto, por ejemplo:

end_1<-traj2

En el caso de que repitieramos nuestro flujo de trabajo con otros archivos, entonces el ultimo objeto creado (end_1) nos serviría para crear un objeto final, que contenga todos los datos de los archivos procesados. Esto se puede hacer de la siguiente manera:

end_all<-as.data.frame(rbind(end_1, end_2, end_3, end_4, end_5, end_6, end_7, end_8))

Ya sea que procesemos uno o más archivos con datos de trayectorias, se debe recordar que para para meter en conjunto el archivo traj y el archivo de parámetros de movilidad al jupyter notebook traj- ah-6-full-0.ipynb, nuestro objeto final traj2 y el archivo de parámetros de movilidad deben tener el mismo numero de observaciones (líneas) (Ver la Figura 3).

Debe recordarse que los archivos que van a servir como entrada para el jupyter notebook traj- ah-6-full-0.ipynb, deben tener extensión csv y que, en el caso del archivo de coordenadas se debe eliminar la primera línea (la que contiene los nombres de las columnas). Mientras que el archivo de parámetros de movilidad si debe llevar la línea con los nombres de las columnas.

4 Conclusiones

Con el flujo de trabajo propuesto en este documento se puede modificar un archivo de coordenadas, para obtener el formato largo. Así mismo, se pueden generar automáticamente los ID de los espermatozoides para que cada conjunto de coordenadas tenga su propio identificador.

5 Agradecimientos

Los autores agradecen al Dr. Masakatsu Fujinoki por compartir sus datos.

Bibliografía

Amann RP and Waberski D (2014) Computer-assisted sperm analysis (CASA): Capabilities and potential developments. Theriogenology 81 5-17.e1-3.
Giaretta E, Munerato M, Yeste M, Galeati G, Spinaci M, Tamanini C, Mari G and Bucci D (2017) Implementing an open-access CASA software for the assessment of stallion sperm motility: Relationship with other sperm quality parameters. Animal Reproduction Science 176 11–19.
Ramón M and Martínez-Pastor F (2018) Implementation of novel statistical procedures and other advanced approaches to improve analysis of CASA data. Reproduction, Fertility, and Development 30 860–866.
Rasband WS (1997) ImageJ, US National Institutes of Health. Bethesda, Maryland, USA.
Rivas AC, Ayala EME and Aragon MA (2022) Effect of various pH levels on the sperm kinematic parameters of boars. South African Journal of Animal Science 52 693–704.
Rodríguez-Martínez EA, Rivas CU, Ayala ME, Blanco-Rodríguez R, Juarez N, Hernandez-Vargas EA and Aragón A (2023) A new computational approach, based on images trajectories, to identify the subjacent heterogeneity of sperm to the effects of ketanserin. Cytometry. Part A 103 655–663.
Wilson-Leedy JG and Ingermann R (2007) Development of a novel CASA system based on open source software for characterization of zebrafish sperm motility parameters. Theriogenology 67 661–672.