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:
- Crear funciones sin argumentos
- Crear funciones con argumentos
- Crear fechas a partir de una cadena de caracteres
- Interfaz con S.O (listado de archivos y hora del sistema)
- Manipulación de fechas
A- Creación de Funciones
B- Componentes de una función
C- Funciones de S.O
D- Fechas
Hemos utilizado en sesiones previas algunas funciones como mean, min o max. Por ejemplo la función round sirve para redondear un número, la función factorial para calcular el factorial, etc. En general se hace uso de una función escribiendo (invocando) el nombre de la función y entre paréntesis () el dato o datos sobre los cuales se desea aplicar la función. Ejm:
round(3.1415)
## [1] 3
factorial(3)
## [1] 6
El valor o valores que se “pasan” dentro de la función se denomina argumento de la función. El argumento puede ser cualquier objeto R o incluso el resultado de otra función R. En el último caso la evaluación de las funciones se hace secuencialmente desde la más interna hasta la llamada más externa. Así en el ejemplo siguiente, R evalúa inicialmente la creación del vector c(1,2,3,4), luego evalúa la media a través de mean(c(1,2,3,4)) y finalmente redondea el resultado que se ha obtenido de esta última operación.
round(mean(c(1,2,3,4)))
## [1] 2
Una función puede recibir tantos argumentos como se desee, siempre y cuando estos se encuentren separados con una coma ,. Normalmente no se conoce a priori cuales son los argumentos que acepta una función en particular, sin embargo es posible obtener esta información a través del comando args(), donde el nombre de la función de interés se coloca entre paréntesis, así: args(min). Nótese, que no todos los argumentos se visualizan, pero sí los más frecuentes. En tanto que para las funciones básicas como args(round) o args(rnorm) presentan todos sus argumentos.
args(round)
## function (x, digits = 0)
## NULL
args(rnorm)
## function (n, mean = 0, sd = 1)
## NULL
args(min)
## function (..., na.rm = FALSE)
## NULL
En el caso de la función de redondeo round(3.5), el argumento digits no se ha “seteado”, es decir no se ha incluido en la llamada de la función. En este caso, R mantiene el valor por defecto que se incluye en la función, es decir redondeo a 0 dígitos decimales. A estos argumentos se les conoce como opcionales ya que tienen un valor predefinido.
Importante: es siempre recomendable incluir en la llamada de la función,el nombre del argumento por dos razones principales. Primero, a medida que se incrementa el número de argumentos es más difícil recordar (o interpretar en el código de alguien más) a que hace referencia cada valor. Segundo, el orden en el que colocan los argumentos es definido y R interpretará los valores asignados al argumento en dicho orden. ¿Cuál de los dos ejemplos es más fácil de interpretar?
rnorm(5, 3, 1)
## [1] 2.727029 3.903482 1.595341 3.574659 2.682911
rnorm(5, mean = 3, sd = 1)
## [1] 3.797598 3.544420 3.598480 3.941327 3.765731
¿Para qué crear una función?
Primero, vamos a crear una función para simular el proceso de lanzar dos dados y obtener el resultado de la suma resultante. Vamos a guardar la codificación de esta función en un archivo llamado funcion_roll.R.
roll <- function() {
die <- 1:6
dice <- sample(die, size = 2, replace = TRUE)
sum(dice)
}
roll()
## [1] 5
roll
## function() {
## die <- 1:6
## dice <- sample(die, size = 2, replace = TRUE)
## sum(dice)
## }
Observe que la función definida roll no tiene argumentos de entrada, ahora modificaremos el código para incluir un argumento con valor por “default” que simula el número de caras del dado; a esta función la llamaremos roll2. Se omite la línea die <- 1:6, pero ahora el número de caras del dado está dado por el argumento bones.
roll2 <- function(bones = 1:6) {
dice <- sample(bones, size = 2, replace = TRUE)
sum(dice)
}
roll2()
## [1] 6
roll2
## function(bones = 1:6) {
## dice <- sample(bones, size = 2, replace = TRUE)
## sum(dice)
## }
Generalizando la estructura para la creación de una función (ilustración tomada del libro “Hands on Programming with R”).
Esquema de una función
Ya que sabemos como definir una función, vamos a crear nuestra propia función. Considerando que la función scan(n=1) se utiliza para leer un (1) valor por teclado, vamos a escribir el código en R para una función que, ingresado un valor por teclado, indique al usuario a través de un mensaje si este número es mayor, menor o igual a 0.
valor <- scan(n=1)
En los ejemplos ilustrados hasta ahora hemos creado la función y la hemos utilizado en un mismo script (programa) R. Sin embargo, es posible que las funciones creadas por un usuario se encuentren en distintos archivos. En este caso, al igual que la invocación de una librería library(rgdal), es necesario cargar en memoria de R el código de creación y ejecución de estas funciones. Para ello, basta con usar el comando source("nombre_archivo_funcion.R"). Según el ejemplo anterior de la función roll(), la llamada se efectúa como source("funcion_roll.R").
Las funciones más frecuentes de sistema operativo consisten en obtener la fecha-hora del sistema y la manipulación de directorios.
Para contabilizar el tiempo de ejecución de un script (programa) R, es posible tomar la hora del reloj al momento de inicio del programa y al final de dicha tarea calcular la diferencia con la hora de fin del proceso, así:
start_task <- Sys.time ()
end_task <- Sys.time () - start_task
end_task
## Time difference of 0.001222134 secs
Sobre la manipulación de archivos y directorios: consideremos que en una carpeta del sistema se tiene almacenado archivos de distinta extensión (.jpg, .pdf, .csv, .nc, etc) y se desea recuperar únicamente el conjunto de archivos de un tipo específico. Utilizaremos la función list.files (Véase los posibles argumentos de esta función y su efecto: args(list.files))
files.csv <- list.files(path = ".",pattern = ".csv")
files.R <- list.files(path = ".",pattern = ".R")
Esta función es muy útil a nivel de implementación. Imaginemos que tenemos una carpeta con todos los archivos mensuales de descarga de una estación de precipitación donde cada uno de ellos corresponde a enero, febrero, marzo, etc. Podríamos efectuar el mismo proceso sobre cada archivo a través de un bucle (estructura repetitiva) utilizando el comando read.table(archivo) donde la variable archivo tomaría el valor de cada uno de los elementos de la lista de los archivos.
Por otra parte, es práctica común que el nombre del archivo como tal encierre (represente) algún tipo de información sobre los datos contenidos. Por ejemplo el mes al que corresponde, el nombre de la estación, etc. Entonces, a partir del nombre del archivo podríamos “recortar” los fragmentos de información útiles.
Actividad Individual: Asuma que una carpeta contiene los siguientes archivos:
Vamos a hacer uso del comando “substring” para extraer la información del nombre del archivo (de la variable tipo character), así:
archivo <- "2005_12_01_1_jp.txt"
anio <- substr(archivo,start = 1,stop = 4)
anio
## [1] "2005"
Obtener un data frame que tenga la siguiente estructura
## anio mes tipo responsable
## 1 2005 12 1 jp
## 2 2005 11 1 jp
## 3 2005 10 1 jp
## 4 2006 12 1 jp
## 5 2006 11 1 jp
## 6 2006 10 1 jp
Y luego vamos a modificar el campo “tipo”" para expresarlo como un texto. El data frame final debe visualizarse así:
## anio mes tipo responsable
## 1 2005 12 Precipitacion jp
## 2 2005 11 Precipitacion jp
## 3 2005 10 Precipitacion jp
## 4 2006 12 Precipitacion jp
## 5 2006 11 Precipitacion jp
## 6 2006 10 Precipitacion jp
Normalmente, al importar un archivo .txt o .csv a través de la función read.table el campo asociado a la fecha se almacena en el data frame resultante como un texto (caracter). Sin embargo es conveniente tener este campo en un formato fecha, de tal manera que permita recuperar fácilmente el año, mes, diferencia entre fechas, etc.
setwd("~/Documents/R_WORKSPACE/Maestria_Hidrologia_2018")
df.observaciones <- read.table("observaciones_2003.csv", header = TRUE, stringsAsFactors = FALSE)
Para transformar un character a un objeto tipo fecha o viceversa.
fechas <- strptime(df.observaciones$FECHA, format="%d/%m/%Y")
df.observaciones$FECHA <- as.POSIXct(fechas)
Para obtener la diferencia entre dos fechas se utiliza la función difftime.
time_series_duration <- difftime(df.observaciones$FECHA[length(df.observaciones$FECHA)], df.observaciones$FECHA[1], units="mins")
time_series_duration
## Time difference of 524160 mins
time_series_duration <- difftime(df.observaciones$FECHA[length(df.observaciones$FECHA)], df.observaciones$FECHA[1], units="hours")
time_series_duration
## Time difference of 8736 hours
Evaluemos la ventaja de tener el campo “FECHA” para extraer el año y el mes de la observación y que posteriormente agregamos a nuestro data frame. En este ejemplo, el archivo original ya incluía el campo month y year, pero esto no es lo común.
df_events_month <- as.numeric(format(df.observaciones$FECHA,"%m"))
df_events_year <- as.numeric(format(df.observaciones$FECHA,"%Y"))
df.observaciones = cbind(df.observaciones,df_events_month)
df.observaciones = cbind(df.observaciones,df_events_year)
¿Cómo funciona el comando colnames?
colnames(df.observaciones)
## [1] "FECHA" "VALOR" "STATUS" "ESTACION"
## [5] "year" "day" "month" "df_events_month"
## [9] "df_events_year"
Para modificar únicamente ciertos nombres de columnas (variables) del data frame:
colnames(df.observaciones)[8] <- "month_extract"
colnames(df.observaciones)[9] <- "year_extract"
head(df.observaciones)
## FECHA VALOR STATUS ESTACION year day month month_extract
## 7671 2003-01-01 0 M012 2003 1 1 1
## 7672 2003-01-02 0 M012 2003 2 1 1
## 7673 2003-01-03 0 M012 2003 3 1 1
## 7674 2003-01-04 0 M012 2003 4 1 1
## 7675 2003-01-05 0 M012 2003 5 1 1
## 7676 2003-01-06 0 M012 2003 6 1 1
## year_extract
## 7671 2003
## 7672 2003
## 7673 2003
## 7674 2003
## 7675 2003
## 7676 2003