7 Las funciones

7.1 ¿Qué es una función?

Con este capítulo vamos a echar un primer vistazo al poder de R a través de las funciones. Una función es un conjunto de líneas de código para realizar una tarea en particular. Hemos visto muchas funciones en capítulos anteriores, unas simples como la función + para añadir números, o otras más complejas como c() o data.frame() que permiten crear un vector o data.frame. En cualquier caso, se puede reconocer una función gracias a los paréntesis que la siguen en los cuales vamos a ingresar argumentos. Los argumentos corresponden a la información que queremos transmitir a nuestra función para que realice la tarea que queremos lograr.

Para funciones simples como +, los paréntesis han sido eliminados para que el código sea más fácil de leer, pero es una función que puede usarse con paréntesis si usamos el signo + entre comillas. Los argumentos son los números que queremos agregar.

5 + 2
## [1] 7
'+'(5, 2)
## [1] 7

En este capítulo nos enfocaremos en las funciones más comunes. No se trata de aprender todo de memoria, sino de saber que existen estas funciones y de poder consultar más adelante este capítulo como referencia. ¡Con tiempo y práctica eventualmente los sabremos de memoria! Hay más de 1000 funciones en la versión básica de R, y más de 10000 paquetes adicionales que se pueden instalar, cada uno con docenas de funciones. Antes de comenzar a escribir una nueva función, siempre debemos verificar que ya no exista.

7.2 Las funciones más comunes

Para trabajar con las funciones, vamos a usar los datos iris que están incluidos con la versión básica de R y que corresponden a la longitud y el ancho de los sépalos y pétalos de diferentes especies de iris. Los datos iris estan en una data.frame de 5 columnas y 150 líneas. Para obtener más información sobre los datos iris, podemos consultar la documentación R con la función help(iris). El acceso a la documentación es el tema de la siguiente sección.

7.2.1 El acceso a la documentación

7.2.1.1 help()

La función esencial de R es acceder a la documentación (en ingles). Todas las funciones R tienen documentación. Podemos acceder a la documentación con la función help() o usando el atajo ?.

help(matrix) # equivalente a ?matrix

La documentación siempre está estructurada de la misma manera. Primero tenemos el nombre de la función buscada matrix, seguida entre llaves por el nombre del paquete R cuya función depende. Veremos cómo instalar paquetes adicionales más adelante. Por ahora tenemos los que vienen con la versión básica de R. Aquí podemos ver que la función matrix() depende del paquete base.

Podemos ver la etiqueta de la función (Matrices), seguida de los parafos Description, Usage, y Arguments. Algunas veces se agregan los párrafos Details, Note, References y See also. El último párrafo es Ejemplos. La última línea de la documentación permite volver al índice del paquete del que depende la función consultada.

Al copiar help(matrix) en nuestra consola R, podemos ver que el párrafo Description indica lo que hace la función. En el caso de help(matrix), hay tres funciones: matrix(), as.matrix() y is.matrix().

# Description
# matrix creates a matrix from the given set of values.
# as.matrix attempts to turn its argument into a matrix.
# is.matrix tests if its argument is a (strict) matrix.

El párrafo Usage explica cómo usar la función y cuáles son los valores predeterminados para cada parámetro.

# Usage
# matrix(data = NA, nrow = 1, ncol = 1, byrow = FALSE,
#        dimnames = NULL)

La función matrix() puede tomar 5 argumentos: data, nrow, ncol, byrow, y dimnames. Podemos ver que por defecto una matrix consistirá de una sola línea y una sola columna, y que la información se completará por columna.

El párrafo Arguments detalla los valores y el tipo de contenedor de cada argumento de nuestra función. Por ejemplo, podemos ver que el argumento dimnames debe ser del tipo list. Es por eso que hemos usado este formato en la sección matrix.

# Arguments
# data      an optional data vector (including a list or expression vector). 
#           Non-atomic classed R objects are coerced by as.vector and all 
#           attributes discarded.
# nrow      the desired number of rows.
# ncol      the desired number of columns.
# byrow     logical. If FALSE (the default) the matrix is filled by columns, 
#           otherwise the matrix is filled by rows.
# dimnames  A dimnames attribute for the matrix: NULL or a list of length 2 
#           giving the row and column names respectively. An empty list is 
#           treated as NULL, and a list of length one as row names. The 
#           list can be named, and the list names will be used as names for 
#           the dimensions.

El párrafo Details proporciona elementos adicionales en la función. El párrafo Examples proporciona ejemplos reproducibles en la consola.

## Example of setting row and column names
mdat <- matrix(c(1,2,3, 11,12,13), nrow = 2, ncol = 3, byrow = TRUE,
               dimnames = list(c("row1", "row2"),
                               c("C.1", "C.2", "C.3")))
mdat
##      C.1 C.2 C.3
## row1   1   2   3
## row2  11  12  13

El nombre de los argumentos no es necesario para que una función sea interpretada correctamente por R. Sin embargo, es mejor usar explicitamente el nombre de los argumentos seguidos por el signo = para que el código sea más legible.

# buen ejemplo
mdat <- matrix(c(1, 2, 3, 11, 12, 13), nrow = 2, ncol = 3, byrow = TRUE)
# mal ejemplo
mdat <- matrix(c(1, 2, 3, 11, 12, 13), 2, 3, TRUE)

7.2.1.2 help.search()

La función help.search() o ?? permite buscar una expresión en toda la documentación. Es útil cuando buscamos una función sin saber el nombre exacto de la función en R.

help.search("average")

La función help.search() devuelve una página que contiene la lista de páginas donde se encontró la expresión en la forma package-name::function-name.

7.2.2 Ver los datos

7.2.2.1 str()

La función str() permite visualizar la estructura interna de un objeto, como se indica en la documentación que podemos consultar con help(str).

str(iris)
## 'data.frame':    150 obs. of  5 variables:
##  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
##  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
##  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
##  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
##  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...

La función str() devuelve el tipo de objeto (data.frame), el número de observaciones (150), el número de variables (5), el nombre de cada variable (Sepal.Length , Sepal.Width, Petal.Length, Petal.Width, y Species), el tipo de cada variable (num,Factor), y los primeros valores de cada una de las variables. Es una función útil para echar un vistazo a un conjunto de datos, pero también para verificar que los datos sean del tipo requirido antes de realizar un análisis estadístico.

7.2.2.2 head() y tail()

La función head() devuelve los primeros valores de un objeto, y la función tail() devuelve los últimos valores de un objeto. Por defecto, se devuelven seis valores, el argumento n controla el número de valores a devolver.

head(iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa
tail(iris)
##     Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
## 145          6.7         3.3          5.7         2.5 virginica
## 146          6.7         3.0          5.2         2.3 virginica
## 147          6.3         2.5          5.0         1.9 virginica
## 148          6.5         3.0          5.2         2.0 virginica
## 149          6.2         3.4          5.4         2.3 virginica
## 150          5.9         3.0          5.1         1.8 virginica
head(iris, n = 2)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa

7.2.2.3 names()

Ya hemos visto la función names(), que permite conocer los nombres de los elementos de un objeto, pero también asignar nombres a los elementos de un objeto como a un matrix, a una list o a un data.frame.

names(iris)
## [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width" 
## [5] "Species"
irisCopy <- iris
names(irisCopy) <- c("a", "b", "c", "d", "e")
names(irisCopy)
## [1] "a" "b" "c" "d" "e"

7.2.2.4 cat() y print()

La función cat() se usa para mostrar el contenido de un objeto mientras que la función print() devuelve el valor de un objeto con la capacidad de realizar conversiones.

cat(names(iris))
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
print(names(iris))
## [1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width" 
## [5] "Species"
cat(iris[1, 1])
## 5.1
print(iris[1, 1])
## [1] 5.1
print(iris[1, 1], digits = 0)
## [1] 5

7.2.3 Manipular los datos

7.2.3.1 rank()

La función rank() devuelve el número de la posición ordenada de cada elemento de un conjunto de elementos. En el caso de elementos del mismo valor, el argumento ties.method hace posible hacer una elección sobre la clasificación. Como con todas las funciones, los detalles están presentes en la documentación.

vecManip <- c(10, 20, 30, 70, 60, 50, 40)
rank(vecManip)
## [1] 1 2 3 7 6 5 4
vecManip2 <- c(10, 20, 30, 10, 50, 10, 40)
rank(vecManip2)
## [1] 2 4 5 2 7 2 6
rank(vecManip2, ties.method = "first")
## [1] 1 4 5 2 7 3 6
rank(vecManip2, ties.method = "min")
## [1] 1 4 5 1 7 1 6
print(iris[, 1])
##   [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4
##  [18] 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5
##  [35] 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0
##  [52] 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8
##  [69] 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4
##  [86] 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8
## [103] 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5 7.7 7.7
## [120] 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7
## [137] 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9
rank(iris[, 1], ties.method = "average")
##   [1]  37.0  19.5  10.5   7.5  27.5  49.5   7.5  27.5   3.0  19.5  49.5
##  [12]  14.0  14.0   1.0  77.0  69.5  49.5  37.0  69.5  37.0  49.5  37.0
##  [23]   7.5  37.0  14.0  27.5  27.5  43.5  43.5  10.5  14.0  49.5  43.5
##  [34]  56.0  19.5  27.5  56.0  19.5   3.0  37.0  27.5   5.0   3.0  27.5
##  [45]  37.0  14.0  37.0   7.5  46.0  27.5 138.0 112.0 135.5  56.0 118.0
##  [56]  69.5 104.0  19.5 121.5  43.5  27.5  82.0  86.5  92.5  62.5 126.5
##  [67]  62.5  77.0  97.5  62.5  82.0  92.5 104.0  92.5 112.0 121.5 132.0
##  [78] 126.5  86.5  69.5  56.0  56.0  77.0  86.5  49.5  86.5 126.5 104.0
##  [89]  62.5  56.0  56.0  92.5  77.0  27.5  62.5  69.5  69.5  97.5  37.0
## [100]  69.5 104.0  77.0 139.0 104.0 118.0 145.0  19.5 143.0 126.5 141.0
## [111] 118.0 112.0 132.0  69.5  77.0 112.0 118.0 147.5 147.5  86.5 135.5
## [122]  62.5 147.5 104.0 126.5 141.0  97.5  92.5 112.0 141.0 144.0 150.0
## [133] 112.0 104.0  92.5 147.5 104.0 112.0  86.5 135.5 126.5 135.5  77.0
## [144] 132.0 126.5 126.5 104.0 118.0  97.5  82.0
# help(rank)
# ...
# Usage
# rank(x, na.last = TRUE,
#     ties.method = c("average", "first", "last", 
#       "random", "max", "min"))

7.2.3.2 order()

La función order() devuelve el número de la reorganización de los elementos en función de su posición. Es muy útil, por ejemplo, para ordenar un data.frame en función de una columna.

print(vecManip2)
## [1] 10 20 30 10 50 10 40
rank(vecManip2)
## [1] 2 4 5 2 7 2 6
order(vecManip2)
## [1] 1 4 6 2 3 7 5
print(iris[, 1])
##   [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4
##  [18] 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5
##  [35] 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0
##  [52] 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8
##  [69] 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4
##  [86] 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8
## [103] 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5 7.7 7.7
## [120] 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7
## [137] 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9
rank(iris[, 1])
##   [1]  37.0  19.5  10.5   7.5  27.5  49.5   7.5  27.5   3.0  19.5  49.5
##  [12]  14.0  14.0   1.0  77.0  69.5  49.5  37.0  69.5  37.0  49.5  37.0
##  [23]   7.5  37.0  14.0  27.5  27.5  43.5  43.5  10.5  14.0  49.5  43.5
##  [34]  56.0  19.5  27.5  56.0  19.5   3.0  37.0  27.5   5.0   3.0  27.5
##  [45]  37.0  14.0  37.0   7.5  46.0  27.5 138.0 112.0 135.5  56.0 118.0
##  [56]  69.5 104.0  19.5 121.5  43.5  27.5  82.0  86.5  92.5  62.5 126.5
##  [67]  62.5  77.0  97.5  62.5  82.0  92.5 104.0  92.5 112.0 121.5 132.0
##  [78] 126.5  86.5  69.5  56.0  56.0  77.0  86.5  49.5  86.5 126.5 104.0
##  [89]  62.5  56.0  56.0  92.5  77.0  27.5  62.5  69.5  69.5  97.5  37.0
## [100]  69.5 104.0  77.0 139.0 104.0 118.0 145.0  19.5 143.0 126.5 141.0
## [111] 118.0 112.0 132.0  69.5  77.0 112.0 118.0 147.5 147.5  86.5 135.5
## [122]  62.5 147.5 104.0 126.5 141.0  97.5  92.5 112.0 141.0 144.0 150.0
## [133] 112.0 104.0  92.5 147.5 104.0 112.0  86.5 135.5 126.5 135.5  77.0
## [144] 132.0 126.5 126.5 104.0 118.0  97.5  82.0
order(iris[, 1])
##   [1]  14   9  39  43  42   4   7  23  48   3  30  12  13  25  31  46   2
##  [18]  10  35  38  58 107   5   8  26  27  36  41  44  50  61  94   1  18
##  [35]  20  22  24  40  45  47  99  28  29  33  60  49   6  11  17  21  32
##  [52]  85  34  37  54  81  82  90  91  65  67  70  89  95 122  16  19  56
##  [69]  80  96  97 100 114  15  68  83  93 102 115 143  62  71 150  63  79
##  [86]  84  86 120 139  64  72  74  92 128 135  69  98 127 149  57  73  88
## [103] 101 104 124 134 137 147  52  75 112 116 129 133 138  55 105 111 117
## [120] 148  59  76  66  78  87 109 125 141 145 146  77 113 144  53 121 140
## [137] 142  51 103 110 126 130 108 131 106 118 119 123 136 132
head(iris[order(iris[, 1]),], n = 10)
##    Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 14          4.3         3.0          1.1         0.1  setosa
## 9           4.4         2.9          1.4         0.2  setosa
## 39          4.4         3.0          1.3         0.2  setosa
## 43          4.4         3.2          1.3         0.2  setosa
## 42          4.5         2.3          1.3         0.3  setosa
## 4           4.6         3.1          1.5         0.2  setosa
## 7           4.6         3.4          1.4         0.3  setosa
## 23          4.6         3.6          1.0         0.2  setosa
## 48          4.6         3.2          1.4         0.2  setosa
## 3           4.7         3.2          1.3         0.2  setosa

7.2.3.3 sort()

La función sort() se usa para ordenar los elementos de un objeto. No permite la clasificación por más de una variable, como es el caso de order().

print(vecManip2)
## [1] 10 20 30 10 50 10 40
sort(vecManip2)
## [1] 10 10 10 20 30 40 50
print(iris[, 1])
##   [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.6 5.0 4.4 4.9 5.4 4.8 4.8 4.3 5.8 5.7 5.4
##  [18] 5.1 5.7 5.1 5.4 5.1 4.6 5.1 4.8 5.0 5.0 5.2 5.2 4.7 4.8 5.4 5.2 5.5
##  [35] 4.9 5.0 5.5 4.9 4.4 5.1 5.0 4.5 4.4 5.0 5.1 4.8 5.1 4.6 5.3 5.0 7.0
##  [52] 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 5.0 5.9 6.0 6.1 5.6 6.7 5.6 5.8
##  [69] 6.2 5.6 5.9 6.1 6.3 6.1 6.4 6.6 6.8 6.7 6.0 5.7 5.5 5.5 5.8 6.0 5.4
##  [86] 6.0 6.7 6.3 5.6 5.5 5.5 6.1 5.8 5.0 5.6 5.7 5.7 6.2 5.1 5.7 6.3 5.8
## [103] 7.1 6.3 6.5 7.6 4.9 7.3 6.7 7.2 6.5 6.4 6.8 5.7 5.8 6.4 6.5 7.7 7.7
## [120] 6.0 6.9 5.6 7.7 6.3 6.7 7.2 6.2 6.1 6.4 7.2 7.4 7.9 6.4 6.3 6.1 7.7
## [137] 6.3 6.4 6.0 6.9 6.7 6.9 5.8 6.8 6.7 6.7 6.3 6.5 6.2 5.9
sort(iris[, 1])
##   [1] 4.3 4.4 4.4 4.4 4.5 4.6 4.6 4.6 4.6 4.7 4.7 4.8 4.8 4.8 4.8 4.8 4.9
##  [18] 4.9 4.9 4.9 4.9 4.9 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.0 5.1 5.1
##  [35] 5.1 5.1 5.1 5.1 5.1 5.1 5.1 5.2 5.2 5.2 5.2 5.3 5.4 5.4 5.4 5.4 5.4
##  [52] 5.4 5.5 5.5 5.5 5.5 5.5 5.5 5.5 5.6 5.6 5.6 5.6 5.6 5.6 5.7 5.7 5.7
##  [69] 5.7 5.7 5.7 5.7 5.7 5.8 5.8 5.8 5.8 5.8 5.8 5.8 5.9 5.9 5.9 6.0 6.0
##  [86] 6.0 6.0 6.0 6.0 6.1 6.1 6.1 6.1 6.1 6.1 6.2 6.2 6.2 6.2 6.3 6.3 6.3
## [103] 6.3 6.3 6.3 6.3 6.3 6.3 6.4 6.4 6.4 6.4 6.4 6.4 6.4 6.5 6.5 6.5 6.5
## [120] 6.5 6.6 6.6 6.7 6.7 6.7 6.7 6.7 6.7 6.7 6.7 6.8 6.8 6.8 6.9 6.9 6.9
## [137] 6.9 7.0 7.1 7.2 7.2 7.2 7.3 7.4 7.6 7.7 7.7 7.7 7.7 7.9

7.2.3.4 append()

Esta función se usa para agregar un elemento a un vector en una posición determinada por el argumento after. Esta función también es más rápida que su alternativa c().

print(vecManip2)
## [1] 10 20 30 10 50 10 40
append(vecManip2, 5)
## [1] 10 20 30 10 50 10 40  5
append(vecManip2, 5, after = 2)
## [1] 10 20  5 30 10 50 10 40

7.2.3.5 cbind() y rbind()

Las funciones cbind() y rbind() permiten combinar elementos por columna o por línea.

cbind(vecManip2, vecManip2)
##      vecManip2 vecManip2
## [1,]        10        10
## [2,]        20        20
## [3,]        30        30
## [4,]        10        10
## [5,]        50        50
## [6,]        10        10
## [7,]        40        40
rbind(vecManip2, vecManip2)
##           [,1] [,2] [,3] [,4] [,5] [,6] [,7]
## vecManip2   10   20   30   10   50   10   40
## vecManip2   10   20   30   10   50   10   40

7.2.3.6 paste() y paste0()

Estas son dos funciones que usaremos mucho a partir de ahora. Las funciones paste() y paste0() se usan para concatenar cadenas de texto. La función paste0() es equivalente a paste() sin proponer un separador entre los elementos a concatenar. La función paste0() también es más rápida.

paste(1, "a")
## [1] "1 a"
paste0(1, "a")
## [1] "1a"
paste(1, "a", sep = "_")
## [1] "1_a"
paste0("prefix_", vecManip2, "_suffix")
## [1] "prefix_10_suffix" "prefix_20_suffix" "prefix_30_suffix"
## [4] "prefix_10_suffix" "prefix_50_suffix" "prefix_10_suffix"
## [7] "prefix_40_suffix"
paste(vecManip2, rank(vecManip2), sep = "_")
## [1] "10_2" "20_4" "30_5" "10_2" "50_7" "10_2" "40_6"

7.2.3.7 rev()

La función rev () devuelve los elementos de un objeto en orden inverso.

print(vecManip2)
## [1] 10 20 30 10 50 10 40
rev(vecManip2)
## [1] 40 10 50 10 30 20 10

7.2.3.8 %in%()

La función %in%() se puede comparar con un operador de comparación. Esta función toma dos objetos como argumentos y devuelve TRUE o FALSE para cada elemento del primer objeto de acuerdo con su presencia o ausencia en el segundo objeto. Para acceder a la documentación de la función, use help('%in%') (con comillas simples).

print(vecManip)
## [1] 10 20 30 70 60 50 40
print(vecManip2)
## [1] 10 20 30 10 50 10 40
vecManip %in% vecManip2
## [1]  TRUE  TRUE  TRUE FALSE FALSE  TRUE  TRUE
vecManip2 %in% vecManip
## [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE

7.2.4 Funciones matemáticas

Ya hemos visto las funciones +, -, *, /, ^, %% y otros operadores aritméticos. R también tiene funciones matemáticas básicas como exponencial exp(), raíz cuadrada sqrt(), valor absoluto abs(), sinus sin(), coseno cos(), tangente tan(), logaritmo log(), logaritmo base 10 log10(), arco coseno acos(), arco sinus asin(), y arco tangente atan().

print(vecManip2)
## [1] 10 20 30 10 50 10 40
exp(vecManip2)
## [1] 2.202647e+04 4.851652e+08 1.068647e+13 2.202647e+04 5.184706e+21
## [6] 2.202647e+04 2.353853e+17
sqrt(vecManip2)
## [1] 3.162278 4.472136 5.477226 3.162278 7.071068 3.162278 6.324555
abs(-vecManip2)
## [1] 10 20 30 10 50 10 40
sin(vecManip2)
## [1] -0.5440211  0.9129453 -0.9880316 -0.5440211 -0.2623749 -0.5440211
## [7]  0.7451132
cos(vecManip2)
## [1] -0.8390715  0.4080821  0.1542514 -0.8390715  0.9649660 -0.8390715
## [7] -0.6669381
tan(vecManip2)
## [1]  0.6483608  2.2371609 -6.4053312  0.6483608 -0.2719006  0.6483608
## [7] -1.1172149
log(vecManip2)
## [1] 2.302585 2.995732 3.401197 2.302585 3.912023 2.302585 3.688879
log10(vecManip2)
## [1] 1.000000 1.301030 1.477121 1.000000 1.698970 1.000000 1.602060
acos(vecManip2/100)
## [1] 1.470629 1.369438 1.266104 1.470629 1.047198 1.470629 1.159279
asin(vecManip2/100)
## [1] 0.1001674 0.2013579 0.3046927 0.1001674 0.5235988 0.1001674 0.4115168
atan(vecManip2/100)
## [1] 0.09966865 0.19739556 0.29145679 0.09966865 0.46364761 0.09966865
## [7] 0.38050638

7.2.5 Estadísticas descriptivas

También podemos realizar estadísticas descriptivas de forma muy simple a partir de un conjunto de datos.

7.2.5.1 mean()

La función mean() devuelve la media. Para ignorar los valores faltantes NA, hay que afectar el valor TRUE al argumento na.rm().

mean(iris[, 1])
## [1] 5.843333
vecManip3 <- c(1, 5, 6, 8, NA, 45, NA, 14)
mean(vecManip3)
## [1] NA
mean(vecManip3, na.rm = TRUE)
## [1] 13.16667

7.2.5.2 sd()

La función sd() devuelve la desviación estándar.

sd(iris[, 1])
## [1] 0.8280661
print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
sd(vecManip3)
## [1] NA
sd(vecManip3, na.rm = TRUE)
## [1] 16.16684

7.2.5.3 max() y min()

La función max() devuelve el valor máximo y min() el valor mínimo.

max(iris[, 1])
## [1] 7.9
print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
max(vecManip3)
## [1] NA
max(vecManip3, na.rm = TRUE)
## [1] 45
min(iris[, 1])
## [1] 4.3
min(vecManip3)
## [1] NA
min(vecManip3, na.rm = TRUE)
## [1] 1

7.2.5.4 quantile()

La función quantile() devuelve el cuantil definido por el argumento probs.

quantile(iris[, 1])
##   0%  25%  50%  75% 100% 
##  4.3  5.1  5.8  6.4  7.9
quantile(iris[, 1], probs = c(0, 0.25, 0.5, 0.75, 1))
##   0%  25%  50%  75% 100% 
##  4.3  5.1  5.8  6.4  7.9
quantile(iris[, 1], probs = c(0, 0.1, 0.5, 0.9, 1))
##   0%  10%  50%  90% 100% 
##  4.3  4.8  5.8  6.9  7.9

7.2.5.5 summary()

La función summary() devuelve un resumen con el mínimo, primer cuartil, mediana, promedio, tercer cuartil y máximo.

summary(iris[, 1])
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   4.300   5.100   5.800   5.843   6.400   7.900

7.2.5.6 median()

La función median() devuelve la mediana.

median(iris[, 1])
## [1] 5.8
print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
median(vecManip3)
## [1] NA
median(vecManip3, na.rm = TRUE)
## [1] 7

7.2.5.7 length()

La función length() devuelve el tamaño de un objeto (número de elementos).

length(iris[, 1])
## [1] 150
print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
length(vecManip3)
## [1] 8

7.2.5.8 nrow() et ncol()

La función nrow() devuelve el número de líneas y la función ncol() el número de columnas en un objeto.

nrow(iris)
## [1] 150
ncol(iris)
## [1] 5

7.2.5.9 round(), ceiling(), floor(), et trunc()

La función round() le permite seleccionar una cierta cantidad de decimales (0 por defecto)

round(5.56874258564)
## [1] 6
round(5.56874258564, digits = 2)
## [1] 5.57

La función ceiling() devuelve el entero más pequeño que no es inferior al valor especificado.

ceiling(5.9999)
## [1] 6
ceiling(5.0001)
## [1] 6

La función floor() devuelve el entero más grande que no excede el valor especificado.

floor(5.9999)
## [1] 5
floor(5.0001)
## [1] 5

La función trunc() devuelve la parte entera del valor especificado.

trunc(5.9999)
## [1] 5
trunc(5.0001)
## [1] 5

7.2.5.10 rowSums() et colSums()

Las funciones rowSums() y colSums() calculan la suma de filas y columnas.

rowSums(iris[, c(1, 2, 3, 4)])
##   [1] 10.2  9.5  9.4  9.4 10.2 11.4  9.7 10.1  8.9  9.6 10.8 10.0  9.3  8.5
##  [15] 11.2 12.0 11.0 10.3 11.5 10.7 10.7 10.7  9.4 10.6 10.3  9.8 10.4 10.4
##  [29] 10.2  9.7  9.7 10.7 10.9 11.3  9.7  9.6 10.5 10.0  8.9 10.2 10.1  8.4
##  [43]  9.1 10.7 11.2  9.5 10.7  9.4 10.7  9.9 16.3 15.6 16.4 13.1 15.4 14.3
##  [57] 15.9 11.6 15.4 13.2 11.5 14.6 13.2 15.1 13.4 15.6 14.6 13.6 14.4 13.1
##  [71] 15.7 14.2 15.2 14.8 14.9 15.4 15.8 16.4 14.9 12.8 12.8 12.6 13.6 15.4
##  [85] 14.4 15.5 16.0 14.3 14.0 13.3 13.7 15.1 13.6 11.6 13.8 14.1 14.1 14.7
##  [99] 11.7 13.9 18.1 15.5 18.1 16.6 17.5 19.3 13.6 18.3 16.8 19.4 16.8 16.3
## [113] 17.4 15.2 16.1 17.2 16.8 20.4 19.5 14.7 18.1 15.3 19.2 15.7 17.8 18.2
## [127] 15.6 15.8 16.9 17.6 18.2 20.1 17.0 15.7 15.7 19.1 17.7 16.8 15.6 17.5
## [141] 17.8 17.4 15.5 18.2 18.2 17.2 15.7 16.7 17.3 15.8
colSums(iris[, c(1, 2, 3, 4)])
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##        876.5        458.6        563.7        179.9

7.2.5.11 rowMeans() et colMeans()

Las funciones rowMeans() y colMeans() calculan el promedio de filas y columnas.

rowMeans(iris[, c(1, 2, 3, 4)])
##   [1] 2.550 2.375 2.350 2.350 2.550 2.850 2.425 2.525 2.225 2.400 2.700
##  [12] 2.500 2.325 2.125 2.800 3.000 2.750 2.575 2.875 2.675 2.675 2.675
##  [23] 2.350 2.650 2.575 2.450 2.600 2.600 2.550 2.425 2.425 2.675 2.725
##  [34] 2.825 2.425 2.400 2.625 2.500 2.225 2.550 2.525 2.100 2.275 2.675
##  [45] 2.800 2.375 2.675 2.350 2.675 2.475 4.075 3.900 4.100 3.275 3.850
##  [56] 3.575 3.975 2.900 3.850 3.300 2.875 3.650 3.300 3.775 3.350 3.900
##  [67] 3.650 3.400 3.600 3.275 3.925 3.550 3.800 3.700 3.725 3.850 3.950
##  [78] 4.100 3.725 3.200 3.200 3.150 3.400 3.850 3.600 3.875 4.000 3.575
##  [89] 3.500 3.325 3.425 3.775 3.400 2.900 3.450 3.525 3.525 3.675 2.925
## [100] 3.475 4.525 3.875 4.525 4.150 4.375 4.825 3.400 4.575 4.200 4.850
## [111] 4.200 4.075 4.350 3.800 4.025 4.300 4.200 5.100 4.875 3.675 4.525
## [122] 3.825 4.800 3.925 4.450 4.550 3.900 3.950 4.225 4.400 4.550 5.025
## [133] 4.250 3.925 3.925 4.775 4.425 4.200 3.900 4.375 4.450 4.350 3.875
## [144] 4.550 4.550 4.300 3.925 4.175 4.325 3.950
colMeans(iris[, c(1, 2, 3, 4)])
## Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
##     5.843333     3.057333     3.758000     1.199333

7.2.5.12 aggregate()

La función aggregate() permite agrupar los elementos de un objeto de acuerdo con un valor. El argumento by define el elemento sobre el que se realiza la agrupación. Debe ser del tipo list.

aggregate(iris[, c(1, 2, 3, 4)], by = list(iris$Species), FUN = mean)
##      Group.1 Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1     setosa        5.006       3.428        1.462       0.246
## 2 versicolor        5.936       2.770        4.260       1.326
## 3  virginica        6.588       2.974        5.552       2.026
aggregate(iris[, c(1, 2)], by = list(iris$Species), FUN = summary)
##      Group.1 Sepal.Length.Min. Sepal.Length.1st Qu. Sepal.Length.Median
## 1     setosa             4.300                4.800               5.000
## 2 versicolor             4.900                5.600               5.900
## 3  virginica             4.900                6.225               6.500
##   Sepal.Length.Mean Sepal.Length.3rd Qu. Sepal.Length.Max.
## 1             5.006                5.200             5.800
## 2             5.936                6.300             7.000
## 3             6.588                6.900             7.900
##   Sepal.Width.Min. Sepal.Width.1st Qu. Sepal.Width.Median Sepal.Width.Mean
## 1            2.300               3.200              3.400            3.428
## 2            2.000               2.525              2.800            2.770
## 3            2.200               2.800              3.000            2.974
##   Sepal.Width.3rd Qu. Sepal.Width.Max.
## 1               3.675            4.400
## 2               3.000            3.400
## 3               3.175            3.800

7.2.5.13 range()

La función range() devuelve el mínimo y el máximo.

range(iris[, 1])
## [1] 4.3 7.9
print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
range(vecManip3)
## [1] NA NA
range(vecManip3, na.rm = TRUE)
## [1]  1 45

7.2.5.14 unique()

La función unique() devuelve los valores únicos de un objeto (sin duplicados).

unique(iris[, 1])
##  [1] 5.1 4.9 4.7 4.6 5.0 5.4 4.4 4.8 4.3 5.8 5.7 5.2 5.5 4.5 5.3 7.0 6.4
## [18] 6.9 6.5 6.3 6.6 5.9 6.0 6.1 5.6 6.7 6.2 6.8 7.1 7.6 7.3 7.2 7.7 7.4
## [35] 7.9
print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
unique(vecManip3)
## [1]  1  5  6  8 NA 45 14

7.3 Otras funciones útiles

No podemos abordar todas las funciones útiles, aquí solo abordaremos ciertas funciones. A lo largo de este libro, se usarán nuevas funciones. Cuando se utiliza una nueva función, nuestro reflejo siempre debe ser el mismo: consultar la documentación con la función help().

7.3.1 seq_along()

La función seq_along() se usa para crear un vector del tamaño del objeto rellenado y tomando como valores los números de 1 a N (N corresponde al número de elementos del objeto). Esta función nos servirá mucho en el capítulo sobre bucles.

print(vecManip3)
## [1]  1  5  6  8 NA 45 NA 14
seq_along(vecManip3)
## [1] 1 2 3 4 5 6 7 8

7.3.2 :

La función : permite crear una secuencia desde a hacia b por pasos de 1. Ha sido difícil escribir los capítulos anteriores sin usarlo ya que esta función es muy útil. Aquí estan algunos ejemplos.

5:10
## [1]  5  6  7  8  9 10
head(iris[, c(1, 2, 3, 4)])
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1          5.1         3.5          1.4         0.2
## 2          4.9         3.0          1.4         0.2
## 3          4.7         3.2          1.3         0.2
## 4          4.6         3.1          1.5         0.2
## 5          5.0         3.6          1.4         0.2
## 6          5.4         3.9          1.7         0.4
head(iris[, 1:4]) # ;-)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1          5.1         3.5          1.4         0.2
## 2          4.9         3.0          1.4         0.2
## 3          4.7         3.2          1.3         0.2
## 4          4.6         3.1          1.5         0.2
## 5          5.0         3.6          1.4         0.2
## 6          5.4         3.9          1.7         0.4
miVec01 <- c(1, 2, 3, 4)
miVec01 <- 1:4 # ;-)
-10:12
##  [1] -10  -9  -8  -7  -6  -5  -4  -3  -2  -1   0   1   2   3   4   5   6
## [18]   7   8   9  10  11  12
5:-5
##  [1]  5  4  3  2  1  0 -1 -2 -3 -4 -5
paste("X", 1:10, sep = "_")
##  [1] "X_1"  "X_2"  "X_3"  "X_4"  "X_5"  "X_6"  "X_7"  "X_8"  "X_9"  "X_10"

7.3.3 rep()

La función rep() permite repetir elementos.

miVec12 <- c(1, 1, 1, 1, 1, 1, 1, 1, 1)
miVec12 <- rep(1, times = 9) # ;-)
rep("Hola", times = 3)
## [1] "Hola" "Hola" "Hola"
rep(1:3, time = 3)
## [1] 1 2 3 1 2 3 1 2 3
rep(1:3, length.out = 10)
##  [1] 1 2 3 1 2 3 1 2 3 1
rep(1:3, each = 3)
## [1] 1 1 1 2 2 2 3 3 3

7.3.4 seq()

La función seq() permite crear una secuencia personalizada.

seq(from = 0, to = 1, by = 0.2)
## [1] 0.0 0.2 0.4 0.6 0.8 1.0
seq(from = 20, to = 10, length.out = 10)
##  [1] 20.00000 18.88889 17.77778 16.66667 15.55556 14.44444 13.33333
##  [8] 12.22222 11.11111 10.00000
letters[seq(from = 1, to = 26, by = 2)]
##  [1] "a" "c" "e" "g" "i" "k" "m" "o" "q" "s" "u" "w" "y"
rep(seq(from = 1, to = 2, by = 0.5), times = 3)
## [1] 1.0 1.5 2.0 1.0 1.5 2.0 1.0 1.5 2.0

7.3.5 getwd()

La función getwd() establece la carpeta de trabajo. Esto corresponde a la ubicación relativa desde la cual R se posiciona para identificar los archivos. Este concepto tendrá sentido cuando veamos cómo importar y exportar datos.

getwd()
## [1] "D:/GitHub/myRbook_SP"

7.3.6 setwd()

La función setwd() se usa para definir un nuevo directorio de trabajo (carpeta de trabajo).

oldWd <- getwd()
print(oldWd)
## [1] "D:/GitHub/myRbook_SP"
setwd("..")
getwd()
## [1] "D:/GitHub"
setwd(oldWd)
getwd()
## [1] "D:/GitHub/myRbook_SP"

7.3.7 list.files()

La función list.files () se usa para listar todos los archivos en el directorio de trabajo.

list.files(pattern = "(html)$") # html
## [1] "google_analytics_SP.html"
list.files(pattern = "(pdf)$") # pdf
## character(0)

7.3.8 ls()

Al igual que la función list.files() hace posible listar todos los archivos presentes en el directorio de trabajo, la función ls() permite listar todos los objetos presentes en el entorno de trabajo de R.

ls()
##  [1] "aLogic"       "bddInsect"    "characters"   "contrib"     
##  [5] "dfForMat"     "factor01"     "i"            "irisCopy"    
##  [9] "j"            "k"            "logicals"     "mdat"        
## [13] "miArray"      "miArray02"    "miDf01"       "miDfSub01"   
## [17] "miDfSub02"    "miDfSub03"    "miDfSub04"    "miList01"    
## [21] "miList02"     "miList0203"   "miList03"     "miList04"    
## [25] "miList05"     "miList06"     "miMat"        "miMat01"     
## [29] "miMat02"      "miVec01"      "miVec02"      "miVec03"     
## [33] "miVec04"      "miVec05"      "miVec06"      "miVec07"     
## [37] "miVec08"      "miVec09"      "miVec10"      "miVec11"     
## [41] "miVec12"      "miVec13"      "miVec14"      "miVec15"     
## [45] "miVec20"      "miVec21"      "miVec22"      "miVec23"     
## [49] "miVec24"      "miVec25"      "miVecArr"     "miVecArr02"  
## [53] "miVecConf"    "miVecNA"      "miVecOp"      "msg"         
## [57] "myCol"        "myRow"        "myText"       "myText2"     
## [61] "myText3"      "myText4"      "myText5"      "nbrRep"      
## [65] "newVec"       "newVec2"      "numbers"      "oldWd"       
## [69] "opAriDf"      "roundDou"     "sumIntDou"    "sumIntInt"   
## [73] "termino01"    "termino02"    "vecForMat01"  "vecForMat02" 
## [77] "vecForMat03"  "vecForMatrix" "vecManip"     "vecManip2"   
## [81] "vecManip3"
zzz <- "a new object"
ls()
##  [1] "aLogic"       "bddInsect"    "characters"   "contrib"     
##  [5] "dfForMat"     "factor01"     "i"            "irisCopy"    
##  [9] "j"            "k"            "logicals"     "mdat"        
## [13] "miArray"      "miArray02"    "miDf01"       "miDfSub01"   
## [17] "miDfSub02"    "miDfSub03"    "miDfSub04"    "miList01"    
## [21] "miList02"     "miList0203"   "miList03"     "miList04"    
## [25] "miList05"     "miList06"     "miMat"        "miMat01"     
## [29] "miMat02"      "miVec01"      "miVec02"      "miVec03"     
## [33] "miVec04"      "miVec05"      "miVec06"      "miVec07"     
## [37] "miVec08"      "miVec09"      "miVec10"      "miVec11"     
## [41] "miVec12"      "miVec13"      "miVec14"      "miVec15"     
## [45] "miVec20"      "miVec21"      "miVec22"      "miVec23"     
## [49] "miVec24"      "miVec25"      "miVecArr"     "miVecArr02"  
## [53] "miVecConf"    "miVecNA"      "miVecOp"      "msg"         
## [57] "myCol"        "myRow"        "myText"       "myText2"     
## [61] "myText3"      "myText4"      "myText5"      "nbrRep"      
## [65] "newVec"       "newVec2"      "numbers"      "oldWd"       
## [69] "opAriDf"      "roundDou"     "sumIntDou"    "sumIntInt"   
## [73] "termino01"    "termino02"    "vecForMat01"  "vecForMat02" 
## [77] "vecForMat03"  "vecForMatrix" "vecManip"     "vecManip2"   
## [81] "vecManip3"    "zzz"

7.3.9 rm()

La función rm() permite eliminar un objeto presente en el entorno de trabajo de R.

rm(zzz)
ls()
##  [1] "aLogic"       "bddInsect"    "characters"   "contrib"     
##  [5] "dfForMat"     "factor01"     "i"            "irisCopy"    
##  [9] "j"            "k"            "logicals"     "mdat"        
## [13] "miArray"      "miArray02"    "miDf01"       "miDfSub01"   
## [17] "miDfSub02"    "miDfSub03"    "miDfSub04"    "miList01"    
## [21] "miList02"     "miList0203"   "miList03"     "miList04"    
## [25] "miList05"     "miList06"     "miMat"        "miMat01"     
## [29] "miMat02"      "miVec01"      "miVec02"      "miVec03"     
## [33] "miVec04"      "miVec05"      "miVec06"      "miVec07"     
## [37] "miVec08"      "miVec09"      "miVec10"      "miVec11"     
## [41] "miVec12"      "miVec13"      "miVec14"      "miVec15"     
## [45] "miVec20"      "miVec21"      "miVec22"      "miVec23"     
## [49] "miVec24"      "miVec25"      "miVecArr"     "miVecArr02"  
## [53] "miVecConf"    "miVecNA"      "miVecOp"      "msg"         
## [57] "myCol"        "myRow"        "myText"       "myText2"     
## [61] "myText3"      "myText4"      "myText5"      "nbrRep"      
## [65] "newVec"       "newVec2"      "numbers"      "oldWd"       
## [69] "opAriDf"      "roundDou"     "sumIntDou"    "sumIntInt"   
## [73] "termino01"    "termino02"    "vecForMat01"  "vecForMat02" 
## [77] "vecForMat03"  "vecForMatrix" "vecManip"     "vecManip2"   
## [81] "vecManip3"

7.4 Algunos ejercicios para practicar

Aquí hay algunos ejercicios para mejorar el uso de las funciones y aprender nuevas gracias a la documentación. Algunos ejercicios son difíciles, podremos volver a resolverlos más tarde.

7.4.1 Secuencias

7.4.1.1 Vamos a reproducir las siguientes secuencias:

-3 -4 -5 -6 -7 -8 -9 -10 –11

-3 -1 1 3 5 7 9 11

3.0 3.2 3.4 3.6 3.8 4.0

20 18 16 14 12 10 8 6

“a” “f” “k” “p” “u” “z”

“a” “a” “a” “a” “a” “f” “f” “f” “f” “f” “k” “k” “k” “k” “k” “p” “p” “p” “p” “p” “u” “u” “u” “u” “u” “z” “z” “z” “z” “z”

7.4.1.2 Posibles soluciones (porque siempre hay varias soluciones):

-3:-11
## [1]  -3  -4  -5  -6  -7  -8  -9 -10 -11
seq(from = -3, to = 11, by = 2)
## [1] -3 -1  1  3  5  7  9 11
seq(from = 3.0, to = 4.0, by = 0.2)
## [1] 3.0 3.2 3.4 3.6 3.8 4.0
letters[seq(from = 1, to = 26, by = 5)]
## [1] "a" "f" "k" "p" "u" "z"
letters[rep(seq(from = 1, to = 26, by = 5), each = 5)]
##  [1] "a" "a" "a" "a" "a" "f" "f" "f" "f" "f" "k" "k" "k" "k" "k" "p" "p"
## [18] "p" "p" "p" "u" "u" "u" "u" "u" "z" "z" "z" "z" "z"

7.4.2 Estadísticas descriptivas

En el conjunto de datos iris, ¿cuántos valores de ancho del sépalo son mayores que 3? Entre 2.8 y 3.2?

¿Cómo se puede visualizar la distribución de datos (función table())?

¿Cuáles son los 10 valores más pequeños?

¿Cómo se calcula el intervalo que contiene el 90% de los valores?

Si la distribución de los datos era Normal, ¿cuál sería el valor teórico de este intervalo del 90% (función qnorm())?

Soluciones:

length(iris$Sepal.Width[iris$Sepal.Width > 3])
## [1] 67
length(iris$Sepal.Width[iris$Sepal.Width > 2.8 & 
  iris$Sepal.Width < 3.2])
## [1] 47
table(iris$Sepal.Width)
## 
##   2 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9   3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 
##   1   3   4   3   8   5   9  14  10  26  11  13   6  12   6   4   3   6 
## 3.9   4 4.1 4.2 4.4 
##   2   1   1   1   1
table(round(iris$Sepal.Width))
## 
##   2   3   4 
##  19 106  25
irisSepWCopy <- iris$Sepal.Width
irisSepWCopy <- irisSepWCopy[order(irisSepWCopy)]
head(irisSepWCopy, n = 10)
##  [1] 2.0 2.2 2.2 2.2 2.3 2.3 2.3 2.3 2.4 2.4
quantile(irisSepWCopy, probs = c(0.05, 0.95))
##    5%   95% 
## 2.345 3.800
qnorm(
  p = c(0.05, 0.95), 
  mean = mean(irisSepWCopy), 
  sd = sd(irisSepWCopy)
)
## [1] 2.340397 3.774270

7.5 Escribir una función

Cuando reproducimos las mismas operaciones varias veces, el código se vuelve difícil de escribir y de mantener porque si tenemos que hacer una modificación, tendremos que repetirla cada vez que la usemos. Esto es un signo de la necesidad de usar una función. En el siguiente ejemplo, sera largo modificar el código si queremos agregar +45 en lugar de +20 para cada línea.

35 + 20
## [1] 55
758 + 20
## [1] 778
862 + 20
## [1] 882
782 + 20
## [1] 802

Como todas las funciones básicas de R, nuestras funciones tendrán un nombre y argumentos. Al igual que con los nombres de los objetos y los nombres de los archivos, es importante elegir bien el nombre de nuestra función (ver la sección sobre objetos). Para crear una función utilizaremos la función function() que toma como argumento los argumentos de nuestra función. La función devolverá el resultado deseado. Por defecto, el resultado devuelto es el último utilizado, pero es mejor usar la función return(). La siguiente función addX() toma como argumento x y devuelve x + 20.

addX <- function(x){
  return(x + 20)
}

Nuestro código se convierte en:

addX(35)
## [1] 55
addX(758)
## [1] 778
addX(862)
## [1] 882
addX(782)
## [1] 802

Si queremos cambiar el código para agregar 45 en lugar de 20, simplemente cambiamos la función addX().

addX <- function(x){
  return(x + 45)
}
addX(35)
## [1] 80
addX(758)
## [1] 803
addX(862)
## [1] 907
addX(782)
## [1] 827

Aquí podríamos haber usado el formato vector para evitar la repetición, pero eso no siempre es posible.

c(35, 758, 862, 782) + 20
## [1]  55 778 882 802

Vamos a esribir una nueva función que contará el número de consonantes y vocales en minúsculas en una palabra. Primero separaremos todas las letras de la palabra con la función strsplit (podemos consultar la ayuda para saber más acerca de esta función). Luego contaremos las vocales y las consonantes con la función length(). Para la lista de letras, usaremos el objeto letters incluido en R que contiene las 26 letras en minuscula (consulte la ayuda con ?letters).

print(letters) # las 26 letras
##  [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q"
## [18] "r" "s" "t" "u" "v" "w" "x" "y" "z"
countVowelConso <- function(word){ # nombre: countVowelConso ; argumento: word
  wordSplit <- strsplit(word, split = "")[[1]] # separar letras de word
  vowels <- c("a", "e", "i", "o", "u", "y") # las vocales
  numVowel <- length(wordSplit[wordSplit %in% vowels]) # numero de vocales
  consonants <- letters[!letters %in% vowels] # las consonantes
  numConso <- length(wordSplit[wordSplit %in% consonants]) # numero de consonantes
  return(c(numVowel, numConso)) # el resultado de la funcion
}

Ahora podemos usar nuestra función.

countVowelConso(word = "qwertyuiop azertyuiop")
## [1] 11  9

Esta función se puede modificar mostrando un mensaje más explícito. Aunque en general se debe evitar este tipo de mensaje para evitar sobrecargar las funciones, puede ser útil verificar que todo esté funcionando correctamente (luego lo borraremos).

countVowelConso <- function(word){
  wordSplit <- strsplit(word, split = "")[[1]]
  vowels <- c("a", "e", "i", "o", "u", "y")
  numVowel <- length(wordSplit[wordSplit %in% vowels])
  consonants <- letters[!letters %in% vowels]
  numConso <- length(wordSplit[wordSplit %in% consonants])
  print(paste0("Hay ", numVowel, " vocales y ", 
    numConso, " consonantes en la palabra '", word, "'."))
  return(c(numVowel, numConso))
}
countVowelConso(word = "qwertyuiop azertyuiop")
## [1] "Hay 11 vocales y 9 consonantes en la palabra 'qwertyuiop azertyuiop'."
## [1] 11  9

Por otro lado, si usamos countVowelConso(word = 5), se devolverá un error porque nuestra función espera un objeto de tipo character. En general, se recomienda manejar los errores devueltos por nuestras funciones para que nuestro código sea más fácil de mantener. Aquí simplemente comprobaremos que el argumento sea de tipo character, en un vector de tamaño 1. También comentaremos nuestra función para encontrar rápidamente lo que hace (comentario insertado en la primera línea, que a veces encontramos en la última línea de las funciones).

countVowelConso <- function(word){ # número de vocales y consonantes
  if(is.vector(word) & is.character(word) & length(word) == 1){
    wordSplit <- strsplit(word, split = "")[[1]]
    vowels <- c("a", "e", "i", "o", "u", "y")
    numVowel <- length(wordSplit[wordSplit %in% vowels])
    consonants <- letters[!letters %in% vowels]
    numConso <- length(wordSplit[wordSplit %in% consonants])
    return(c(numVowel, numConso))
  } else {
    print(paste0("Error: ", 
      "argumento 'word' incorrecto (", word, ")"))
  }
} 
countVowelConso(word = "qwertyuiop azertyuiop")
## [1] 11  9
countVowelConso(word = 5)
## [1] "Error: argumento 'word' incorrecto (5)"

Con R como para cualquier lenguaje de programación, para un problema siempre hay múltiples soluciones. Recordamos la sección sobre tipos de datos (tipo de datos logical), así como la sección sobre operadores de comparación que el valor de TRUE es 1 y el valor de FALSE es 0. Hemos visto anteriormente que la función % in% devuelve TRUE o FALSE para cada elemento del primer objeto dependiendo de su presencia o ausencia en el segundo objeto. Nuestra función podría haber usado otra función en lugar de length() para contar vocales y consonantes (función sum()).

countVowelConsoAlt <- function(word){ # número de vocales y consonantes
  if(is.vector(word) & is.character(word) & length(word) == 1){
    wordSplit <- strsplit(word, split = "")[[1]]
    vowels <- c("a", "e", "i", "o", "u", "y")
    numVowel <- sum(wordSplit %in% vowels) # <- cambio aqui
    consonants <- letters[!letters %in% vowels]
    numConso <- sum(wordSplit %in% consonants) # <- cambio aqui
    return(c(numVowel, numConso))
  } else {
    print(paste0("Error: ", 
      "argumento 'word' incorrecto (", word, ")"))
  }
} 
countVowelConsoAlt(word = "qwertyuiop azertyuiop")
## [1] 11  9

No existe una solución óptima en absoluto, todo depende de los objetivos deseados. La primera solución puede ser más fácil de entender, y la segunda puede ser más rápida en términos de velocidad de ejecución (repitiendo el uso de la función 10000 veces, el ahorro de tiempo es casi cero en nuestro caso).

system.time(replicate(n = 10000, countVowelConso(word = "qwertyuiop azertyuiop")))
##    user  system elapsed 
##    0.12    0.00    0.12
system.time(replicate(n = 10000, countVowelConsoAlt(word = "qwertyuiop azertyuiop")))
##    user  system elapsed 
##     0.1     0.0     0.1

Una función puede tener valores predeterminados para sus argumentos. Este es el caso para la mayoría de las funciones existentes. Por defecto, nuestra función ahora contará el número de vocales y consonantes en la palabra qwerty (los paréntesis son necesarios incluso en ausencia de argumentos).

countVowelConsoAlt <- function(word = "qwerty"){ # número de vocales y consonantes
  if(is.vector(word) & is.character(word) & length(word) == 1){
    wordSplit <- strsplit(word, split = "")[[1]]
    vowels <- c("a", "e", "i", "o", "u", "y")
    numVowel <- sum(wordSplit %in% vowels)
    consonants <- letters[!letters %in% vowels]
    numConso <- sum(wordSplit %in% consonants)
    return(c(numVowel, numConso))
  } else {
    print(paste0("Error: ", 
      "argumento 'word' incorrecto (", word, ")"))
  }
} 
countVowelConsoAlt() # no hay que olvidar los paréntesis
## [1] 2 4

R tiene muchas funciones, por lo tanto, antes de comenzar a escribir una nueva función, siempre debemos verificar que ya no exista en la versión básica de R o en los packages desarrollado por la comunidad de usuarios. Para esto podemos usar la ayuda con la función ??miBusqueda, pero también nuestro navegador de Internet.

7.6 Otras funciones desarrolladas por la comunidad de usuarios: los packages

Un package (o paquete) es un conjunto de archivos que agregaremos a R para usar funciones (o conjuntos de datos) que otras personas hayan desarrollado. Actualmente hay más de 10,000 paquetes en los servidores CRAN de R (CRAN; https://cran.r-project.org/web/packages/), más de 1000 en los servidores de BioConductor (para análisis genómicos) y varios cientos en GitHub. Cada paquete hace posible usar nuevas funciones para casi todo … Por lo tanto, puede ser difícil encontrar el paquete adecuado para lo que queremos lograr, y es importante dedicar tiempo a la busqueda del paquete adecuado y probar varios soluciones.

Para usar un paquete, primero debemos instalarlo, y luego cargarlo en nuestra sesión R.

7.6.1 Instalar un paquete

Una vez que hemos seleccionado nuestro paquete, podemos descargarlo e instalarlo con la función install.packages(), que toma el nombre del paquete entre comillas como argumento (la función tolera la ausencia de comillas, pero es mejor usarlas para que el código sea más legible). Algunos paquetes ya son instalados por defecto con R, como stats (que también se carga de forma predeterminada).

install.packages("stats") # R statistical functions

La instalación de un paquete debe hacerse una vez, luego el paquete está en nuestra computadora.

7.6.2 Cargar un paquete

Para poder usar las funciones de un paquete, tenemos que cargarlo en nuestra sesión R. Hay tantos paquetes disponibles que R no cargará todos los que tenemos instalados por defecto, sino solo los que necesitaremos para nuestro estudio actual. Para cargar un paquete usamos la función library() o require().

library("stats")

La carga del paquete debe hacerse cada vez que queremos ejecutar nuestro código, por lo tanto, es una parte integral de nuestro script.

7.6.3 Portabilidad del código

Acabamos de ver que la instalación de un paquete solo se debe hacer una vez por computadora, y que la carga de un paquete se debe lograr para cada nueva sesión de R. Si uno cambia de computadora o si compartimos un script con colegas, puede haber errores de ejecución relacionados con la falta de instalación de un paquete. Para superar este problema, se recomienda utilizar una función que verifique si los paquetes necesarios para ejecutar un script están instalados; si es necesario, instálelos y luego cárguelos. Hay muchas funciones para hacer esto en Internet. La solución que proponemos aquí es una mezcla adaptada de diferentes fuentes. No es necesario comprender los detalles de este script por el momento, sino simplemente comprender lo que hace. Este es un ejemplo para el paquete stats y graphics, dos paquetes que ya estan presente con la versión básica de R, pero podemos tratar todos los paquetes disponibles en CRAN; la lista se puede encontrar aquí: https://cran.r-project.org/web/packages/available_packages_by_name.html.

pkgCheck <- function(packages){
    for(x in packages){
        try(if(!require(x, character.only = TRUE)){
            install.packages(x, dependencies = TRUE)
            if(!require(x, character.only = TRUE)){
                stop()
            }
        })
    }
}
pkgCheck(c("stats", "graphics"))

Alternativamente, podemos usar la función .packages() para listar los paquetes disponibles en el CRAN en orden alfabético.

head(.packages(all.available = TRUE), n = 30)
##  [1] "abind"      "acepack"    "actuar"     "ade4"       "adehabitat"
##  [6] "agricolae"  "AlgDesign"  "alr4"       "animation"  "ape"       
## [11] "arm"        "arules"     "arulesViz"  "askpass"    "assertthat"
## [16] "audio"      "babynames"  "backports"  "base64"     "base64enc" 
## [21] "BDgraph"    "beanplot"   "BH"         "bibtex"     "bindr"     
## [26] "bindrcpp"   "binom"      "bit"        "bitops"     "biwavelet"

La función pkgCheck() asegura la portabilidad de nuestros scripts: funcionarán en todas las computadoras sin tener que realizar ningún cambio. Por lo tanto, nuestros scipts pueden adjuntarse, por ejemplo, a nuestros artículos científicos y así garantizar la reproducibilidad de nuestros resultados.

7.7 Conclusión

Felicitaciones! Ahora sabemos reconocer y usar una función, sabemos cómo buscar ayuda para una función e incluso sabemos escribir nuestras propias funciones. También sabemos que hay muchas funciones desarrolladas por la comunidad de usuarios de R dentro de paquetes (packages) que sabemos cómo instalar y cargar, y asegurar la portabilidad de nuestros scripts de una computadora a otra (importante para la reproducibilidad de los resultados). El próximo capítulo se enfocará en leer y escribir archivos porque nuestros datos suelen estar en archivos de texto u hojas de cálculo.