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

Temario

A- Estructuras Condicionales
B- Estructuras Repetitivas
C- FOR
D- WHILE
E- REPEAT
F- Gráficos

Estructuras Condicionales

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.

Estructuras Repetitivas

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.

FOR

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

WHILE

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

REPEAT

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

Gráficos

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?

Scatterplot

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.

Histogram

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

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)

Barplot

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)

Grabar un gráfico

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