1. Cargar directorio y
datos
# Definimos el directorio de trabajo donde están los archivos CSV
setwd("C:/Users/rebec/Desktop/DOCTORADO/2025/Experimentos/OE1/Exp16grados/12-06-2025")
# Cargamos las librerías necesarias
library(readr) # Para importar archivos CSV
library(dplyr) # Para manipulación de datos
library(readxl) # Para leer archivos Excel
1.1 Importar datos
crudos
# Importamos los datos de los sensores 1, 2 y 3
datos.rebeca.brutos1 <- read_csv("AD448125-S1,2,3.CSV") %>%
rename(
Date.Time = `Date/Time`, # Renombramos para evitar caracteres problemáticos
Sensor.serial = User, # Nombre más representativo para el ID del sensor
Sensor.serial2 = `Sensor serial`# En caso tengamos dos columnas similares, diferenciamos
)
# Importamos los datos de los sensores 4, 5 y 6
datos.rebeca.brutos2 <- read_csv("A8447716-S4,5,6.CSV") %>%
rename(
Date.Time = `Date/Time`,
Sensor.serial = `Sensor serial`,
Sensor.serial2 = `User`
)
# Combinamos ambos archivos en una sola tabla
datos <- rbind(datos.rebeca.brutos1, datos.rebeca.brutos2)
2. Asociar sensores con
cámaras
# Cargamos la tabla de cámaras, que asocia sensores con las cámaras de incubación
camaras <- read_excel("camaras.xlsx")
# Extraemos los IDs de los sensores, eliminando valores faltantes
sensores <- na.omit(camaras$ID)
3. Filtrar y renombrar
por sensor
# Para cada sensor, creamos una tabla separada que contenga solo las variables necesarias
for (i in seq_along(sensores)) {
sensor <- sensores[i]
tabla_filtrada <- datos[datos$Sensor.serial == sensor, c("Date.Time", "Value", "Value2")]
colnames(tabla_filtrada) <- c("Date", "OxySens", "TempSens") # Renombramos columnas
assign(paste0("tabla_sensor_", sensor), tabla_filtrada) # Creamos una variable para cada tabla
}
4. Unir tablas y
calcular minutos
# Tomamos como referencia la primera tabla (Sensor 1)
tabla_referencia <- get(paste0("tabla_sensor_", sensores[1]))
DataClean.test <- tabla_referencia
colnames(DataClean.test)[2:3] <- c("OxySens1", "TempSens1")
# Agregamos los datos de los otros sensores a la tabla principal
for (i in 2:length(sensores)) {
sensor <- sensores[i]
tabla_actual <- get(paste0("tabla_sensor_", sensor))
DataClean.test <- cbind(DataClean.test, tabla_actual[, c("OxySens", "TempSens")])
colnames(DataClean.test)[(ncol(DataClean.test) - 1):(ncol(DataClean.test))] <-
c(paste0("OxySens", i), paste0("TempSens", i))
}
# Convertimos la fecha a formato POSIXct para cálculos temporales
DataClean.test$Date <- as.POSIXct(DataClean.test$Date, format = "%d.%m.%Y %H:%M:%S")
# Calculamos el tiempo transcurrido en minutos desde el inicio del experimento
DataClean.test$Minutes <- as.numeric(difftime(DataClean.test$Date, min(DataClean.test$Date), units = "mins"))
5. Detección automática
de incubaciones
# Función que estima la pendiente de O2 vs tiempo para detectar disminuciones de O2
calcular_pendiente <- function(y_vals, x_vals) {
modelo <- lm(y_vals ~ x_vals)
coef(modelo)[2]
}
# Definimos el tamaño de la ventana (n puntos consecutivos)
n <- 10
DataClean.test$IncubStatus <- 0
# Para cada subconjunto de 10 puntos, evaluamos la pendiente
for (i in 1:(nrow(DataClean.test) - n + 1)) {
subset <- DataClean.test[i:(i + n - 1), ]
pendiente <- calcular_pendiente(subset$OxySens2, subset$Minutes)
DataClean.test$IncubStatus[i:(i + n - 1)] <- ifelse(pendiente > 0, 0, 1) # 1 si O2 baja
}
# Refinamos: eliminamos los primeros 4 valores negativos para evitar falsos positivos
reemplazar_primeros_n <- function(vector, n) {
contador <- 0
reemplazar <- FALSE
for (i in 1:length(vector)) {
if (vector[i] == 1) {
if (contador < n) {
vector[i] <- 0
contador <- contador + 1
}
reemplazar <- TRUE
} else if (reemplazar) {
contador <- 0
reemplazar <- FALSE
}
}
return(vector)
}
DataClean.test$IncubStatus <- reemplazar_primeros_n(DataClean.test$IncubStatus, 4)
6. Numerar
incubaciones
# Creamos una columna para identificar el número de cada incubación
DataClean.test$IncubNumber <- 0
incub_counter <- 0
for (i in 1:nrow(DataClean.test)) {
if (DataClean.test$IncubStatus[i] == 1) {
if (i == 1 || DataClean.test$IncubStatus[i - 1] == 0) {
incub_counter <- incub_counter + 1
}
DataClean.test$IncubNumber[i] <- incub_counter
}
}
# Revisamos los números de incubación detectados
unique(DataClean.test$IncubNumber)
## [1] 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
## [26] 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
## [51] 50 51
7. Cálculo de
saturación de oxígeno
# Usamos la función DO.saturation del paquete rMR
library(biglm)
library(DBI)
library(rMR)
# Calculamos la saturación de O2 del Sensor 1 con temperatura, salinidad y elevación conocidas
DataClean.test$OxySat1 <- DO.saturation(DataClean.test$OxySens1, DataClean.test$TempSens1, elevation.m = 0, salinity = 35) * 100
# Guardamos el dataset final procesado
write.csv(DataClean.test, file = "DataClean.csv", row.names = FALSE)
8. Visualización de
resultados
# Graficamos la señal de cada sensor
par(mfrow = c(3, 2))
for (i in 1:6) {
plot(DataClean.test$Minutes, DataClean.test[[paste0("OxySens", i)]], type = "l",
ylab = "DO (mg/L)", xlab = "Minutos", main = paste("Sensor", i))
}

# Destacamos con líneas rojas los segmentos identificados como incubaciones
par(mfrow = c(3, 2))
for (i in 1:6) {
plot(DataClean.test$Minutes, DataClean.test[[paste0("OxySens", i)]], type = "l",
ylab = "DO (mg/L)", xlab = "Minutos", main = paste("Sensor", i))
lines(DataClean.test$Minutes[DataClean.test$IncubStatus == 1],
DataClean.test[[paste0("OxySens", i)]][DataClean.test$IncubStatus == 1],
col = "red", lwd = 2)
}
