Introducción al Muestreo Estadístico by Jairo A. Ayala-Godoy is licensed under CC BY-ND 4.0
R tiene suficientes funciones para que realicemos todas las tareas básicas de análisis de datos, desde importar información hasta crear documentos (¡este docuemnto ha sido creado con R!).
Sin embargo, es común que necesitemos realizar tareas para las que no existe una función específica o la necesidad de combinar funciones.
Escribir funciones es una de las actividades más importantes en cualquier lenguaje de programación. Su finalidad es poder encapsular fragmentos de código que necesiten ser ejecutados en múltiples ocasiones, con la posibilidad de ejecutarlos en cada ocasión con diferentes parámetros. A su vez, las funciones aumentan la legibilidad de un programa, de modo que es más fácil de entender.
Una de las mejores maneras de lograr tener mayor alcance haciendo Ciencia de datos es escribir funciones. Las funciones te permitirán automatizar algunas tareas comunes de una forma más poderosa y general que copiar-y-pegar.
Escribir funciones tiene tres grandes ventajas:
Puedes dar a la función un nombre que hará tu código más fácil de entender.
A medida que cambien los requerimientos, solo necesitarás cambiar tu código en un solo lugar, en vez de en varios lugares.
Eliminas las probabilidades de errores accidentales cuando copias y pegas.
Escribir funciones es un viaje de toda una vida. Incluso después de usar R por varios años, seguimos aprendiendo nuevas técnicas y mejores formas de abordar viejos problemas.
Una función tiene un nombre, argumentos y un cuerpo. Las funciones definidas por el usuario son creadas usando la sigiente estructura.
nombre <- function(argumentos) {
operaciones
}
El nombre que asignamos a una función nos permite ejecutarla y hacer referencia a ella. Es recomendable elegir nombres claros y descriptivos, no ambiguos. Una vez que la función tiene nombre, podemos llamarla usando su nombre, al igual que con las funciones por defecto de R.
Los argumentos son las variables que necesita la función para realizar sus operaciones. Aparecen entre paréntesis, separados por comas. Los argumentos pueden ser datos, estructuras de datos, conexiones a archivos u otras funciones.
El cuerpo de la función contiene, entre llaves, todas las operaciones que se ejecutarán cuando la función sea llamada. El cuerpo de una función puede ser tan sencillo o complejo como nosotros deseemos, incluso podemos definir funciones dentro de una función.
Si el código del cuerpo de la función tiene errores, sus operaciones no se realizarán y nos será devuelto un mensaje de error al ejecutarla. R no avisa si nuestra función va a funcionar o no hasta que intentamos ejecutarla.
Podemos crear una función que sea el área de un rectángulo.
area_rect <- function(l1,l2){
l1*l2
}
Cada vez que sea requiera utilizar nuestra función, debemos colocar el nombre de la función y sus argumentos.
area_rect(2,5)
## [1] 10
area_rect(3,3)
## [1] 9
También podemos escribir funciones con base en otras funciones.
precio_area <- function(l1,l2,precio){
area_rect(l1,l2)*precio
}
precio_area(2,3,100)
## [1] 600
Si tenemos varias operaciones dentro de la función, retorna como salida la última operación
precio_area <- function(l1,l2,precio){
area=area_rect(l1,l2)
area*precio
}
precio_area(2,3,100)
## [1] 600
Para evitar errores, se le puede especificar que se desea que retorne, usando el argumento return()
Cuota_hipoteca <- function(capital, years, interes){
interes_mensual = interes/12/100
meses = 1:(years*12)
cuota = capital/sum(1/(1+interes_mensual)^meses)
return(cuota)
}
Cuota_hipoteca(150000,30,3)
## [1] 632.4061
También puedes retornar varios resultados.
Cuota_hipoteca <- function(capital, years, interes){
interes_mensual = interes/12/100
meses = 1:(years*12)
cuota = capital/sum(1/(1+interes_mensual)^meses)
hipoteca = cuota*12*years
return(list(Mensual=cuota,Total_pagar=hipoteca))
}
Cuota_hipoteca(150000,30,3)
## $Mensual
## [1] 632.4061
##
## $Total_pagar
## [1] 227666.2
Las expresiones condicionales permiten ejecutar un código u otro en función de un criterio.
flog <- function(x){
if (x <= 0) ("No se puede calcular")
else (log(x))
}
Debemos probar si esta realizando lo que deseamos
flog(-10)
## [1] "No se puede calcular"
log(exp(1))
## [1] 1
También existe la función ifelse()
que permite escribir la función anterior más compacta.
flog1 <- function(x){
ifelse (x <= 0,"No se puede calcular",log(x))
}
flog(5)
## [1] 1.609438
flog1(5)
## [1] 1.609438
En la programación imperativa es habitual construir bucles dentro de los cuales se va modificando el valor de una expresión. Los bucles más habituales en R se crean con la función for()
. Su sintaxis es:
for (var in vector){
# expresión que se repite
}
Un ejemplo para ilustrar el uso de los bucles es el del cálculo del factorial de un número.
fact <- function(n){
fact <- 1
for (i in 1:n){
fact<- fact * i}
return(fact)
}
fact(5)
## [1] 120
Modifica la función factorial anterior para que devuelva explícitamente un error siempre que el argumento de la función no sea un entero positivo.
Crea una función que calcule la raíz cuadrada de los elementos de un vector.
Crea una función que, dado un número \(n\) devuelva la lista de sus divisores.
En muchos casos hipotíticos de clase usamos datos simulados. En R existe una familia de funciones donde la primera letra indica si es la distribución acumulada (p), la distribución de densidad (d) o una muestra al azar (r) y la segunda parte indica sobre la distribución. (norm
= normal, unif
= uniforme, pois
= poisson, etc.).
Por ejemplo: Distribución Normal
# Datos simulados
set.seed(1234)
X <- rnorm(500, mean = 4, sd = 0.5)
hist(X,col="lightblue")
# P(X < 5) Probabilidad acumulada bajo los parámetros de distribución simulada arriba
round(pnorm(4.5, mean = 4, sd = 0.5),2)
## [1] 0.84
# q(0.75) Valor del percentil 75 bajo los parámetros de distribución simulada arriba
round(qnorm(0.84, mean = 4, sd = 0.5),1)
## [1] 4.5
Por otra parte, la función básica de muestreo en R es sample()
.
# La función sample()
Datos <- 1:30
Datos
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
## [26] 26 27 28 29 30
set.seed(1234)
# sólo cambio el orden: permutación
sample(Datos, size = 30, replace = FALSE)
## [1] 28 16 26 22 5 12 15 9 30 6 29 4 2 7 21 10 20 19 8 13 11 27 25 24 3
## [26] 17 14 23 1 18
# muestreo aleatorio sin remplazo
sample(Datos, size = 5, replace = FALSE)
## [1] 5 2 15 8 20
# muestreo aleatorio con remplazo
sample(Datos, size = 5, replace = TRUE)
## [1] 16 12 3 23 9
Una ventaja de R es la posibilidad de programar una serie de análisis para que se ejecuten de manera sucesiva, sin necesidad de muchos conocimientos técnicos sobre programación. Una manera de realizar una serie de funciones repetitivas es mediante bucles (“loops”). Las principales funciones que realizan bucles son for()
e if()
. Sabemos que los bucles generan inversión en tiempo computacional, entonces para evitar realizar bucles se pueden utilizar las funciones de la serie apply.
Posee la estructura general: para (cada elemento en un vector) {realizar \(x\) acción}. Si necesitamos que los resultados de la acción sean guardados, es necesario crear un vector vacío antes de ejecutar la función y luego rellenarlo con el resultado del bucle.
set.seed(1234)
Data <- runif(1000,2,10)
vec <- rep(0) # vector vacío antes del bucle
for(i in 1:1000) {
vec[i] <- mean(sample(Data, 30))}
hist(vec,main="",xlab="",ylab="",col="lightgreen") # teorema central del límite
Agrega una condición: si (condición) entonces hacer \(X\), en caso contrario hacer \(Y\).
# Distribución Normal truncada
X1 <- rnorm(1000, mean= 2, sd=4)
X2 <- rep(0)
for (i in 1:1000) {
if (X1[i] < 0) X2[i] <- NA
else X2[i] <- X1[i]}
hist(X2,main="",xlab="",ylab="",col="lightpink")
Aplica una función a las columnas o filas de una matriz. Existen funciones similares como lapply, tapply, sapply (ver ayuda de la función apply para conocer la utilizadad de cada una).
m <- rbinom(50,size=20,prob=0.5)
X <- matrix(m,10, 5)
apply(X, 1, mean) # media por filas
## [1] 10.0 10.8 11.2 9.0 9.2 12.0 9.0 11.0 10.6 11.4
apply(X, 2, mean) # media por columnas
## [1] 11.1 10.7 8.9 11.2 10.2
Siempre que sea posible, es preferible aplicar una función apply()
(o álgebra sencilla) en vez de un bucle.
# Ejemplo 1
A <- c(2,3,5,8,13)
B <- c(3,5,7,9,11)
# con bucle
C <- rep(0)
for(i in 1:5) {
C[i] <- A[i] + B[i]}
C
## [1] 5 8 12 17 24
# vectorizado
C <- A + B
C
## [1] 5 8 12 17 24
# Ejemplo 2
set.seed(1234)
m <- rpois(200,10)
X <- matrix(m, 40, 5)
# con bucle
med <- rep(0)
for(i in 1:5) med[i] <- max(X[, i])
med
## [1] 20 19 16 17 19
# vectorizado
med <- apply(X, 2, max)
med
## [1] 20 19 16 17 19