2.2 Operaciones con matrices

Una matriz \(2 \times 3\) que contiene los elementos \(1:6\), por columna, se genera a través de:

A <- matrix(1:6, nrow = 2)

Alternativamente, ncol podría haber sido usado, con matrix(1:6, ncol = 3) dando el mismo resultado:

matrix(1:6, ncol = 3)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

Álgebra básica matricial

La transposición \(A^T\) de \(A\) es:

t(A)
#>      [,1] [,2]
#> [1,]    1    2
#> [2,]    3    4
#> [3,]    5    6

Se puede acceder a las dimensiones de una matriz utilizando dim(), nrow() y ncol(); por eso:

dim(A)
#> [1] 2 3
nrow(A)
#> [1] 2
ncol(A)
#> [1] 3

Los elementos individuales de una matriz, los vectores de fila o columna, e incluso las submatrices completas, pueden extraerse especificando las filas y columnas de la matriz de la que se seleccionan. Lo anterior usa una extensión simple de las reglas para subconjuntos de vectores. En realidad, internamente, las matrices son vectores con un atributo de dimensión que habilita la indexación de tipo fila/columna:

  • El elemento \(a_{ij}\) de una matriz \(A\) se extrae usando \(A[i, j]\).
  • Se pueden extraer filas o columnas enteras mediante \(A[i, ]\) y \(A[, j]\), respectivamente, que devuelven la fila o columna correspondiente en forma de vectores. Esto significa que el atributo de dimensión se descarta (por defecto); por esta razón, el subconjunto devolverá un vector en lugar de una matriz si la matriz resultante tiene solo una columna o fila.

Ocasionalmente, es necesario extraer filas, columnas o incluso elementos individuales de una matriz, como los es una matriz misma. La eliminación del atributo de dimensión se puede desactivar usando A[i, j, drop = FALSE].

Como ejemplo, el código que se muestra a continuación selecciona una matriz cuadrada que contiene el primer y tercer elemento de cada fila (se debe tener en cuenta que \(A\) tiene solo dos filas en el ejemplo):

A1 <- A[1:2, c(1, 3)]

Alternativamente, y de forma más compacta, \(A1\) podría haber sido generado usando \(A[, -2]\). Resulta importante mencionar que si no se especifica ningún número de fila, se tomarán todas las filas; el \(-2\) especifica que se requieren todas las columnas excepto la segunda.

\(A1\) es una matriz cuadrada, y si no es singular tiene una inversa. Una forma de verificar la singularidad es calcular el determinante usando la función det() disponible en R. Aquí, det(A1)es igual a \(-4\); por eso A1 no es singular. Alternativamente, sus autovalores (y autovectores) están disponibles usando eigen(). Aquí, eigen(A1)produce los valores propios \(7.531\) y \(-0.531\), mostrando nuevamente que \(A1\) no es singular.

La inversa de una matriz, si no se puede evitar, se calcula utilizando solve():

solve(A1)
#>      [,1]  [,2]
#> [1,] -1.5  1.25
#> [2,]  0.5 -0.25

Se puede comprobar que esto es de hecho el inverso de \(A1\) multiplicando \(A1\) con su inverso. Esto requiere el operador para la multiplicación de matrices, \(\textbf{%*%}\):

A1 %*% solve(A1)
#>      [,1] [,2]
#> [1,]    1    0
#> [2,]    0    1

De manera similar, las matrices conformables se suman y restan usando los operadores aritméticos + y -. Vale la pena señalar que en el caso de matrices no conformables, el reciclaje se realiza a lo largo de las columnas. Por cierto, el operador \(\textbf{*}\) también funciona para matrices; devuelve la matriz de elementos o el producto de Hadamard de matrices conformables. Otros tipos de productos matriciales que a menudo se requieren en econometría son el producto Kronecker, disponible a través de la función kronecker(), y el producto cruzado \(A^{T} B\) de dos matrices, para las cuales se implementa un algoritmo computacionalmente eficiente en la función crossprod().

Además de la descomposición espectral calculada por eigen() como se ha mencionado más arriba, R proporciona otras descomposiciones matriciales de uso frecuente, incluida la descomposición de valor singular, mediante la función svd(), la descomposición QR, mediante la función qr(), y la descomposición de Cholesky, mediante la función chol().

Modelado de matrices

En econometría, existen muchos casos en los que se necesitan construir matrices con patrones especiales. R proporciona funciones para generar matrices con tales patrones. Por ejemplo, se puede crear una matriz diagonal con unos en la diagonal usando

diag(4)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    0    0    0
#> [2,]    0    1    0    0
#> [3,]    0    0    1    0
#> [4,]    0    0    0    1

que produce la matriz de identidad de \(4 \times 4\). De manera equivalente, el mismo resultado se puede obtener mediante diag(1, 4, 4), donde el \(1\) se recicla a la longitud requerida \(4\). Por supuesto, también se obtienen fácilmente matrices diagonales más generales: diag(rep(c(1,2), c(10, 10))) produce una matriz diagonal de tamaño \(20 \times 20\) cuyos primeros \(10\) elementos diagonales son iguales a \(1\), mientras que los restantes son iguales a \(2\) (los lectores con conocimientos básicos de regresión lineal notarán que una aplicación podría ser un patrón de heterocedasticidad).

Aparte de configurar matrices diagonales, la función diag() también se puede utilizar para extraer la diagonal de una matriz existente; por ejemplo, diag(A1). Adicionalmente, upper.tri() y lower.tri() se pueden utilizar para consultar las posiciones de los elementos triangulares superiores o inferiores de una matriz, respectivamente.

Combinando matrices

También es posible formar nuevas matrices a partir de las existentes para hacer eso se deben usar las funciones rbind() y cbind(), que son similares a la función c() para concatenar vectores; como sugieren sus nombres, combinan matrices por filas o columnas. Por ejemplo, para agregar una columna de unos a la matriz \(A1\) se puede usar:

cbind(1, A1)
#>      [,1] [,2] [,3]
#> [1,]    1    1    5
#> [2,]    1    2    6

De igual forma, se puede lograr el mismo resultado combinando \(A1\) y \(diag(4, 2)\) por filas:

rbind(A1, diag(4, 2))
#>      [,1] [,2]
#> [1,]    1    5
#> [2,]    2    6
#> [3,]    4    0
#> [4,]    0    4

R como lenguaje de programación

R es un lenguaje de programación orientado a objetos, interpretado y con todas las funciones. En consecuencia, se puede usar para todas las tareas que también se usan en otros lenguajes de programación, no solo para el análisis de datos. Lo que lo hace particularmente útil para la estadística y econometría; es decir, R fue diseñado para “programar con datos” (Chambers 1998). Esto tiene varias implicaciones para los tipos de datos empleados y el paradigma utilizado de orientación a objetos.

Un tratamiento en profundidad de la programación en S/R se da en Venables y Ripley (2000). Si el lector posee conocimientos básicos de alemán, Ligges (2007) es una excelente introducción a la programación con R. En un nivel más avanzado, el equipo central de desarrollo de R (2021f, g) proporciona orientación sobre la definición del lenguaje y cómo se puede escribir las extensiones del sistema R. Los últimos documentos se pueden descargar de CRAN y también se envían con cada distribución de R.

La moda de un vector

Probablemente la estructura de datos más simple en R es un vector. Todos los elementos de un vector deben ser del mismo tipo; técnicamente, deben ser de la mismo “moda.” La moda de un vector \(x\) se puede consultar usando mode(x). En este contexto, se pueden crear vectores de moda “numérica,” “lógica” y de “caracteres” (aunque existen otras).

Ya se ha visto que crea un vector numérico cumple con la aplicación típica de almacenar los valores de alguna variable numérica en un conjunto de datos.

x <- c(1.8, 3.14, 4, 88.169, 13)

Vectores lógicos y de caracteres

Los vectores lógicos se componen de las constantes lógicas TRUE (cierto) y FALSE (falso). En una nueva sesión, los alias T y F también con compatibles con los operadores lógicos en R (esto es, también son utilizados como constantes lógicas). Sin embargo, a diferencia de TRUE y FALSE, los valores de T y F puede cambiarse (por ejemplo, utilizando T para representar el tamaño de una muestra en un contexto de series de tiempo o utilizando F como una variable para denotar un estadístico F), por lo que se recomienda no confiar en ellos, sino utilizar siempre TRUE y FALSE.

Al igual que los vectores numéricos, los vectores lógicos se pueden crear desde cero. De igual forma, pueden surgir como resultado de una comparación lógica:

x > 3.5
#> [1] FALSE FALSE  TRUE  TRUE  TRUE

A continuación se explican otras operaciones lógicas.

Se pueden emplear vectores de caracteres para almacenar cadenas. Especialmente en los primeros capítulos de este curso, se usarán principalmente para asignar etiquetas o nombres a ciertos objetos como vectores y matrices. Por ejemplo, se pueden asignar nombres a los elementos de \(x\) mediante:

names(x) <- c("a", "b", "c", "d", "e")
x
#>      a      b      c      d      e 
#>  1.800  3.140  4.000 88.169 13.000

Alternativamente, se podría haber usado names(x) <- letters[1:5], donde letters y LETTERS son vectores integrados que contienen las \(26\) letras minúsculas y mayúsculas del alfabeto latino, respectivamente. Aunque no se usan mucho en este curso, se puede notar aquí que las facilidades de manipulación de caracteres en R va mucho más allá de estos simples ejemplos, permitiendo, entre otras cosas, cálculos en documentos de texto o cadenas de comandos.

Más sobre subconjuntos

Habiendo introducido vectores de modas numéricas, de caracteres y lógicos, es útil revisar cómo crear subconjunto de vectores. A estas alturas, se ha visto cómo extraer partes de un vector usando índices numéricos, pero de hecho esto también es posible usando caracteres (si existe un atributo names) o lógicos (en cuyo caso los elementos correspondientes a TRUE son seleccionados). Por lo tanto, los siguientes comandos producen el mismo resultado:

x[3:5]
#>      c      d      e 
#>  4.000 88.169 13.000
x[c("c", "d", "e")]
#>      c      d      e 
#>  4.000 88.169 13.000
x[x > 3.5]
#>      c      d      e 
#>  4.000 88.169 13.000

El subconjunto de matrices (y también de marcos de datos o matrices multidimensionales) funciona de manera similar.

Listas

Hasta ahora, solo se han utilizado vectores planos. Ahora se procede a introducir algunas estructuras de datos relacionadas que son similares, pero contienen más información.

En R, las listas son vectores genéricos, donde cada elemento puede ser virtualmente cualquier tipo de objeto (de moda arbitraria); por ejemplo, un vector, una matriz, un marco de datos completo, una función o nuevamente una lista. Se debe tener en cuenta que este último también permite crear estructuras de datos recursivas. Debido a esta flexibilidad, las listas son la base para la mayoría de los objetos complejos en R; por ejemplo, para marcos de datos o modelos de regresión ajustados (por nombrar dos ejemplos que se describirán más adelante).

Como ejemplo simple, se crea, usando la función list(), una lista que contiene una muestra de una distribución normal estándar (generada con rnorm()) más algunas marcas en forma de una cadena de caracteres (“distribución normal”) y una lista que contiene los parámetros de población (list(mean = 0, sd = 1)).

mylist <- list(sample = rnorm(5), 
               family = "distribución normal", 
               parameters = list(mean = 0, sd = 1))

mylist$sample
#> [1]  1.2114800 -0.7378503  1.9217204 -0.3016781  0.2874794
mylist$family
#> [1] "distribución normal"
mylist$parameters
#> $mean
#> [1] 0
#> 
#> $sd
#> [1] 1
mylist$parameters$mean
#> [1] 0
mylist$parameters$sd
#> [1] 1

Para seleccionar ciertos elementos de una lista, se pueden usar los operadores de extracción $ o [[. Este último es similar a [, la principal diferencia es que solo puede seleccionar un único elemento. Por lo tanto, las siguientes declaraciones son equivalentes:

mylist[[1]]
#> [1]  1.2114800 -0.7378503  1.9217204 -0.3016781  0.2874794
mylist[["sample"]]
#> [1]  1.2114800 -0.7378503  1.9217204 -0.3016781  0.2874794
mylist$sample
#> [1]  1.2114800 -0.7378503  1.9217204 -0.3016781  0.2874794

El tercer elemento de mylist, al ser una lista, las funciones del extractor también se pueden combinar en ella como se hizo antes:

mylist[[3]]$sd
#> [1] 1

Comparaciones lógicas

R tiene un conjunto de funciones que implementan comparaciones lógicas estándar, así como algunas funciones adicionales que son convenientes cuando se trabaja con valores lógicos. Los operadores lógicos son <, <=, >, >=, == (para igualdad exacta) y != ( para la desigualdad). De igual forma, si expr1 y expr2 son expresiones lógicas, entonces:

  • expr1&expr2 representa la intersección entre expr1 y expr2 (el operador lógico “&” funciona como la intersección “y”).
  • expr1|expr2 representa la unión entre expr1 o expr2 (el operador lógico “|” funciona como la unión “o”).
  • !expr1 representa la negación de expr1 (el operador lógico “!” funciona como la negación “¬”).

Por lo tanto

x <- c(1.8, 3.14, 4, 88.169, 13)
x > 3 & x <= 4
#> [1] FALSE  TRUE  TRUE FALSE FALSE

Con el objetivo de evaluar para qué elementos de un vector una determinada expresión es TRUE, se puede usar la función which():

which(x > 3 & x <= 4)
#> [1] 2 3

Las funciones especializadas which.min() y which.max() están disponibles para calcular la posición del mínimo y el máximo. En adición a & y |, las funciones all() y any() comprueban si todas o al menos algunas entradas de un vector son TRUE:

all(x > 3)
#> [1] FALSE
any(x > 3)
#> [1] TRUE

Se necesita cierta precaución al evaluar la igualdad exacta. Cuando se aplica a una entrada numérica, == no permite la representación finita de fracciones o el error de redondeo; de ahí situaciones como:

(1.5 - 0.5) == 1
#> [1] TRUE
(1.9 - 0.9) == 1
#> [1] FALSE

El error anterior ocurre debido a la aritmética de punto flotante (Goldberg 1991). Para estos fines se recomienda hacer uso de la función all.equal():

all.equal(1.9 - 0.9, 1)
#> [1] TRUE

Además, la función identical() comprueba si dos objetos en R son exactamente idénticos.

Debido a la coerción, también es posible calcular directamente vectores lógicos utilizando aritmética ordinaria. Cuando se coacciona a numérico, FALSE se convierte en \(0\) y TRUE se convierte en \(1\), como en:

7 + TRUE
#> [1] 8

Coerción

Para convertir un objeto de un tipo o clase a otro diferente, R proporciona una serie de funciones de coerción, denominadas convencionalmente as.foo(), dónde foo es el tipo o clase deseada; por ejemplo, numérico (numeric), carácter (character), matriz (matrix), marco de datos (data.frame), entre muchos otros. Suelen ir acompañadas de un is.foo(), función que comprueba si un objeto es de un tipo o clase foo. Por tanto:

is.numeric(x)
#> [1] TRUE
is.character(x)
#> [1] FALSE
as.character(x)
#> [1] "1.8"    "3.14"   "4"      "88.169" "13"

En determinadas situaciones, la coersión también se ve forzada automáticamente por R; por ejemplo, cuando el usuario intenta poner elementos de diferentes clases en un solo vector (que solo puede contener elementos de la misma clase). Por ejemplo:

c(1, "a")
#> [1] "1" "a"

En este caso, el vector contiene un número y un caracter. No obstante, la coersión del carácter en R es mayor que la del número.

Generación de números aleatorios

Para los entornos de programación en estadística y econometría, es vital disponer de buenos generadores de números aleatorios (random number generators o RNG), en particular para permitir a los usuarios realizar estudios de Monte Carlo. El RNG de R admite varios algoritmos (se recomienda ver ?RNG para mas detalles). Aquí, se describen algunos comandos importantes.

El RNG se fundamenta en una “semilla aleatoria,” que es la base para la generación de números pseudoaleatorios. Configurando la semilla a un valor específico usando la función set.seed(), las simulaciones se pueden reproducir exactamente igual por otros usuarios. Por ejemplo, usando la función rnorm() para generar números aleatorios con una distribución normal:

set.seed(123)
rnorm(2)
#> [1] -0.5604756 -0.2301775

rnorm(2)
#> [1] 1.55870831 0.07050839

set.seed(123)
rnorm(2)
#> [1] -0.5604756 -0.2301775

Otra función básica para extraer muestras aleatorias, con o sin reemplazo de un conjunto finito de valores, es sample(). El valor predeterminado es draw (sin reemplazo), un vector del mismo tamaño que su argumento de entrada; es decir, para calcular una permutación de la entrada como en:

sample(1:5)
#> [1] 3 2 4 5 1
sample(c("masculino", "femenino"), size = 5, replace = TRUE, prob = c(0.2, 0.8))
#> [1] "femenino"  "masculino" "femenino"  "femenino"  "femenino"

El segundo comando extrae una muestra de tamaño \(5\), con reemplazo, de los valores “masculino” y “femenino,” que se escogen con probabilidades 0.2 y 0.8, respectivamente.

Arriba, ya se usó la función rnorm() para extraer de una distribución normal. Dicha función pertenece a una familia más amplia de funciones que son todas de la forma rdist(), dónde dist correspondiente a una familia distinta de distribución probabilística:

  • norm
  • unif
  • binom
  • pois
  • t
  • f
  • chisq

Todas las funciones enlistadas toman el tamaño de la muestra n como su primer argumento junto con otros argumentos que controlan los parámetros de la distribución respectiva. Por ejemplo, rnorm() toma mean (media) y sd (desviación estandar) como argumentos adicionales, con \(0\) y \(1\) como sus correspondientes valores predeterminados. Sin embargo, estas no son las únicas funciones disponibles para distribuciones estadísticas. Normalmente, también existen ddist(), pdist() y qdist(), que implementan la densidad, la función de distribución de probabilidad acumulada y la función de cuantiles (función de distribución inversa), respectivamente.

Control de flujo

Como la mayoría de los lenguajes de programación, R proporciona estructuras de control estándar como las declaraciones if/else, for (para bucles) y while (para bucles). Todas ellas tienen en común que una expresión expr se evalúa, ya sea condicional a una determinada cond (para if y while) o para una secuencia de valores (para for). La expresion expr en sí mismo puede ser una expresión simple o una expresión compuesta; es decir, típicamente un conjunto de expresiones simples encerradas entre llaves {…}. A continuación se presentan algunos breves ejemplos que ilustran su uso (se recomienda ver ?Control para mayor información):

Una declaración if/else es de la forma:

if(cond) {
    expr1
} else {
    expr2
}

Dónde expr1 se evalúa si cond es TRUE, de lo contrario se evalúa expr2. La rama else se puede omitir si está vacía. Un ejemplo simple (si no muy significativo) es:

x <- c(1.8, 3.14, 4, 88.169, 13)
if(rnorm(1) > 0) sum(x) else mean(x)
#> [1] 22.0218

Donde la condición es el valor de un número aleatorio normal estándar, y la evaluación calcula la suma o la media del vector x.

Tenga en cuenta que la condición cond solo puede ser de longitud \(1\). Sin embargo, también existe una función ifelse() ofreciendo una versión vectorizada; por ejemplo:

ifelse(x > 4, sqrt(x), x^2)
#> [1]  3.240000  9.859600 16.000000  9.389835  3.605551

Esto calcula la raíz cuadrada de los valores de x que son mayores que \(4\) y el cuadrado para los restantes.

El bucle for se construye de manera similar, pero el argumento principal para for() es de tipo variable en secuencia. Para ilustrar su uso, se calculan recursivamente las primeras diferencias en el vector x.

for(i in 2:5) {x[i] <- x[i] - x[i-1]}
x[-1]
#> [1]   1.340   2.660  85.509 -72.509

Finalmente, un bucle while se ve bastante similar. El argumento para while() es una condición que puede cambiar en cada ejecución del bucle para que finalmente pueda convertirse en FALSE, como en:

while(sum(x) < 100) {x <- 2 * x}
x
#> [1]   14.400   10.720   21.280  684.072 -580.072

Escritura de funciones

Una de las características de S y R es que los usuarios se convierten naturalmente en desarrolladores. En otras palabras, crean variables u objetos que aplican a funciones de forma interactiva (ya sea para modificarlos o para crear otros objetos de interés), lo que es parte de cada sesión en R. Al hacerlo, a menudo surgen secuencias típicas de comandos que se ejecutan para diferentes conjuntos de valores de entrada. En lugar de repetir los mismos pasos “a mano,” se pueden integrar fácilmente nuevas funciones. Un ejemplo simple es:

cmeans <- function(X) {
    rval <- rep(0, ncol(X))
    for(j in 1:ncol(X)) {
        mysum <- 0
        for(i in 1:nrow(X)) mysum <- mysum + X[i,j]
        rval[j] <- mysum/nrow(X)
        }
    return(rval)
}

Esto crea la función cmeans() (¡deliberadamente incómoda!), que toma un argumento de matriz X y usa un doble bucle for para calcular primero la suma y luego la media en cada columna. El resultado se almacena en un vector rval (el valor de return o valor de retorno), que se devuelve después de completar ambos ciclos. Esta función se puede aplicar fácilmente a nuevos datos, como en:

X <- matrix(1:20, ncol = 2)
cmeans(X)
#> [1]  5.5 15.5

Como era de esperar, produce el mismo resultado que la función incorporada colMeans():

colMeans(X)
#> [1]  5.5 15.5

La función cmeans() toma solo un argumento X que no tiene un valor predeterminado. Si el autor de una función desea establecer un valor predeterminado, esto se puede lograr fácilmente definiendo un función (function) con una lista de pares name = expr, donde name es el argumento de la variable y expr es una expresión con el valor predeterminado. Si se omite este último, no se establece ningún valor predeterminado.

En lenguajes interpretados basados en matrices, como lo es R, los bucles suelen ser menos eficientes que los correspondientes cálculos vectorizados ofrecidos por el sistema. Por lo tanto, evitar bucles reemplazándolos con operaciones vectorizadas puede ahorrar tiempo de cálculo, especialmente cuando el número de iteraciones en el bucle puede aumentar. Para ilustrar el punto anterior, se generan \(2 \times 10^6\) números aleatorios de la distribución normal estándar y se compara con la función incorporada colMeans() con la incómoda función cmeans(). Se emplea la función system.time(), que es útil para perfilar código:

X <- matrix(rnorm(2*10^6), ncol = 2)
system.time(colMeans(X))
#>    user  system elapsed 
#>       0       0       0
system.time(cmeans(X))
#>    user  system elapsed 
#>    0.16    0.00    0.15

Claramente, el desempeño de cmeans() es vergonzoso y usar colMeans() se prefiere.

Cálculos vectorizados

Como se señaló anteriormente, los bucles se pueden evitar utilizando aritmética vectorizada. En el caso de cmeans(), la función calcula las medias de una matriz por columnas, sería útil calcular directamente las medias columna por columna utilizando la función incorporada mean(). De hecho, esta es la solución preferida. Utilizando las herramientas de las que se dispone hasta el momento, se podría proceder de la siguiente manera:

cmeans2 <- function(X) {
    rval <- rep(0, ncol(X))
    for(j in 1:ncol(X)) rval[j] <- mean(X[,j])
    return(rval)
}

Esto elimina uno de los bucles for y solo recorre las columnas. El resultado es idéntico a las soluciones anteriores, pero el rendimiento es claramente mejor que el de cmeans():

system.time(cmeans2(X))
#>    user  system elapsed 
#>    0.02    0.00    0.01

Sin embargo, el código de cmeans2() todavía parece un poco engorroso con el resto de bucles for -se puede escribir de forma mucho más compacta con la función apply(). Esto aplica funciones sobre los márgenes de una matriz y toma tres argumentos:

  1. La matriz.
  2. El índice del margen.
  3. La función que se va a evaluar.

En este caso, la llamada a la función es:

apply(X, 2, mean)
#> [1] -0.0005206169 -0.0015577944

Porque se requieren las medias (usando la función mean()) sobre las columnas (es decir, la segunda dimensión) de X. El rendimiento de la función apply() a veces puede ser mejor que un bucle for. Sin embargo, en muchos casos ambos enfoques funcionan de manera bastante similar:

system.time(apply(X, 2, mean))
#>    user  system elapsed 
#>    0.03    0.00    0.03

Para resumir, lo anterior implica que:

  1. Los cálculos por elementos deben evitarse si existen cálculos vectorizados disponibles.
  2. Las soluciones optimizadas (si están disponibles) generalmente funcionan mejor que las soluciones genéricas for o apply().
  3. Los bucles se pueden escribir de forma más compacta utilizando la función apply().

De hecho, esto es tan común en R que existen varias alternativas a la función apply(); a saber, están disponibles las funciones lapply(), tapply(), y sapply(). El primero devuelve una lista, el segundo una tabla y el tercero intenta simplificar el resultado a un vector o matriz cuando sea posible (se recomienda consultar las páginas del manual correspondientes para obtener información más detallada y ejemplos).

Palabras reservadas

Como la mayoría de los lenguajes de programación, R tiene una serie de palabras reservadas que proporcionan las construcciones gramaticales básicas del idioma. Algunos de estos ya se han presentado anteriormente y algunos más se describen a continuación. Una lista casi completa de palabras reservadas en R se puede consultar mediante el comando ?Reserved. Algunas palabras clave que siempre debe tener en mente son:

  • if
  • else
  • for
  • in
  • while
  • repeat
  • break
  • next
  • function
  • TRUE
  • FALSE
  • NA
  • NULL
  • Inf
  • NaN

Si se intenta utilizar alguno de estos nombres para designar variables o cualquier tipo de objeto, se producirá un error.