4.1 Funciones e iteración

“To understand computations in R, two slogans are helpful:
* Everything that exists is an object.
* Everything that happens is a function call.”
— John Chambers

Funciones

En R todas las operaciones son producto de la llamada a una función, esto incluye operaciones como +, operadores que controlan flujo como for, if y while, e incluso operadores para obtener subconjuntos como [ ] y $.

Para escribir código eficiente y fácil de leer es importante saber escribir funciones, se dice que si hiciste copy-paste de una sección de tu código 3 o más veces es momento de escribir una función.

Escribimos una función para calcular un promedio ponderado:

Notemos que esta función recibe hasta dos argumentos:

  1. x: el vector a partir del cual calcularemos el promedio y

  2. wt: un vector de ponderadores para cada componente del vector x.

Notemos además que al segundo argumento le asignamos un valor predeterminado, esto implica que si no especificamos los ponderadores la función usará el valor predeterminado y promediara con mismo peso a todas las componentes.

Veamos como escribir una función que reciba un vector y devuelva el mismo vector centrado en cero.

  • Comenzamos escribiendo el código para un caso particular, por ejemplo, reescalando el vector \((0, 5, 10)\).

Una vez que lo probamos lo convertimos en función:

Ejercicio

Escribe una función que reciba un vector y devuelva el mismo vector reescalado al rango 0 a 1. Comienza escribiendo el código para un caso particular, por ejemplo, empieza reescalando el vector . Tip: la función range() devuelve el rango de un vector.

Estructura de una función

Las funciones de R tienen tres partes:

  1. El cuerpo: el código dentro de la función
  1. Los formales: la lista de argumentos que controlan como puedes llamar a la función,
  1. El ambiente: el mapeo de la ubicación de las variables de la función, cómo busca la función cada función el valor de las variables que usa.

Veamos mas ejemplos, ¿qué regresan las siguientes funciones?

Las reglas de búsqueda determinan como se busca el valor de una variable libre en una función. A nivel lenguaje R usa lexical scoping, esto implica que en R los valores de los símbolos se basan en como se anidan las funciones cuando fueron creadas y no en como son llamadas.

Las reglas de bússqueda de R, lexical scoping, son:

  1. Enmascaramiento de nombres: los nombres definidos dentro de una función enmascaran aquellos definidos fuera.

Si un nombre no está definido R busca un nivel arriba,

Y lo mismo ocurre cuando una función está definida dentro de una función.

Y cuando una función crea otra función:

  1. Funciones o variables: en R las funciones son objetos, sin embargo una función y un objeto no-función pueden llamarse igual. En estos casos usamos un nombre en el llamado de una función se buscará únicamente entre los objetos de tipo función.
  1. Cada vez que llamamos una función es un ambiente limpio, es decir, los objetos que se crean durante la llamada de la función no se pasan a las llamadas posteriores.
  1. Búsqueda dinámica: la búsqueda lexica determina donde se busca un valor más no determina cuando. En el caso de R los valores se buscan cuando la función se llama, y no cuando la función se crea.

Las reglas de búsqueda de R lo hacen muy flexible pero también propenso a cometer errores. Una función que suele resultar útil para revisar las dependencias de nuestras funciones es findGlobals() en el paquete codetools, esta función enlista las dependencias dentro de una función:

Observaciones del uso de funciones

  1. Cuando llamamos a una función podemos especificar los argumentos en base a posición, nombre completo o nombre parcial:
  1. Los argumentos de las funciones en R se evalúan conforme se necesitan (lazy evaluation),

La función anterior nunca utiliza el argumento b, de tal manera que f(2) no produce ningún error.

  1. Funciones con el mismo nombre en distintos paquetes:

La función filter() (incluida en R base) aplica un filtro lineal a una serie de tiempo de una variable.

Ahora cargamos dplyr.

R tiene un conflicto en la función a llamar, nosotros requerimos usar filter de stats y no la función filter de dplyr. R utiliza por default la función que pertenece al último paquete que se cargó.

La función search() nos enlista los paquetes cargados y el orden.

Una opción es especificar el paquete en la llamada de la función:

Como alternativa surge el paquete conflicted que alerta cuando hay conflictos y tiene funciones para especificar a que paquete se desea dar preferencia en una sesión de R.