Una de las maneras de incrementar nuestro valor como analistas de datos, es entender la potencia y flexibilidad que ofrece crear funciones personalizadas.
Las funciones permiten al analista automatizar tareas comunes en una forma más inteligente y conveniente que el copiar y pegar, de tal manera que se cree un proceso genérico que sea aplicable a diversas situaciones en múltiples contextos.
Cuando se debe escribir una función?
Se debe considerar escribir una función cuando el analista se encuentra a si mismo copiando y pegando el mismo codigo más de al menos 3 veces.
Por ejemplo:
#Data frame ficticio de asistencia de aficionados equipos
df <- data.frame(Alajuela = round(runif(10,0,20000)),
Saprissa = round(runif(10,0,20000)),
Cartago = round(runif(10,0,5000)),
Heredia = round(runif(10,0,10000)),
PerezZeledon = round(runif(10,0,8000)),
SanCarlos = round(runif(10,0,6000)),
Grecia = round(runif(10,0,5000)),
UCR = round(runif(10,0,500)),
Carmelita= round(runif(10,0,500)),
Guadalupe = round(runif(10,0,1500)),
Limon = round(runif(10,0,4500))
)
df## Alajuela Saprissa Cartago Heredia PerezZeledon SanCarlos Grecia UCR
## 1 13959 1528 4227 1198 130 1251 2659 152
## 2 5754 10778 386 83 2755 4853 4381 200
## 3 26 11227 4498 5050 2734 5193 4850 397
## 4 13631 5470 3387 7197 4349 2643 2924 375
## 5 1826 3457 2419 2974 6117 2245 2844 68
## 6 12816 16167 4264 5110 7808 3927 4397 472
## 7 8563 167 3070 9398 6797 2852 2707 119
## 8 9381 234 3277 3926 2008 5828 4964 351
## 9 5119 7386 3375 7849 1016 3813 3821 399
## 10 8843 13239 630 1589 997 1815 1829 97
## Carmelita Guadalupe Limon
## 1 41 596 1970
## 2 43 355 2856
## 3 67 1207 2911
## 4 372 366 704
## 5 3 1273 2603
## 6 387 441 2406
## 7 36 1203 4406
## 8 17 866 1447
## 9 343 779 824
## 10 418 1096 1117
#Ahora, queremos obtener la venta promedio de cada equipo,basado en un monto de costo de entrada:
precio<-5000
sum(df$Alajuela)*precio/nrow(df)## [1] 39959000
Una manera más elegante,eficiente y reproducible de lograr este tipo de tareas es mediante la creación de funciones.
El anterior ejemplo se puede escribir mediante la siguiente función:
# Creación de función
montoPromedio <- function(vector,precio){ # dentro de "function se incluyen los argumentos de la función
# Cuerpo de la función
calculo<- sum(vector)*precio/length(vector)
calculop <- paste0("El valor promedio es de ", calculo)
# En return se incluye el resultado deseado que la función debe guardar
# Cuando se tienen varios resultados se pueden guardar en una lista
return(calculop)
}
#Probar la función
MontoAlajuela <- montoPromedio(vector=df$Alajuela,precio=5000)
MontoAlajuela## [1] "El valor promedio es de 39959000"
Veamos un ejemplo más:
Una persona desea sacar un préstamo, de \(P\) colones a una tasa de interés mensual \(i\). El préstamo tiene que ser reembolsado en \(n\) cuotas mensuales de tamaño \(R\), comenzando dentro de un mes. El problema es calcular \(R\). La fórmula \(R\) es:
\[ R = P \cdot \left( \frac{i}{1 - (1 + i)^{-n}} \right) \]
Si suponemos que \(P=150000\), que la tasa de interés es del 2% y que el número de pagos es 10. EL código en R sería:
tasa.interes <- 0.02
n <- 10
principal <- 150000
pago <- principal * tasa.interes/(1 - (1 + tasa.interes)^(-n))
pago## [1] 16698.98
Escribiendolo como una función:
calcula.cuota <- function(tasa.interes, n, principal) {
pago <- principal * tasa.interes/(1 - (1 + tasa.interes)^(-n))
return(pago)
}
calcula.cuota(tasa.interes=0.02, n=10,principal= 150000)## [1] 16698.98
## [1] 250456.5
ifLa clásica sentencia if cumple la función de toma de decisiones con respecto a algún flujo de datos.
La estructura de la función es la siguiente:
Si test_expression es igual a TRUE, el statement se ejecuta.Pero si test_expression es igual a FALSE, nada pasa.Acá,test_expression puede ser lógico o numérico, pero solamente el primer elemento es tomado en consideración:
## [1] "Positive number"
El diagrama de la sentencia if es el siguiente:
Flujograma de la sentencia if
elseEn la función anterior, observamos como no pasaba nada si la operación no se cumplia.
Agragando el componente else, se puede evaluar tanto para el resultado TRUE como el FALSE .La estructura es la siguiente:
El diagrama de la sentencia if else es el siguiente:
Flujograma de la sentencia if…else
## [1] "Negative number"
if … else encadenadoEn muchas ocasiones, tenemos más de dos alternativas, por lo que podemos utilizar la sentencia de manera encadenada:
if ( test_expression1) {
statement1
} else if ( test_expression2) {
statement2
} else if ( test_expression3) {
statement3
} else {
statement4
}Siguiendo el mismo ejemplo:
x <- 0
if (x < 0) {
print("Negative number")
} else if (x > 0) {
print("Positive number")
} else
print("Zero")## [1] "Zero"
Alternativamente se puede utilizar la función ifelse.
if en funcionesSupongamos que en los datos generados de equipos de fútbol, queremos conocer si un equipo tuvo más asistencia que otro.Para esto podemos programar una función:
if ( test_expression1) {
statement1
} else if ( test_expression2) {
statement2
} else if ( test_expression3) {
statement3
} else {
statement4
}Siguiendo el mismo ejemplo:
compara<- function(columna1,columna2,df){
if(sum(df[,columna1])>sum(df[,columna2])){
result<- names(df)[columna1]
}
else if(sum(df[,columna1])<sum(df[,columna2])){
result<- names(df)[columna2]
}
else {
result<- c("Equipos tuvieron misma asistencia")
}
return(result)
}
compara(1,2,df)## [1] "Alajuela"
## [1] "Equipos tuvieron misma asistencia"
## [1] "Heredia"
if con más de un resultadoPodemos programar una función para que tenga como resultado más de un elemento.Por ejemplo, queremos tener una función que calcule la correlación entre dos vectores, pero que a su vez de la opción al usuario de plotear el scatterplot:
cor.plot <- function(x, y, plotear = FALSE) {
if (plotear == TRUE)
plot(x, y)
return(cor(x, y))
}
#Evaluar función
vec1 <- runif(50)
vec2 <- rnorm(50)
#Sin definir el argumento "plotear"
cor.plot(vec1, vec2)## [1] -0.02894911
## [1] -0.02894911
## [1] -0.02894911
Alternativamente, se pueden almacenar todos los elementos de la función como una lista dentro de return :
cor.plot2 <- function(x, y, plotear = FALSE) {
if (plotear == TRUE)
return(list(correlacion=cor(x, y),plot=plot(x, y)
)
)
}
#Evaluar función
vec1 <- runif(50)
vec2 <- rnorm(50)
cor.plot2(vec1, vec2, plotear=TRUE)## $correlacion
## [1] 0.1136328
##
## $plot
## NULL
Otro ejemplo de función con la función if, es generando argumentos en los que el usuario pueda escoger el procedimiento a seguir:
#Crea función con argumento "función" de varias opciones
calcula <- function(x, y, funcion = c("multiplica","suma")) {
if (funcion == "multiplica") {
res<- x*y
return(res)
}
else{ res2<-x+y
res2}
}
#Datos de ejemplo
x<-rnorm(10)
y<-rnorm(10)
x## [1] -1.13105162 0.08503372 -0.24495840 -0.56918002 -1.19912788
## [6] -0.43265940 -0.90348598 0.93566622 0.38334978 -2.28242357
## [1] -0.05392202 0.59520468 -0.38166955 0.13505644 -1.09189305
## [6] 0.83093883 1.60782514 -0.26599566 -1.29306447 0.13336229
## [1] 0.06098859 0.05061247 0.09349316 -0.07687143 1.30931940
## [6] -0.35951350 -1.45264747 -0.24888315 -0.49569598 -0.30438923
## [1] -1.1849736 0.6802384 -0.6266279 -0.4341236 -2.2910209 0.3982794
## [7] 0.7043392 0.6696706 -0.9097147 -2.1490613
for en RComo se ha venido explicando, en el análisis de datos usualmente nos enfrentamos a situaciones tediosas o repetitivas que en algunas ocasiones se pueden volver imporsibles ejecutar de manera manual.En esas situaciones es donde entre en juego los ciclos.En este curso vamos a introducir el ciclo for que pasa iterativamente por cada uno de los elementos que se le indique y ejecuta una acción hasta que haya recorrido todos los elementos.
El siguiente es el diagrama del ciclo for:
Flujograma del ciclo for
Volviendo al ejemplo de la asiatencia de los equipos de fútbol, supongamos que debemos crear un reporte con el monto promedio por partido que cada equipo tuvo( con la ayuda de la función montoPromedio que ya calculamos) e imprimir en pantalla el resultado para cada uno.
#Definir el límite superior del ciclo for
n<-ncol(df)
for(i in 1:n){
suma<-montoPromedio(df[,i],precio=5000)
concat<-paste0(names(df)[i],":",suma)
print(concat)
}## [1] "Alajuela:El valor promedio es de 39959000"
## [1] "Saprissa:El valor promedio es de 34826500"
## [1] "Cartago:El valor promedio es de 14766500"
## [1] "Heredia:El valor promedio es de 22187000"
## [1] "PerezZeledon:El valor promedio es de 17355500"
## [1] "SanCarlos:El valor promedio es de 17210000"
## [1] "Grecia:El valor promedio es de 17688000"
## [1] "UCR:El valor promedio es de 1315000"
## [1] "Carmelita:El valor promedio es de 863500"
## [1] "Guadalupe:El valor promedio es de 4091000"
## [1] "Limon:El valor promedio es de 10622000"
La mejor manera de entender que está sucediendo en el ciclo, es leyendolo de la siguiente manera: "Para cada i(donde i es el índice del elemento que el ciclo se encuentra evaluando) desde 1 hasta n (el número de columnas de df) , calcule y guarde: