Maestría en Hidrología
Universidad de Cuenca
http://www.ucuenca.edu.ec/maestria-ecohidrologia/
Johanna Orellana-Alvear (MSc, PhD candidate)
johanna.orellana@ucuenca.edu.ec
Curso completo en: http://rpubs.com/Johanna_Orellana_Alvear/MHidro_indice_2018
En esta lección aprenderás a:
- Utilizar estructuras condicionales (if-else)
- Utilizar estructuras repetitivas (for, while y repeat)
- Crear gráficos sencillos
- Guardar gráficos generados en R
- Manejar datos NA y NaN
A- Estructuras Condicionales
B- Estructuras Repetitivas
C- FOR
D- WHILE
E- REPEAT
F- Gráficos
Las estructuras condicionales permiten hacer una bifurcación en la secuencia de ejecución de un script (programa) R.
setwd("~/Documents/R_WORKSPACE/Maestria_Hidrologia_2018")
df.observaciones <- read.table("observaciones_2003.csv", header = TRUE, stringsAsFactors = FALSE)
obs <- df.observaciones[1,]
if (obs[1,"VALOR"] > 10){
print('Valido')
}else{
print('No valido')
}
## [1] "No valido"
Existe también un comando corto para el uso de sentencias de control con la siguiente sintaxis: ifelse(condición, comando1, comando2). Reescriba el ejemplo anterior usando este comando corto. Es posible también que una sentencia if no tenga una contraparte else. En este caso se usa el if para ejecutar sentencias adicionales en un caso en particular.
for (value in c("My", "first", "for", "loop")) {
print("one run")
}
## [1] "one run"
## [1] "one run"
## [1] "one run"
## [1] "one run"
chars<- c()
words <- c("My", "fourth", "for", "loop")
for(i in 1:4){
chars[i] <- words[i]
}
El objetivo es obtener un vector con la media calculada del campo VALOR de cada una de las estaciones del data frame df.observaciones. Para ello utilizaremos el comando unique que permite obtener el listado de estaciones y crearemos un vector vec.means para almacenar las medias
Abordaremos tres formas distintas de utilizar bucles de ejecución: for,while y repeat.
vec.means <- c()
cont <- 0
lst.estaciones <- unique(df.observaciones$ESTACION)
for (estacion in lst.estaciones){
mean.estacion <- mean(df.observaciones[df.observaciones$ESTACION==estacion,"VALOR"])
cont <- cont+1
vec.means[cont] <- mean.estacion
}
Pero…. guardar los valores de las medias en un vector independiente no sería lo mejor si necesitamos asociar cada media a su respectiva estación. Vamos a modificar el código para generar al final un data frame que contenga dos columnas ESTACION y MEAN.VALOR.
vec.means <- c()
cont <- 0
lst.estaciones <- unique(df.observaciones$ESTACION)
for (estacion in lst.estaciones){
mean.estacion <- mean(df.observaciones[df.observaciones$ESTACION==estacion,"VALOR"])
cont <- cont+1
vec.means[cont] <- mean.estacion
}
estadisticas_estaciones <- data.frame(ESTACION=lst.estaciones,MEAN.VALOR=vec.means)
head(estadisticas_estaciones)
## ESTACION MEAN.VALOR
## 1 M012 NA
## 2 M032 NA
## 3 M040 1.347945
## 4 M142 NA
## 5 M179 NA
## 6 M180 NA
Existe un problema al calcular las medias. En tanto que el data frame df.observaciones contiene valores NA, es necesario indicarle a R que debe omitir o eliminar estos valores en el cálculo. Para esto utilizaremos el parámetro rm.na=TRUE que le indica a R que debe eliminar los valores NA antes de efectuar el cálculo. (Véase también donde utilizar la variante na.omit).
vec.means <- c()
cont <- 0
lst.estaciones <- unique(df.observaciones$ESTACION)
for (estacion in lst.estaciones){
mean.estacion <- mean(df.observaciones[df.observaciones$ESTACION==estacion,"VALOR"],na.rm = TRUE)
cont <- cont+1
vec.means[cont] <- mean.estacion
}
estadisticas_estaciones <- data.frame(ESTACION=lst.estaciones,MEAN.VALOR=vec.means)
head(estadisticas_estaciones)
## ESTACION MEAN.VALOR
## 1 M012 0.6369318
## 2 M032 NaN
## 3 M040 1.3479452
## 4 M142 1.7903134
## 5 M179 NaN
## 6 M180 2.7328488
Los valores NA se han eliminado del cálculo, sin embargo no se han eliminado del data frame original. Por ejemplo si recuperamos los valores de la última estación, cuyo nombre se encuentra almacenado en la variable estacion:
df.observaciones[df.observaciones$ESTACION==estacion,"VALOR"]
## [1] 0.0 0.1 0.0 0.2 5.1 0.0 4.0 3.1 0.0 0.0 0.0 4.2 4.1 7.7
## [15] 2.2 40.0 9.7 8.3 5.9 2.1 4.5 0.2 0.0 0.0 10.4 0.0 1.0 3.3
## [29] 2.4 20.1 10.0 0.0 24.3 2.4 0.7 3.2 5.2 10.1 8.4 8.4 15.0 1.3
## [43] 14.3 0.2 4.1 5.1 0.0 5.2 6.4 0.2 3.3 0.0 0.2 0.0 0.0 6.4
## [57] 19.9 22.2 2.0 0.1 0.4 0.2 0.3 0.1 0.0 0.0 0.0 0.0 0.2 0.0
## [71] 0.5 22.0 39.3 14.0 0.0 7.0 0.6 0.0 0.0 0.0 38.4 13.2 1.2 0.0
## [85] 0.0 71.2 0.4 3.2 8.5 0.0 0.1 0.3 0.0 0.0 0.0 0.0 0.2 23.2
## [99] 20.1 2.9 14.5 0.0 0.0 0.0 3.6 3.0 3.3 2.2 0.0 0.0 0.0 72.3
## [113] 1.1 0.0 4.3 0.2 0.0 3.0 0.0 0.0 0.0 0.0 0.0 5.2 4.9 1.1
## [127] 0.5 0.0 0.0 2.3 0.2 4.9 0.3 1.0 0.0 0.6 0.6 7.7 0.0 0.0
## [141] 2.1 0.0 0.0 0.0 5.3 0.0 0.2 0.1 0.0 0.3 2.1 1.5 0.0 0.1
## [155] 0.2 0.0 0.1 1.2 0.0 0.0 0.5 1.6 0.4 0.0 1.5 0.5 0.6 0.0
## [169] 3.2 2.1 2.7 0.1 1.7 0.0 0.1 0.2 0.0 0.1 0.0 0.0 2.3 1.1
## [183] 0.4 0.0 0.0 0.0 2.1 3.3 0.0 4.3 4.2 0.0 0.0 0.0 0.0 0.0
## [197] 0.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
## [211] 0.0 0.0 0.0 0.0 0.0 0.0 0.6 0.0 2.5 2.4 0.0 0.0 0.0 0.0
## [225] 0.1 0.0 1.2 0.4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
## [239] 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
## [253] 1.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.2
## [267] 0.0 0.0 0.0 0.2 0.0 0.0 0.0 0.0 0.0 0.2 0.2 0.0 0.0 1.4
## [281] 0.3 0.0 0.0 0.0 0.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.2 0.0
## [295] 0.0 1.3 1.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.1
## [309] 0.0 0.2 0.1 0.0 0.0 0.0 0.0 0.2 5.5 3.8 0.3 0.0 1.1 0.3
## [323] 0.2 1.5 0.0 0.2 0.0 0.1 0.0 4.0 4.5 12.3 10.3 0.0 0.0 1.4
## [337] 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5.2 0.0 10.2 11.4 8.4 0.2
## [351] 0.0 0.0 0.0 0.6 0.0 3.2 3.0 15.3 10.3 12.6 11.1 3.3 1.2 NA
## [365] NA
Sin embargo, nótese que al efectuar el cálculo de la media, algunos valores son NaN, esto significa que la ejecución de la función mean produjo un valor que no es un número. En este caso todos los elementos del vector de datos correspondientes al campo "VALOR" de la estación "M032" contienen el valor de NA, por lo cual la función mean produce como resultado NaN Not A Number.
df.observaciones[df.observaciones$ESTACION=="M032","VALOR"]
## [1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [24] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [47] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [70] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [93] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [116] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [139] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [162] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [185] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [208] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [231] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [254] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [277] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [300] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [323] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [346] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Finalmente, las observaciones que no contienen un valor “real” (un número), deberían quedar fuera de nuestro análisis, por ello vamos a conservar únicamente los casos completos. Es decir, las observaciones cuyos campos (variables) tengan valores que sean utilizables. Para ello:
vec.means <- c()
cont <- 0
lst.estaciones <- unique(df.observaciones$ESTACION)
for (estacion in lst.estaciones){
mean.estacion <- mean(df.observaciones[df.observaciones$ESTACION==estacion,"VALOR"],na.rm = TRUE)
cont <- cont+1
vec.means[cont] <- mean.estacion
}
estadisticas_estaciones <- data.frame(ESTACION=lst.estaciones,MEAN.VALOR=vec.means)
estadisticas_estaciones<-estadisticas_estaciones[complete.cases(estadisticas_estaciones), ]
head(estadisticas_estaciones)
## ESTACION MEAN.VALOR
## 1 M012 0.6369318
## 3 M040 1.3479452
## 4 M142 1.7903134
## 6 M180 2.7328488
## 7 M185 0.7912698
## 8 M232 2.3148760
A diferencia de la estructura for, al usar el bucle while no existe una asignación directa de cada uno de los elementos de la lista a una variable (e.g la variable estacion). Por lo tanto es necesario referenciar el contenido de cada elemento de la lista directamente en lugar de esta variable. Esto s ilustra en la línea 6 del ejemplo siguiente df.observaciones$ESTACION==lst.estaciones[cont].
vec.means <- c()
cont <- 0
lst.estaciones <- unique(df.observaciones$ESTACION)
while (cont < length(lst.estaciones) ) {
cont <- cont+1
mean.estacion <- mean(df.observaciones[df.observaciones$ESTACION==lst.estaciones[cont],"VALOR"],na.rm = TRUE)
vec.means[cont] <- mean.estacion
}
estadisticas_estaciones <- data.frame(ESTACION=lst.estaciones,MEAN.VALOR=vec.means)
estadisticas_estaciones<-estadisticas_estaciones[complete.cases(estadisticas_estaciones), ]
head(estadisticas_estaciones)
## ESTACION MEAN.VALOR
## 1 M012 0.6369318
## 3 M040 1.3479452
## 4 M142 1.7903134
## 6 M180 2.7328488
## 7 M185 0.7912698
## 8 M232 2.3148760
La condición de salida de la estructura repeat, a diferencia de la estructura while, se especifica al final del bucle a través de la sentencia if(condicion){break}.
vec.means <- c()
cont <- 0
lst.estaciones <- unique(df.observaciones$ESTACION)
repeat {
cont <- cont+1
mean.estacion <- mean(df.observaciones[df.observaciones$ESTACION==lst.estaciones[cont],"VALOR"],na.rm = TRUE)
vec.means[cont] <- mean.estacion
if(cont == length(lst.estaciones)){
break
}
}
estadisticas_estaciones <- data.frame(ESTACION=lst.estaciones,MEAN.VALOR=vec.means)
estadisticas_estaciones<-estadisticas_estaciones[complete.cases(estadisticas_estaciones), ]
head(estadisticas_estaciones)
## ESTACION MEAN.VALOR
## 1 M012 0.6369318
## 3 M040 1.3479452
## 4 M142 1.7903134
## 6 M180 2.7328488
## 7 M185 0.7912698
## 8 M232 2.3148760
Ahora modifica este script para que, además de la media de cada estación, el data frame estadisticas_estaciones contenga los campos(columnas) MAX.VALOR y MIN.VALOR.
## ESTACION MEAN.VALOR MIN.VALOR MAX.VALOR
## 1 M012 0.6369318 0 31.0
## 3 M040 1.3479452 0 41.1
## 4 M142 1.7903134 0 27.0
## 6 M180 2.7328488 0 72.0
## 7 M185 0.7912698 0 10.2
## 8 M232 2.3148760 0 91.6
Para la sección de gráficos utilizaremos principalmente los data frames df.observaciones y estadisticas_estaciones. No es posible detallar todas las posibilidades a nivel de gráficos para cada caso en particular ya que R posee una amplia variedad de parámetros que permiten “prsonalizar” los gráficos de acuerdo a las necesidades.
A diferencia de las asignaciones en variables, los gráficos en esta sección no son almacenados en variables sino por el contrario se visualizan únicamente en pantalla o es posible guardarlos en un archivo.
Para este primer ejercicio sobre gráficos, haremos uso de la ayuda de R para identificar que hace el comando rnorm, y ahora crearemos un data frame en base al resultado que produce este comando. Ejecute una a una las líneas que corresponden al comando plot para identificar las variaciones. ¿Cuáles son las diferencia entre la ejecución del comando plot en cada caso y su resultado?
dat <- data.frame(xvar = 1:20 + rnorm(20,sd=3),
yvar = 1:20 + rnorm(20,sd=3),
zvar = 1:20 + rnorm(20,sd=3))
head(dat)
## xvar yvar zvar
## 1 -0.1694489 4.3293844 -1.486349
## 2 3.5032497 7.2930571 -4.118473
## 3 4.1625325 -0.8950064 1.544071
## 4 2.7949122 6.0523365 3.768651
## 5 2.5063692 5.1209724 2.347123
## 6 9.2979574 5.6429349 5.706017
plot(dat$zvar)
plot(dat$zvar, type="o", col="blue")
plot(dat$xvar, dat$yvar)
plot(yvar ~ xvar, dat)
fitline <- lm(dat$yvar ~ dat$xvar)
abline(fitline) #linea de regresion
Para añadir la línea de regresión, hemos utilizado el comando abline. Consulte a más detalle que hace el comando lm. Ahora, es posible borrar (limpiar) la pantalla de gráficos mediante el comando dev.off(dev.list()["RStudioGD"]).
Este ha sido un ejemplo que nos ha permitido usar el comando help y elaborar un gráfico rápido. Ahora iremos a la ayuda del comando plot para ver cuales son los posibles argumentos y parámetros que se pueden “pasar” al comando plot y utilizarlo con los datos de los data frames df.observaciones y estadisticas_estaciones.
Los gráficos previos aún están incompletos, por ejemplo: requieren título, modificación de las etiquetas de los ejes. Primero modificaremos las etiquetas de los ejes y luego incorporaremos el título.
head(estadisticas_estaciones)
## ESTACION MEAN.VALOR MIN.VALOR MAX.VALOR
## 1 M012 0.6369318 0 31.0
## 3 M040 1.3479452 0 41.1
## 4 M142 1.7903134 0 27.0
## 6 M180 2.7328488 0 72.0
## 7 M185 0.7912698 0 10.2
## 8 M232 2.3148760 0 91.6
plot(estadisticas_estaciones$ESTACION, estadisticas_estaciones$MAX.VALOR, xlab='Estaciones',ylab='Máximo')
Hemos modificado los ejes, ahora intentemos incorporar el título: plot(estadisticas_estaciones$MIN.VALOR ~ estadisticas_estaciones$ESTACION, xlab='Estaciones',ylab='Mínimo', title='Valores mínimos por estación') ¿Cuál es el error ?
El parámetro title no se puede incorporar en la función plot, por lo tanto debemos crearlo como un objeto aparte que se va a sobreponer al gráfico actual.
plot(estadisticas_estaciones$ESTACION, estadisticas_estaciones$MAX.VALOR, xlab='Estaciones',ylab='Maximo')
title('Valores maximos por estacion')
Así también es posible “sobreponer” otros componentes como líneas, textos, un recuadro para la gráfica, etc. Nótese que el comando las=2 permite colocar las etiquetas del eje x con orientación vertical.
plot(estadisticas_estaciones$ESTACION, estadisticas_estaciones$MAX.VALOR, xlab='Estaciones',ylab='Max',las=2)
title('Valores maximos por estacion')
lines(estadisticas_estaciones$ESTACION, estadisticas_estaciones$MAX.VALOR,type="o", col="red")
text(14,80,'Estadistico= xx.xx')
Nota: Averiguar como se puede colocar como texto una función o expresión matemática.
Si tenemos un conjunto de datos, del cual se desea obtener un histograma para identificar su distribución, basta utilizar la función hist(x).
hist(estadisticas_estaciones$MEAN.VALOR)
hist(estadisticas_estaciones$MEAN.VALOR, breaks=8, col="#CCCCFF", freq=FALSE)
boxplot(df.observaciones$VALOR ~ df.observaciones$ESTACION)
Para excluir del gráfico los outliers:
boxplot(df.observaciones$VALOR ~ df.observaciones$ESTACION,las=2,outline=FALSE)
Para generar un gráfico de barras para una determinada clasificación (en este caso la clasificación corresponde al listado de estaciones), se requiere obtener la frecuencia de eventos, valores, observaciones mediante la función table.
counts <- table(df.observaciones$ESTACION)
barplot(counts, main="Distribución de Estaciones", xlab="Estaciones",las=2)
Para visualizar un gráfico que contabilice únicamente las observaciones que contienen datos completos:
df.obs.completas <- df.observaciones[complete.cases(df.observaciones),]
counts <- table(df.obs.completas$ESTACION)
barplot(counts, main="Distribucion de Estaciones", xlab="Estaciones",las=2)
png("plot_barras.png", height=3, width=6, units="in", res=400)
boxplot(df.observaciones$VALOR ~ df.observaciones$ESTACION,las=2,outline=FALSE)
dev.off()
## quartz_off_screen
## 2