8  Funciones

A estas alturas del módulo, ya hemos conocido más de una docena de funciones como tibble(), c() length(), ls() o class(), pero aún no hemos tratado en profundidad la naturaleza de las funciones. Las funciones de R nos permiten realizar acciones, normalmente, sobre determinados objetos. Para conocer qué es exactamente una función, la mejor manera es aprender a crear una nueva.

Funciones y necesidades

Para analizar datos con R no es necesario saber crear funciones. Esto lo dejaremos para los programadores y los analistas de datos más avanzados. Pero debemos saber que la creación de funciones es una de las principales fortalezas de los softwares libres como R:

  • Un usuario tiene una necesidad.
  • Crea una función que cubra esta necesidad.
  • Pon la función a disposición de todos dentro de un paquete.

Todas las funciones que necesitamos las encontraremos en los paquetes de base de R o en paquetes que iremos instalando y cargando cuando sea necesario. Pero de la misma forma que anteriormente hemos visto cómo se crea un marco de datos para comprender más fácilmente cuál es su estructura, también nos resultará útil por el mismo motivo ver cómo se crea una función. Seguidamente, veremos diversas funciones y descubriremos las formas de familiarizarnos rápidamente con sus características.

8.1 Crear una función

Al igual que con los objetos, las funciones también se crean con el símbolo <-. Pero a continuación pondremos function() y seguido de las indicaciones de la acción que llevaremos a cabo dentro de los paréntesis {}. Fijémonos en el siguiente ejemplo:

  • Creamos la función por_tres().
  • La función tiene un único argumento: x.
  • El orden o acción que dejamos guardado es: multiplica x por el valor 3.
por_tres <- function(x) {x * 3}

Una vez creada la función por_tres(), vamos a testarla. Indicaremos cuál es el valor de la función que debe sustituir por x:

por_tres(x = 4)
## [1] 12
por_tres(x = elections$turnout)
## [1] 164.94 167.91 229.74 141.99 246.72
por_tres(x = 1:20)
##  [1]  3  6  9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60

La mayoría de funciones hacen lo que acabamos de observar: transforman objetos y nos devuelven el resultado de la transformación. En el primer caso, la función por_tres() devuelve el resultado 12. Esto se debe a que las indicaciones de la función eran muy claras: multiplica x por 3. En el segundo y tercer caso, multiplicamos por tres cada número del vector numérico indicado dentro de la función.

Los argumentos son cada una de las indicaciones que podemos dar dentro de una función. La función por_tres() tiene un solo argumento: x. Pero podríamos diseñar una función con varios argumentos:

  • Creamos la función por_tres_resta().
  • Primero le decimos que multiplique el argumento x por tres.
  • Luego le decimos que reste el argumento y, que por defecto será cero.
por_tres_resta <- function(x, y = 0) {x * 3 - y}
Una función por dentro

¿Cómo está hecha la función factor()? Examina alguna de las funciones aprendidas en los apartados anteriores. En R Script, solo tienes que pulsar Ctrl y hacer clic en el nombre de la función.

Ahora testaremos la función por_tres_resta(), que multiplicará por tres el valor de x y al resultado obtenido restará y:

por_tres_resta(x = 10, y = 5)
## [1] 25

Hay cuatro ideas clave adicionales que debemos saber sobre las funciones:

  1. No es necesario poner dentro de las funciones el nombre de los argumentos. Podemos poner directamente los valores de cada argumento, siempre y cuando respetemos su orden interno: primero el argumento x y después el argumento y.
por_tres_resta(10, 5)
## [1] 25
  1. Podemos consultar el orden de los argumentos con funciones como args().
args(por_tres_resta)
## function (x, y = 0) 
## NULL
  1. Algunos argumentos tienen a veces valores por defecto. En la función por_tres_resta() hemos indicado explíticamente que si no indicamos ningún valor, el argumento y tendrá asociado por defecto el valor 0.
por_tres_resta(10)
## [1] 30
  1. Las funciones también pueden operar dentro de otras funciones. La función que primero actúa es siempre la de adentro, y su resultado será utilizado como argumento de la función de afuera.

Observamos los dos ejemplos:

La función por_tres_resta() multiplica el valor 10 por tres y queda el valor cinco, de modo que el resultado es 25. Este resultado lo utilizamos como argumento de la función por_tres(), que multiplicará 25 por tres, obteniendo como resultado final el valor 75.

por_tres(por_tres_resta(10, 5))
## [1] 75

La función por_tres() multiplica por tres el valor 10. El resultado 30 es utilizado como primer argumento de por_tres_resta(), que multiplica por tres y le resta el valor cinco, marcado como segundo argumento.

por_tres_resta(por_tres(10), 5)
## [1] 85

8.2 ¿Qué funciones tenemos?

Todas las funciones de R siguen la misma estructura:

función(argumento1, argumento2, argumento3, ...)

Lo complicado es saber qué acción realiza cada función, qué argumentos tiene y cuáles de estos argumentos tienen asociados valores por defecto. Aprender todo esto no tiene otro misterio que practicar y pasar un buen rato aprendiendo la funcionalidad y estructura interna de cada función. Por ello es muy importante que cada estudiante vaya elaborando por su cuenta una lista de funciones y vaya clasificándolas de la forma que crea más conveniente.

A continuación hemos establecido una clasificación de tres tipos de funciones a las que les aplicaremos el contenido del objeto nig, que es una versión reducida de una encuesta realizada por Afrobarometer a la población nigeriana en el año 2021 (Afrobarometer, 2021).

Ejercicio 8.1 (Prepara los datos de Afrobarometer) Descarga aquí el archivo nig.csv e impórtalo a R a través de alguno de los procedimientos que conoces, como la función read_csv() del paquete tidyr.

nig <- read_csv("Datasets/nig.csv")

8.2.1 Funciones sin argumentos

Un primer bloque de funciones son las que normalmente utilizaremos sin introducir ningún argumento. Son funciones que ejecutan acciones relacionadas con la interfaz de R, como consultar el Global Environment, los paquetes instalados o los paquetes cargados.

ls()
installed.packages()
search()
getwd()

8.2.2 Funciones con un argumento

Otro grupo de funciones son aquellas que normalmente utilizaremos introduciendo un solo argumento. Cabe decir que prácticamente todas estas funciones aceptan más argumentos, pero en la gran mayoría de los casos solo pondremos un argumento, que suele ser un objeto, como un vector o un marco de datos. Aquí tenemos varios ejemplos de cómo las funciones nos pueden responder a preguntas simples sobre el marco de datos nig o los vectores que lo conforman:

  • ¿Qué contiene el marco de datos nig? Con glimpse() echaremos un vistazo a los datos:
glimpse(nig)
## Rows: 1,599
## Columns: 15
## $ age                        <chr> "26", "25", "35", "79", "19", "34", "30", "…
## $ language                   <chr> "Igbo", "Other", "Hausa", "Other", "English…
## $ urban_rural                <chr> "Urban", "Rural", "Rural", "Rural", "Rural"…
## $ region                     <chr> "IMO", "FCT ABUJA", "FCT ABUJA", "FCT ABUJA…
## $ electricity_nearby         <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "…
## $ piped_water_nearby         <chr> "Yes", "Can't determine", "Can't determine"…
## $ sweage_system_nearby       <chr> "Yes", "No", "No", "No", "No", "Yes", "No",…
## $ post_office_nearby         <chr> "No", "No", "No", "No", "No", "No", "No", "…
## $ school_nearby              <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "…
## $ soldiers_nearby            <chr> "Yes", "No", "No", "No", "No", "Yes", "No",…
## $ customs_checkpoints_nearby <chr> "No", "No", "No", "No", "No", "No", "No", "…
## $ situation_country          <chr> "Very Bad", "Very Bad", "Fairly Good", "Nei…
## $ situation_personal         <chr> "Fairly Good", "Fairly Bad", "Very good", "…
## $ armed_forces_trust         <chr> "Somewhat", "Just a little", "A lot", "Not …
## $ courts_law_trust           <chr> "Not at all", "Not at all", "Somewhat", "No…
  • ¿Cuál es la lengua de los encuestados? La función unique() nos devuelve los valores únicos del vector nig$language.
unique(nig$language)
##  [1] "Igbo"           "Other"          "Hausa"          "English"       
##  [5] "Pidgin English" "Yoruba"         "Tiv"            "Ebira"         
##  [9] "Isoko"          "Idoma"          "Don't know"     "Urhobo"        
## [13] "Refused"        "Fulani"         "Ijaw"           "Kanuri"        
## [17] "Edo"            "Agatu"          "Esan"           "Igala"         
## [21] "Ogoni"          "Ikwere"         "Nupe"           "Anang"         
## [25] "Ibibio"         "Efik"           "Karekare"       "Berom"
  • ¿Cuántas lenguas diferentes tenemos en la encuesta? Podemos añadir length() al código anterior para que nos cuente el total de valores únicos nig$language.
length(unique(nig$language))
## [1] 28
  • ¿Cuántos ciudadanos tienen soldados cerca del municipio donde viven? Con table() podemos ver las frecuencias de los valores de nig$soldiers_nearby.
table(nig$soldiers_nearby)
## 
## Can't determine              No             Yes 
##               8            1439             152
  • ¿Confía la ciudadanía nigeriana en sus fuerzas armadas?: Si combinamos table() y barplot() podemos graficar la distribución por frecuencias de nig$armed_forces_trust.
barplot(table(nig$armed_forces_trust))

Por tanto, normalmente utilizaremos solo un argumento en las funciones para extraer información de los datos de forma rápida.

8.2.3 Funciones con varios argumentos

Sin embargo, como hemos dicho antes, la mayoría de funciones tienen más de un argumento. Esto nos complica las cosas como usuarios de R, porque si ya es difícil recordar tantas funciones que existen, aún lo es más si tienen varios argumentos asociados. Por suerte, algunas de ellas tienen argumentos fáciles de recordar, porque cada argumento tiene exactamente la misma funcionalidad. Es el caso, por ejemplo, de tres funciones que ya hemos visto anteriormente:

  • c(): sirve para concatenar diferentes valores, por lo que cada valor es un argumento.
  • tibble(): permite crear un marco de datos a partir de ubicar vectores de igual longitud en cada argumento.
  • table(): si ponemos un segundo argumento en forma de vector de igual longitud del primer argumento, nos cruzará los datos en forma de tabla de frecuencias.
table(nig$situation_country, nig$electricity_nearby)
##                       
##                         No Yes
##   Don't know             4   4
##   Fairly Bad           103 257
##   Fairly Good          143 206
##   Neither good nor bad  47  88
##   Refused                0   1
##   Very Bad             145 409
##   Very good             78 114

Las funciones más complicadas son aquellas que tienen varios argumentos y que cada uno de sus argumentos tiene una funcionalidad distinta. Estas son más difíciles de recordar. Vamos a ver un ejemplo con sample(), una función que permite obtener una muestra aleatoria de un conjunto de datos. Fijémonos en que tiene varios argumentos:

args(sample)
## function (x, size, replace = FALSE, prob = NULL) 
## NULL

Vamos a verlos uno por uno.

El argumento x es un vector. Si lo introducimos, sample() nos devuelve los valores del vector desordenados de forma aleatoria.

sample(1:10)

El segundo argumento, size, nos devuelve un vector de longitud diferente (más pequeña) al vector que hemos puesto en x. Supongamos que hacemos el sorteo de dos cestas y hemos repartido 100 boletos. Podemos decidir los ganadores haciendo uso de R, pidiéndole que saque dos números al azar.

sample(1:100, 2)
## [1] 44 79

A veces, podemos querer que se repitan los valores. Es decir, que una vez sacado un valor al azar, R vuelva a ponerlo dentro del bombo para que pueda volver a salir en la siguiente jugada. Esto ocurre, por ejemplo, cuando lanzamos una moneda al aire, que se pueden repetir las caras o las cruces. Esto se logra introduciendo el tercer argumento replace.

moneda <- c("Cara", "Cruz")
sample(moneda, 5, replace = TRUE)
## [1] "Cruz" "Cruz" "Cruz" "Cruz" "Cruz"

Incluso podemos trucar la moneda con un cuarto argumento, en el que le indicamos la probabilidad de que salga cada valor. En esa moneda, será menos probable que salga cara.

moneda <- c("Cara", "Cruz")
sample(moneda, 10, replace = TRUE, prob = c(0.2, 0.8))
##  [1] "Cruz" "Cara" "Cruz" "Cruz" "Cruz" "Cruz" "Cara" "Cruz" "Cruz" "Cruz"

¿Y debemos memorizar todos los argumentos de cada una de las funciones de R? ¡Claro que no! En primer lugar, debemos saber que normalmente trabajamos con un grupo reducido de entre 30 y 50 funciones, de las que con la mayoría de ellas utilizamos solo un argumento. Y en segundo lugar, por suerte tenemos las herramientas de ayuda, que serán muy útiles para refrescarnos la memoria cuando utilizamos funciones que requieren varios argumentos.

8.3 Ayuda con las funciones

Los usuarios de R suelen tener mala memoria para recordar las funciones y sus argumentos. Por ello, para trabajar con R es absolutamente imprescindible utilizar las herramientas de ayuda que ofrece la comunidad de usuarios del programa. No existe ningún usuario de R, por muy avanzados que sean sus conocimientos, que no utilice la ayuda en su día a día.

Como hemos visto anteriormente, una posibilidad que tenemos es la función args(), que nos recuerda los argumentos de una función. Pero más allá de una lista de argumentos, debemos saber que todas las funciones de R tienen un cuadro de ayuda que conviene saber utilizar. También es fundamental dominar otros recursos de los que dispone R en la red.

8.3.1 Cuadro de ayuda

El cuadro de ayuda se abre de la siguiente forma:

?función

Y normalmente consta de los siguientes apartados:

  • Description: Una pequeña explicación de su funcionalidad.
  • Usage: Cómo se utiliza la función en código.
  • Argumentos: Hace una descripción de cómo utilizar los argumentos de la función.
  • Otros apartados: Raramente los utilizaremos.
  • Examples: Muy importante. Normalmente incluye ejemplos reproducibles que permitirán hacernos una idea

Veamos tres ejemplos:

Si miramos la ayuda de ?mean vemos que solo hace falta un solo argumento (x), pero también acepta otros argumentos.

  • x suele ser un vector numérico o lógico.
  • trim es por defecto 0 y elimina las observaciones de los extremos a la hora de hacer la media del vector numérico que indicamos en x.
  • na.rm (por defecto FALSE) elimina los valores perdidos (NA).
mean(elections$turnout)
## [1] 63.42

Si ponemos trim = 0.2, eliminará los valores que se encuentren en el 20 % superior y 20 % inferior de la distribución. Por tanto, eliminará 82.24 y 47.33 antes de hacer la media.

mean(elections$turnout, trim = 0.2)
## [1] 62.51

Si miramos la ayuda de ?str_replace, del paquete stringr vemos que tiene tres argumentos y los tres son necesarios para ejecutar la función.

  • stringr es el string (vector de carácter) donde practicaremos los cambios.
  • pattern es el patrón que deberá buscar en el string en cuestión.
  • replacement es el vector de carácter, normalmente de un solo valor, con el que sustituirá el patrón que hemos indicado.
str_replace(elections$country, "Germany", "France")
[1] "Colombia"    "Japan"       "France"      "Chile"       "New Zealand"

En el apartado de Examples tenemos ejemplos muy útiles para ver las posibilidades de la función.

El cuadro de ayuda también sirve para explorar marcos de datos vinculados a paquetes. Si consultamos la ayuda de ?who, del paquete tidyr, veremos que nos muestra datos del Global Tuberculosis Report. En Format habitualmente encontraremos el libro de códigos del marco de datos.

Ejercicio 8.2 (Menú de ayuda) Investiga las funciones ?seq(), ?rep() y sort() mediante el cuadro de ayuda. Trata de construir un código para cada función para que devuelva a la consola el resultado que se indica a continuación:

  • seq(): 7 12 17 22 27 32
  • rep(x = q): 3 3 9 9 5 5 6 6 3 3 9 9
  • sort(x = q): 9 6 5 3

En las dos últimas funciones deberás indicar, como argumento x, el objeto q e intentar averiguar el resto de argumentos:

q <- c(3, 9, 5, 6)
seq(from=7,to=32,by=5)
## [1]  7 12 17 22 27 32
rep(x=q,length.out=12,each=2)
##  [1] 3 3 9 9 5 5 6 6 3 3 9 9
sort(x=q,decreasing=TRUE)
## [1] 9 6 5 3

8.3.2 La red

Tras el cuadro de ayuda, la red es la segunda gran amistad que tiene el analista de datos de R.

  • Cheat sheets: Su traducción literal sería ‘chuletas’. En un espacio muy reducido, explican cómo utilizar las funciones de un paquete determinado. La mayoría se pueden encontrar en la web de Posit.
  • Stackoverflow: Stackoverflow es un foro de usuarios de programas estadísticos como R. Funciona de la siguiente forma: nos damos de alta, abrimos una consulta y, al cabo de pocos minutos, algún usuario nos habrá devuelto una respuesta.
  • Google: Obviamente un recurso sencillo es copiar el error de la consola en Google o hacer una pregunta en el buscador de Google (preferiblemente en inglés). Alguna de las primeras entradas seguramente nos conducirá a una página web donde alguien antes ha tenido el mismo problema y donde otro le habrá indicado la solución.

Ejercicio 8.3 (Funciones) Practica aquí lo aprendido en esta sección. Recuerda que no es necesario utilizar R para realizar los ejercicios.