15 Analizar datos de loggers de temperatura
En estudios de biología, ecología, o agronomía, frecuentemente usamos datos de temperatura de dataloggers. En este estudio vamos a ver como analizar esos datos usando datos de temperatura del altiplano Boliviano cerca de la ciudad de El Alto. El primer paso es transformar los datos del datalogger en un formato que sea fácil de leer para R. Usaremos un archivo CSV y la función read.table()
. El archivo se puede descargar desde el sitio web del libro en GitHub (https://github.com/frareb/myRBook_SP/blob/master/myFiles/E05C13.csv ; el archivo se puede leer desde su destino en GitHub https://raw.githubusercontent.com/frareb/myRBook_SP/master/myFiles/E05C13.csv).
<- read.table("myFiles/E05C13.csv", skip = 1, header = TRUE,
bdd sep = ",", dec = ".", stringsAsFactors = FALSE)
# Desde GitHub:
# bdd <- read.table("https://raw.githubusercontent.com/frareb/myRBook_SP/master/myFiles/E05C13.csv",
# skip = 1, header = TRUE, sep = ",", dec = ".", stringsAsFactors = FALSE)
colnames(bdd) <- c("id", "date", "temp")
head(bdd)
## id date temp
## 1 1 11/12/15 23:00:00 4.973
## 2 2 11/12/15 23:30:00 4.766
## 3 3 11/13/15 00:00:00 4.844
## 4 4 11/13/15 00:30:00 4.844
## 5 5 11/13/15 01:00:00 5.076
## 6 6 11/13/15 01:30:00 5.282
tail(bdd)
## id date temp
## 32781 32781 09/25/17 21:00:00 7.091
## 32782 32782 09/25/17 21:30:00 6.914
## 32783 32783 09/25/17 22:00:00 6.813
## 32784 32784 09/25/17 22:30:00 6.611
## 32785 32785 09/25/17 23:00:00 6.331
## 32786 32786 09/25/17 23:30:00 5.385
str(bdd)
## 'data.frame': 32786 obs. of 3 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ date: chr "11/12/15 23:00:00" "11/12/15 23:30:00" "11/13/15 00:00:00" "11/13/15 00:30:00" ...
## $ temp: num 4.97 4.77 4.84 4.84 5.08 ...
Podemos observar que la fecha esta al formato character
, y que contiene la fecha con el mes, el día, y el año separados con /
, un espacio, y la hora con horas de 0 a 24, minutos, y segundos, separados con :
(ejemplo: 11/12/15 23:00:00
para el 12 de Noviembre de 2015 a las 11 de la noche). Vamos a separar la información en varios objetos para ver todas las opciones segun el tipo de datos que se puede tener.
Primero vamos a separar la fecha de la hora. Para esto vamos a usar la función strsplit()
usando como separador el espacio entre la fecha y la hora.
strsplit("11/12/15 23:00:00", split = " ")
## [[1]]
## [1] "11/12/15" "23:00:00"
Como indican los corchetes dobles, la función devuelve un objeto en el formato list
. Nosotros queremos el vector
que corresponde al primer elemento de la list
entonces vamos a añadir [[1]]
.
strsplit("11/12/15 23:00:00", split = " ")[[1]]
## [1] "11/12/15" "23:00:00"
El primer elemento del vector
es la fecha. Para tener todas las fechas vamos a hacer un bucle con la función sapply()
.
<- sapply(strsplit(bdd[, 2], split = " "), "[[", 1)
bddDay head(bddDay)
## [1] "11/12/15" "11/12/15" "11/13/15" "11/13/15" "11/13/15" "11/13/15"
A continuación vamos a necesitar las fechas en el formato Date
. Entonces necesitamos transformar el objeto en el formato Date
con la función as.Date()
.
<- as.Date(sapply(strsplit(bdd[, 2], split = " "), "[[", 1), format = "%m/%d/%y")
bddDay head(bddDay)
## [1] "2015-11-12" "2015-11-12" "2015-11-13" "2015-11-13" "2015-11-13"
## [6] "2015-11-13"
Vamos a añadir la information al formato Date
en nuestro objeto bdd
. Con la función str()
, podemos ver que el formato de bdd$day
es Date
.
$day <- bddDay
bddstr(bdd)
## 'data.frame': 32786 obs. of 4 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ date: chr "11/12/15 23:00:00" "11/12/15 23:30:00" "11/13/15 00:00:00" "11/13/15 00:30:00" ...
## $ temp: num 4.97 4.77 4.84 4.84 5.08 ...
## $ day : Date, format: "2015-11-12" "2015-11-12" ...
Si necesitamos la información del horario, usaremos el formato POSIX
con la función as.POSIXct()
. Vamos a añadir la information al formato POSIX
en nuestro objeto bdd
. Con la función str()
, podemos ver que el formato de bdd$posix
es POSIXct
.
<- as.POSIXct(bdd$date, format = "%m/%d/%y %H:%M:%S")
bddPosix head(bddPosix)
## [1] "2015-11-12 23:00:00 CET" "2015-11-12 23:30:00 CET"
## [3] "2015-11-13 00:00:00 CET" "2015-11-13 00:30:00 CET"
## [5] "2015-11-13 01:00:00 CET" "2015-11-13 01:30:00 CET"
$posix <- bddPosix
bddstr(bdd)
## 'data.frame': 32786 obs. of 5 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ date : chr "11/12/15 23:00:00" "11/12/15 23:30:00" "11/13/15 00:00:00" "11/13/15 00:30:00" ...
## $ temp : num 4.97 4.77 4.84 4.84 5.08 ...
## $ day : Date, format: "2015-11-12" "2015-11-12" ...
## $ posix: POSIXct, format: "2015-11-12 23:00:00" "2015-11-12 23:30:00" ...
En las funciones as.Date()
y as.POSIXct()
tenemos que especificar el formato en el cual esta indicado la fecha con el argumento format
(format = "%m/%d/%y"
y format = "%m/%d/%y %H:%M:%S"
).
código | Valor |
---|---|
%a | dia de la semana abreviado |
%A | dia de la semana |
%b | mes abreviado |
%B | nombre completo del mes |
%d | dia del mes (decimal) |
%j | dia del año (decimal) |
%m | mes (decimal) |
%y | año con dos dígitos |
%Y | año con cuatro dígitos |
%U | semana del año desde el domingo (decimal) |
%W | semana del año desde el lunes (decimal) |
%H | hora 24 |
%I | hora 12 |
%M | minuto |
%S | segundo |
Podemos visualizar los datos con la función plot()
.
plot(x = bdd$day, y = bdd$temp,
type = 'l', ylim = c(-15, 40),
xlab = "Fecha", ylab = "Temperatura (°C)")
Podemos simplificar la información calculando únicamente las temperaturas mínimas, promedias, y máximas del dia con la función tapply()
.
<- tapply(bdd$temp, INDEX = bdd$day, FUN = mean)
tempDayMean <- tapply(bdd$temp, INDEX = bdd$day, FUN = min)
tempDayMin <- tapply(bdd$temp, INDEX = bdd$day, FUN = max)
tempDayMax plot(x = as.Date(names(tempDayMean), format = "%Y-%m-%d"),
y = tempDayMean, type = 'l', ylim = c(-15, 40),
xlab = "Fecha", ylab = "Temperatura (°C)")
points(as.Date(names(tempDayMin), format = "%Y-%m-%d"),
y = tempDayMin, type = 'l', col = 4)
points(as.Date(names(tempDayMax), format = "%Y-%m-%d"),
y = tempDayMax, type = 'l', col = 2)
legend("topright", legend = c("min", "max", "promedio"),
lty = 1, lwd = 2, col = c(4, 2, 1))
Podemos representar la misma información por semana. Para esto vamos a usar la información de los datos en el formato POSIXct
para transformala en semanas.
<- format(bdd$posix, format = "%Y-%W")
anoSem head(anoSem)
## [1] "2015-45" "2015-45" "2015-45" "2015-45" "2015-45" "2015-45"
Y despues hacer el gráfico.
<- tapply(bdd$temp,
tempWeekMean INDEX = format(bdd$posix, format = "%Y-%W-1"), FUN = mean)
<- tapply(bdd$temp,
tempWeekMin INDEX = format(bdd$posix, format = "%Y-%W-1"), FUN = min)
<- tapply(bdd$temp,
tempWeekMax INDEX = format(bdd$posix, format = "%Y-%W-1"), FUN = max)
plot(x = as.Date(names(tempWeekMean), format = "%Y-%W-%u"),
y = tempWeekMean, type = 'l', ylim = c(-15, 40),
xlab = "Fecha", ylab = "Temperatura (°C)")
points(x = as.Date(names(tempWeekMin), format = "%Y-%W-%u"),
y = tempWeekMin, type = 'l', col = 4)
points(x = as.Date(names(tempWeekMax), format = "%Y-%W-%u"),
y = tempWeekMax, type = 'l', col = 2)
legend("topright", legend = c("min", "max", "promedio"),
lty = 1, lwd = 2, col = c(4, 2, 1))
Para no perder la información sobre la variabilidad de la temperatura podemos hacer boxplot
en lugar de plot
.
boxplot(bdd$temp ~ format(bdd$posix, format = "%Y-%m"), las = 3)
Podemos elegir colores para representar la temperatura promedia. Para esto podemos normalizar la temperatura en numeros integrados entre 1 y 101 y hacer corresponder los numeros en una escala de color del azul al rojo.
<- tapply(bdd$temp,
tempMonthMean INDEX = format(bdd$posix, format = "%Y-%m"), FUN = mean)
<- colorRampPalette(c("blue", "red"))(101)
myCol <- round(
tempMeanDayPos - min(tempMonthMean)) /
(tempMonthMean max(tempMonthMean) - min(tempMonthMean))*100) + 1
(boxplot(bdd$temp ~ format(bdd$posix, format = "%Y-%m"), las = 3,
col = myCol[tempMeanDayPos])
Para los que usan ggplot2:
<- function(x){
pkgCheck if (!require(x, character.only = TRUE)){
install.packages(x, dependencies = TRUE)
if(!require(x, character.only = TRUE)) {
stop()
}
}
}pkgCheck("ggplot2")
<- tapply(bdd$temp,
tempMonthMean INDEX = format(bdd$posix, format = "%Y-%m"), FUN = mean)
<- colorRampPalette(c("blue", "red"))(101)
myCol <- round(
tempMeanDayPos - min(tempMonthMean)) /
(tempMonthMean max(tempMonthMean) - min(tempMonthMean))*100) + 1
(<- ggplot(data = bdd,
p01 aes(
x = posix,
y = temp,
group = format(bdd$posix, format = "%Y-%m"))) +
geom_boxplot(outlier.colour = "black", fill = myCol[tempMeanDayPos])
p01
## Warning: Use of `bdd$posix` is discouraged. Use `posix` instead.
## Warning: Removed 4 rows containing missing values (stat_boxplot).
También podemos calcular la diferencia entre la temperatura máxima y la temperatura mínima (variación de temperatura diurna).
<- tempDayMax - tempDayMin
tempDayTR plot(x = as.Date(names(tempDayMean), format = "%Y-%m-%d"),
y = tempDayTR, type = 'l', ylim = c(5, 45),
xlab = "Fecha", ylab = "Variación de temperatura diurna (°C)")
Otra posibilidad es de agrupar los datos para tener la temperatura promedia de un día con la función aggregate()
(como alternativa a la función tapply
).
<- aggregate(x = bdd$temp,
tempHourMean by = list(format(bdd$posix, format = "%H:%M")), FUN = mean)
<- aggregate(x = bdd$temp,
tempHourMin by = list(format(bdd$posix, format = "%H:%M")), FUN = min)
<- aggregate(x = bdd$temp,
tempHourMax by = list(format(bdd$posix, format = "%H:%M")), FUN = max)
<- seq(from = 0, to = 23.5, by = 0.5)
hours plot(x = hours,
y = tempHourMean[, 2], type = 'l', ylim = c(-15, 40),
xlab = "", ylab = "Temperatura (°C)", lwd = 2,
xaxt = "n", panel.first = {
abline(v = hours, col = "gray", lty = 2)
abline(h = 0, lty = 2)
})axis(side = 1, at = hours, labels = tempHourMean[, 1], las = 2)
points(x = hours, y = tempHourMin[, 2], type = 'l', col = 4, lwd = 2)
points(x = hours, y = tempHourMax[, 2], type = 'l', col = 2, lwd = 2)
Tambien podemos calcular las temperaturas de los dias para cada mes.
<- c("Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
meses "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre")
<- seq(from = 0, to = 23.5, by = 0.5)
hours <- format(bdd$day, format = "%m")
bddMonth <- lapply(sort(unique(bddMonth)), function(myMonth){
tempDayEachMonth <- bdd[bddMonth == myMonth, ]
bddX <- aggregate(x = bddX$temp, by = list(format(bddX$posix, format = "%H:%M")), FUN = mean)
tempHourMean <- aggregate(x = bddX$temp, by = list(format(bddX$posix, format = "%H:%M")), FUN = min)
tempHourMin <- aggregate(x = bddX$temp, by = list(format(bddX$posix, format = "%H:%M")), FUN = max)
tempHourMax return(data.frame(tempHourMean, tempHourMin, tempHourMax))
})
# for (i in seq_along(tempDayEachMonth)){ # para todos los meses
for (i in 1:2){ # solo para Enero y Febrero
plot(x = hours, y = tempDayEachMonth[[i]][, 2],
type = 'l', ylim = c(-15, 40),
xlab = "", ylab = "Temperatura (°C)", lwd = 2,
main = meses[i],
xaxt = "n", panel.first = {
abline(v = hours, col = "gray", lty = 2)
abline(h = 0, lty = 2)
})axis(side = 1, at = hours, labels = tempHourMean[, 1], las = 2)
points(x = hours, y = tempDayEachMonth[[i]][, 4],
type = 'l', col = 4, lwd = 2)
points(x = hours, y = tempDayEachMonth[[i]][, 6],
type = 'l', col = 2, lwd = 2)
}
O todo en un mismo grafico, y la variación de temperatura diurna para cada mes.
plot(x = hours, y = tempDayEachMonth[[1]][, 2], type = 'n', ylim = c(-10, 35),
xlab = "", ylab = "Temperatura promedia (°C)",
xaxt = "n",
panel.first = {
abline(v = hours, col = "gray", lty = 2)
abline(h = 0, lty = 2)
})axis(side = 1, at = hours, labels = tempHourMean[, 1], las = 2)
<- c("#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99",
myColors "#E31A1C", "#FDBF6F", "#FF7F00", "#CAB2D6", "#6A3D9A", "#FFFF99",
"#B15928")
for (i in seq_along(tempDayEachMonth)){
points(x = hours,
y = tempDayEachMonth[[i]][, 2],
type = 'l', col = myColors[i], lwd = 2)
}legend("topright", ncol = 4, legend = meses, col = myColors,
lty = 1, lwd = 2, cex = 0.8)
plot(x = hours, y = tempDayEachMonth[[1]][, 2], type = 'n', ylim = c(0, 30),
xlab = "", ylab = "Variación de temperatura diurna (°C)",
xaxt = "n",
panel.first = {
abline(v = hours, col = "gray", lty = 2)
abline(h = 0, lty = 2)
})axis(side = 1, at = hours, labels = tempHourMean[, 1], las = 2)
<- c("#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99",
myColors "#E31A1C", "#FDBF6F", "#FF7F00", "#CAB2D6", "#6A3D9A", "#FFFF99",
"#B15928")
for (i in seq_along(tempDayEachMonth)){
points(x = hours,
y = tempDayEachMonth[[i]][, 6] - tempDayEachMonth[[i]][, 4],
type = 'l', col = myColors[i], lwd = 2)
}legend("topright", ncol = 4, legend = meses, col = myColors,
lty = 1, lwd = 2, cex = 0.8)
También podemos representar las temperaturas diarias con gráficos “ridgeline” y el package ggplot2
(https://www.data-to-viz.com/graph/ridgeline.html).
<- function(x){
pkgCheck if (!require(x, character.only = TRUE)){
install.packages(x, dependencies = TRUE)
if(!require(x, character.only = TRUE)) {
stop()
}
}
}pkgCheck("ggplot2")
pkgCheck("ggridges")
## Le chargement a nécessité le package : ggridges
pkgCheck("viridis")
## Le chargement a nécessité le package : viridis
## Le chargement a nécessité le package : viridisLite
<- unlist(lapply(tempDayEachMonth, "[[", 2))
meanTemps <- rep(meses, each = 48)
labelMonth <- data.frame(month = labelMonth, value = meanTemps,
dfTemps stringsAsFactors = FALSE)
$month <- factor(dfTemps$month, levels = rev(meses))
dfTemps<- ggplot(data = dfTemps, aes(y = month, x = value, fill = ..x..))
p <- p + geom_density_ridges_gradient(stat="binline")
p <- p + scale_fill_viridis(name = "Temp. [°C]", option = "C")
p <- p + xlab("Temperature") + ylab("") +
p theme(
legend.position="none",
panel.spacing = unit(0.1, "lines"),
strip.text.x = element_text(size = 8)
) p
## `stat_binline()` using `bins = 30`. Pick better value with `binwidth`.