Tema 6 Tipos de datos: matrices
En este tema seguimos analizando los principales tipos de datos de R, estudiando las matrices. Las matrices son un tipo de dato que implementa el concepto matemático del mismo nombre. En R, una matriz es un vector que tiene asociado dos dimensiones. Todos los elementos de una matriz deben ser del mismo tipo.
Las matrices se implementan como ilustra la Figura 6.1. La memoria del ordenador es lineal. Por lo tanto, los elementos de una matriz se ubican en posiciones contiguas de memoria columna a columna (en otros lenguajes de programación es fila a fila). Como todos los elementos de una matriz tienen que ser del mismo tipo, ocupan el mismo tamaño en la memoria y, por lo tanto, guardando la dirección de inicio de la matriz es fácil y eficiente acceder a cualquiera de sus elementos dado su número de fila y columna. Por ejemplo, la figura ilustra el pequeño cálculo aritmético realizado para obtener la dirección de memoria donde se almacena el elemento de la matriz ubicado en la fila 2, columna 1. Como R almacena los elementos de las matrices por columnas, siempre que tenga que recorrer todos sus elementos lo hará por columnas en lugar de por filas, porque es más eficiente. En concreto, la creación de matrices que se ve a continuación se realiza por defecto por columnas.

Podemos crear una matriz con la función matrix
:
El primer parámetro es un vector e indica los elementos de la matriz. nrow
sirve para especificar el número de filas y ncol
el número de columnas. Si a partir de uno de los valores de nrow
o ncol
se puede deducir el otro, no es preciso especificar ambos:
matrix(c(2, 3, 6, 5), nrow = 2)
## [,1] [,2]
## [1,] 2 6
## [2,] 3 5
matrix(c(2, 3, 6, 5), ncol = 2)
## [,1] [,2]
## [1,] 2 6
## [2,] 3 5
Por defecto los elementos del vector se almacenan por columnas, pero podemos usar el parámetro byrow
para que se guarden por filas:
matrix(1:20, nrow = 2)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,] 1 3 5 7 9 11 13 15 17 19
## [2,] 2 4 6 8 10 12 14 16 18 20
matrix(1:20, nrow = 2, byrow = TRUE)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
## [1,] 1 2 3 4 5 6 7 8 9 10
## [2,] 11 12 13 14 15 16 17 18 19 20
Si el vector que especifica los elementos tiene un tamaño inferior al de la matriz, los elementos del vector se reciclan. Esto es útil para crear una matriz en la que todos sus elementos tienen el mismo valor:
Podemos cambiar el atributo dim
de un vector para convertirlo en una matriz:
(v <- c(2, 5, 1.1, 10)) # v es un vector
## [1] 2.0 5.0 1.1 10.0
dim(v) <- c(2, 2) # cambia v de vector a matriz
v
## [,1] [,2]
## [1,] 2 1.1
## [2,] 5 10.0
Ejercicio. Crea una matriz 8x8 rellena con un patrón de tablero de ajedrez (utiliza 0 y 1).
col1 <- rep(0:1, 4)
col2 <- rep(1:0, 4)
matrix(c(col1, col2), nrow = 8, ncol = 8)
## [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
## [1,] 0 1 0 1 0 1 0 1
## [2,] 1 0 1 0 1 0 1 0
## [3,] 0 1 0 1 0 1 0 1
## [4,] 1 0 1 0 1 0 1 0
## [5,] 0 1 0 1 0 1 0 1
## [6,] 1 0 1 0 1 0 1 0
## [7,] 0 1 0 1 0 1 0 1
## [8,] 1 0 1 0 1 0 1 0
6.1 Consultar el tamaño de una matriz
La función length
indica cuántos elementos tiene una matriz, la función dim
cuántos elementos hay en cada dimensión, nrow
el número de filas y ncol
el número de columnas.
6.2 Filas y columnas con nombres
Para algunas matrices puede resultar útil que las distintas filas y/o columnas tengan asociado un nombre. Los nombres se pueden especificar al crear la matriz con el parámetro dimnames
de la función matrix
. También de esta forma:
notas <- matrix(runif(9, 1, 10), nrow = 3)
rownames(notas) <- c("Juan", "María", "Jaime")
colnames(notas) <- paste("Examen", 1:3)
notas
## Examen 1 Examen 2 Examen 3
## Juan 7.808117 8.353483 5.887392
## María 7.717274 1.646936 9.760126
## Jaime 2.253255 5.516627 1.508032
Los nombres se especifican mediante un vector de cadenas de caracteres. Es posible consultar los nombres de una dimensión o incluso anularlos:
6.3 Aritmética de matrices
Al igual que ocurre con los vectores, los operadores aritméticos (+
, -
, /
, ^
, …) son aplicables a matrices numéricas y trabajan elemento a elemento, reciclando si es necesario:
(m1 <- matrix(10, nrow = 2, ncol = 2))
## [,1] [,2]
## [1,] 10 10
## [2,] 10 10
(m2 <- matrix(1:4, nrow = 2))
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
m1 - m2^2 + 3
## [,1] [,2]
## [1,] 12 4
## [2,] 9 -3
(m3 <- matrix(rnorm(10), nrow = 2))
## [,1] [,2] [,3] [,4] [,5]
## [1,] -0.04650393 0.071325 -1.1570084 0.08306695 -0.1263636
## [2,] 0.16674280 1.628937 -0.4157481 1.14650415 -0.4490542
abs(m3) # aplica la función absoluto a todos los elementos de m3
## [,1] [,2] [,3] [,4] [,5]
## [1,] 0.04650393 0.071325 1.1570084 0.08306695 0.1263636
## [2,] 0.16674280 1.628937 0.4157481 1.14650415 0.4490542
El producto clásico de matrices se realiza con el operador %*%
:
6.4 Funciones aplicables a matrices
Existen muchas funciones que aplican una operación al conjunto de los elementos de una matriz. Son tantas que no es posible enumerarlas todas, pero ten presente que si quieres hacer un procesamiento típico con matrices es muy probable que esté implementado en alguna función de R.
Por ejemplo, mean
calcula la media de los elementos de una matriz. También se puede calcular la media de las distintas columnas o filas con colMeans
y rowMeans
respectivamente.
m <- matrix(sample(4, size = 9, replace = TRUE), nrow = 3)
m
## [,1] [,2] [,3]
## [1,] 2 3 4
## [2,] 1 3 1
## [3,] 2 4 4
mean(m) # media de todos los elementos
## [1] 2.666667
rowMeans(m) # media de las filas
## [1] 3.000000 1.666667 3.333333
colMeans(m) # media de las columnas
## [1] 1.666667 3.333333 3.000000
sum
, colSums
y rowSums
son análogas, pero suman los elementos de una matriz. Hay funciones, como median
, que pueden aplicarse al conjunto formado por todos los elementos de una matriz, pero no existen versiones para aplicar el procesamiento por filas o columnas. En caso de que tengamos la necesidad de aplicar una función a las filas o columnas de una matriz se puede usar la función apply
:
(m <- matrix(sample(4, size = 9, replace = TRUE), nrow = 3))
## [,1] [,2] [,3]
## [1,] 3 1 4
## [2,] 1 4 4
## [3,] 3 1 2
rowSums(m)
## [1] 8 9 6
apply(m, 1, sum) # mismos efectos que rowSums
## [1] 8 9 6
colSums(m)
## [1] 7 6 10
apply(m, 2, sum) # mismos efectos que colSums
## [1] 7 6 10
La sintaxis de apply
es apply(matriz, dimensión, función)
y el efecto es aplicar la función indicada en el tercer parámetro a la matriz especificada en el primer parámetro en una dimensión. Si dimensión es 1 se aplica a las filas y si es 2 a las columnas. En caso de que la invocación a la función requiera argumentos adicionales, estos pueden ser indicados después del argumento que especifica la función:
(m <- matrix(sample(c(1:5, NA), size = 12, replace = TRUE), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 4 NA 2 3
## [2,] NA 3 3 5
## [3,] 4 4 5 4
apply(m, 2, median) # mediana por columnas
## [1] NA NA 3 4
apply(m, 2, median, na.rm = TRUE)
## [1] 4.0 3.5 3.0 4.0
En la segunda llamada a apply
el parámetro na.rm
se le pasa a la función median
.
Ejemplo. Vamos a realizar una simulación para obtener una aproximación de la función masa de probabilidad de la variable aleatoria máximo del lanzamiento de 2 dados. Esta función es fácil de calcular sin realizar una simulación. La probabilidad de que la variable valga 1 es \(\frac{1}{36}\), la probabilidad de que valga 2 es \(\frac{3}{36}\), la probabilidad de que valga 3 es \(\frac{5}{6}\), etcétera. Para realizar la simulación vamos a lanzar 2 dados un millón de veces, vamos a calcular el máximo de cada tirada de dos dados y luego calcularemos la proporción de esos máximos. Usaremos una matriz para guardar las tiradas, la matriz tiene 2 filas y un millón de columnas y cada columna representa el lanzamiento de dos dados.
m <- matrix(sample(6, size = 2e6, replace = TRUE), nrow = 2)
m[, 1:5] # ver 5 primeras tiradas
## [,1] [,2] [,3] [,4] [,5]
## [1,] 6 1 1 1 6
## [2,] 3 6 6 2 6
va <- apply(m, 2, max) # máximo por columnas: 1e6 var. aleatorias
prop.table(table(va)) # estimación
## va
## 1 2 3 4 5 6
## 0.027668 0.083708 0.138061 0.195239 0.249521 0.305803
c(1, 3, 5, 7, 9, 11)/36 # probabilidades reales
## [1] 0.02777778 0.08333333 0.13888889 0.19444444 0.25000000 0.30555556
Ejercicio. Haz que todas las columnas de una matriz m
aparezcan ordenadas en orden creciente. Sugerencia: usa apply
y sort
.
set.seed(1)
(m <- matrix(sample(100, size = 12), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 68 34 14 51
## [2,] 39 87 82 85
## [3,] 1 43 59 21
apply(m, 2, sort)
## [,1] [,2] [,3] [,4]
## [1,] 1 34 14 21
## [2,] 39 43 59 51
## [3,] 68 87 82 85
Cuando se aplica apply
por filas y el resultado de la función aplicada es un vector, la matriz de salida se rellena por columnas:
set.seed(1); (m <- matrix(sample(100, size = 12), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 68 34 14 51
## [2,] 39 87 82 85
## [3,] 1 43 59 21
apply(m, 1, sort)
## [,1] [,2] [,3]
## [1,] 14 39 1
## [2,] 34 82 21
## [3,] 51 85 43
## [4,] 68 87 59
Observa que la primera fila ordenada es la primera columna de la matriz que genera
apply
, la segunda fila ordenada la segunda columna y así sucesivamente. Si queremos
la salida por filas, se puede calcular la traspuesta:
set.seed(1); (m <- matrix(sample(100, size = 12), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 68 34 14 51
## [2,] 39 87 82 85
## [3,] 1 43 59 21
t(apply(m, 1, sort))
## [,1] [,2] [,3] [,4]
## [1,] 14 34 51 68
## [2,] 39 82 85 87
## [3,] 1 21 43 59
La función apply
también puede usarse con funciones definidas por el usuario.
Aunque la definición de funciones se verá más adelante, vamos a estudiar un ejemplo.
Supongamos que queremos restarle a cada elemento de una columna el mínimo de
esa columna.
set.seed(5)
(m <- matrix(sample(10, size = 6), nrow = 2))
## [,1] [,2] [,3]
## [1,] 2 7 1
## [2,] 9 3 6
apply(m, 2, function(col) col - min(col))
## [,1] [,2] [,3]
## [1,] 0 4 0
## [2,] 7 0 5
La función definida en la invocación a apply
recibe como parámetro un vector
(con una columna de la matriz) y realiza el cálculo col - min(col)
, es decir,
le resta a cada elemento del vector el mínimo del vector.
Vamos a seguir describiendo algunas funciones de R que trabajan con matrices. var
calcula la matriz de covarianzas:
m <- matrix(rnorm(1e4, mean = 0, sd = 2), nrow = 2500)
var(m)
## [,1] [,2] [,3] [,4]
## [1,] 4.00528998 0.01746979 -0.06435747 0.04593452
## [2,] 0.01746979 4.00425351 0.01896810 0.06651531
## [3,] -0.06435747 0.01896810 3.95432971 0.02343102
## [4,] 0.04593452 0.06651531 0.02343102 3.96734850
var
considera cada columna de la matriz como una muestra. Observa la salida: en la diagonal aparece la varianza de cada muestra y en la posición \((x, y)\) la covarianza entre las muestras de las columnas \(x\) e \(y\). En el ejemplo las varianzas tienen un valor cercano a 4 (son muestras de una \(\mathcal{N}(0, 2)\)) y las covarianzas son casi 0 (pues las muestras son independientes entre sí).
La función t
calcula la traspuesta:
(m <- matrix(1:15, ncol = 5, byrow = TRUE))
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 2 3 4 5
## [2,] 6 7 8 9 10
## [3,] 11 12 13 14 15
t(m)
## [,1] [,2] [,3]
## [1,] 1 6 11
## [2,] 2 7 12
## [3,] 3 8 13
## [4,] 4 9 14
## [5,] 5 10 15
La función diag
extrae la diagonal de una matriz:
(m <- matrix(1:9, ncol = 3, byrow = TRUE))
## [,1] [,2] [,3]
## [1,] 1 2 3
## [2,] 4 5 6
## [3,] 7 8 9
diag(m)
## [1] 1 5 9
diag(m) <- 0
m
## [,1] [,2] [,3]
## [1,] 0 2 3
## [2,] 4 0 6
## [3,] 7 8 0
La función diag
aplicada a un vector crea una matriz con dicho vector como diagonal:
Las funciones det
y determinant
calculan el determinante de una matriz. La función solve
resuelve un sistema de ecuaciones lineales. Por ejemplo, supongamos que queremos saber dónde intersectan las rectas \(2x + y -3\) y \(5x + 4y -7\):
Vamos a comprobar gráficamente que el cálculo es correcto:
plot(solve(m, b)[1], solve(m, b)[2], pch = 19)
abline(3, -2, col = "blue")
abline(7/4, -5/4, col = "red")
6.5 Matrices lógicas
Al igual que ocurre con los vectores, se puede aplicar operadores relacionales para crear matrices lógicas.
(m <- matrix(runif(8), nrow = 2))
## [,1] [,2] [,3] [,4]
## [1,] 0.9582220 0.6041567 0.9787058 0.5667330
## [2,] 0.3893406 0.7511718 0.3811577 0.3753636
m >= 0.75
## [,1] [,2] [,3] [,4]
## [1,] TRUE FALSE TRUE FALSE
## [2,] FALSE TRUE FALSE FALSE
Podemos contar y calcular la proporción de los elementos de una matriz que verifican una condición:
sum(m >= 0.75) # ¿Cuántos elementos de m son >= 0.75?
## [1] 3
mean(m >= 0.75) # ¿Y la proporción?
## [1] 0.375
También podemos aplicar los operadores lógicos (&
, |
y !
) a matrices lógicas, así como las funciones any
y all
, de la misma forma que se estudió con los vectores.
# Se simula lanzar 4 veces dos dados
(m <- matrix(sample(6, size = 8, replace = TRUE), nrow = 2))
## [,1] [,2] [,3] [,4]
## [1,] 1 3 6 4
## [2,] 5 2 3 3
sum(m == 5 | m == 3) # ¿Cuántas veces sale el 3 o el 5?
## [1] 4
any(m == 6) # ¿Ha salido el 6 al menos una vez?
## [1] TRUE
all(m != 1) # ¿No ha salido el 1?
## [1] FALSE
Ejemplo. Una bolsa contiene 3 bolas rojas y 5 negras. Si extraemos 3 bolas de la bolsa al azar, ¿cuál es la probabilidad de que 2 de ellas sean negras y la otra roja? Esta probabilidad es:
\[ \frac{\binom{5}{2}\binom{3}{1}}{\binom{8}{3}} \]
que se puede calcular en R así:
Vamos a realizar una simulación para estimar esta probabilidad: repetiremos el experimento aleatorio de extraer 3 bolas al azar un millón de veces y calcularemos en qué proporción de los experimentos había 2 bolas negras y una roja. Para ello vamos a utilizar la función replicate
, que permite ejecutar una expresión varias veces:
bolsa <- rep(c("r", "n"), times = c(3, 5)) # simula la bolsa
# simula un millón de extracciones
s <- replicate(1e6, sample(bolsa, size = 3))
Como la expresión usada en replicate
(sample(bolsa, size = 3)
) genera un vector de tamaño 3, el resultado de cada ejecucion se almacena en una columna distinta de una matriz de salida, cuyo tamaño es \(3x10^6\). Es decir, cada columna de la matriz de salida es un experimento. Ahora calculamos la proporción de experimentos en los que ha ocurrido el evento:
s <- colSums(s == "r")
mean(s == 1) # probabilidad estimada
## [1] 0.536005
choose(5, 2) * choose(3, 1) / choose(8, 3) # probabilidad real
## [1] 0.5357143
La primera línea convierte a s
, la matriz de salida, en un vector. Cada elemento del vector indica cuántas bolas rojas hay en ese experimento. La segunda línea calcula la proporción de experimentos en los que ha salido exactamente una bola roja y, por tanto, dos negras. O sea, la segunda línea calcula la probabilidad estimada.
La función replicate
nos ofrece una alternativa para realizar simulaciones. Por ejemplo, supongamos que queremos simular el lanzamiento de dos dados 5 veces
matrix(sample(6, size = 10, replace = TRUE), nrow = 2)
## [,1] [,2] [,3] [,4] [,5]
## [1,] 5 3 1 3 4
## [2,] 6 3 2 5 1
# alternativa usando replicate
replicate(5, sample(6, size = 2, replace = TRUE))
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 3 1 5 1
## [2,] 4 3 6 5 4
Cuando se quiere repetir un experimento sin reemplazo, entonces la solución es usar replicate
, como, por ejemplo, en el ejemplo previo de extraer al azar 3 bolas de una urna con 8 bolas varias veces.
6.6 Indexación de matrices
La indexación de matrices es similar a la de los vectores, pero es preciso indicar los índices de las dos dimensiones separados por una coma. Si alguna dimensión se deja vacía se seleccionan todos los elementos de dicha dimensión. Vamos a estudiar los 6 tipos de indexación que admiten las matrices. Los cuatro primeros son análogos a los usados en los vectores. Hay que tener en cuenta que con las matrices hay que especificar índices para las filas y para las columnas, con lo que se puede mezclar dos tipos de indexación, por ejemplo, indexar las filas con un vector lógico y las columnas con un vector numérico. En casi todos los ejemplos de esta sección las matrices son indexadas para consultar sus valores, pero también es posible indexar una matriz para modificarla.
6.6.1 Mediante un vector de enteros positivos
Se seleccionan los índices indicados en el vector:
(m <- matrix(1:16, nrow = 4))
## [,1] [,2] [,3] [,4]
## [1,] 1 5 9 13
## [2,] 2 6 10 14
## [3,] 3 7 11 15
## [4,] 4 8 12 16
m[c(1, 3), 3:4] # filas 1 y 3, columnas 3 y 4
## [,1] [,2]
## [1,] 9 13
## [2,] 11 15
m[2, 2] # fila 2, columna 2
## [1] 6
m[3:4, ] # filas 3 y 4 (todas las columnas)
## [,1] [,2] [,3] [,4]
## [1,] 3 7 11 15
## [2,] 4 8 12 16
6.6.2 Mediante un vector de enteros negativos
El vector indica los índices no seleccionados, en lugar de los seleccionados:
(m <- matrix(1:16, nrow = 4))
## [,1] [,2] [,3] [,4]
## [1,] 1 5 9 13
## [2,] 2 6 10 14
## [3,] 3 7 11 15
## [4,] 4 8 12 16
m[, -1] # todas las columnas, salvo la primera
## [,1] [,2] [,3]
## [1,] 5 9 13
## [2,] 6 10 14
## [3,] 7 11 15
## [4,] 8 12 16
m[1:2, -c(1, ncol(m))] # filas 1 y 2. Todas las columnas salvo la primera y la última
## [,1] [,2]
## [1,] 5 9
## [2,] 6 10
6.6.3 Mediante valores lógicos
Es muy útil, pues permite filtrar aquellas filas o columnas que verifiquen una condición. Por ejemplo:
(m <- matrix(1:16, nrow = 4))
## [,1] [,2] [,3] [,4]
## [1,] 1 5 9 13
## [2,] 2 6 10 14
## [3,] 3 7 11 15
## [4,] 4 8 12 16
m[m[, 1] %% 2 == 0, ]
## [,1] [,2] [,3] [,4]
## [1,] 2 6 10 14
## [2,] 4 8 12 16
Selecciona las filas en las que la columna 1 tiene un valor par. Veamos otro ejemplo con una condición un poco más compleja: vamos a seleccionar aquellas columnas de una matriz que no tienen valores NA
.
(m <- matrix(sample(c(NA, 1:3), size = 12, replace = TRUE), nrow = 2))
## [,1] [,2] [,3] [,4] [,5] [,6]
## [1,] 2 1 2 1 NA 2
## [2,] 2 2 2 1 3 NA
condicion <- apply(is.na(m), 2, any)
m[, !condicion]
## [,1] [,2] [,3] [,4]
## [1,] 2 1 2 1
## [2,] 2 2 2 1
También se puede usar una matriz lógica para indexar. Por ejemplo:
(m <- matrix(sample(c(1:3, NA), size = 12, replace = TRUE), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 3 NA 1 1
## [2,] 3 1 NA NA
## [3,] 2 2 2 NA
is.na(m) # produce una matriz lógica
## [,1] [,2] [,3] [,4]
## [1,] FALSE TRUE FALSE FALSE
## [2,] FALSE FALSE TRUE TRUE
## [3,] FALSE FALSE FALSE TRUE
m[is.na(m)] <- 0 # indexa con una matriz lógica
m
## [,1] [,2] [,3] [,4]
## [1,] 3 0 1 1
## [2,] 3 1 0 0
## [3,] 2 2 2 0
6.6.4 Mediante un vector de cadenas de caracteres
Para usar esta posibilidad las filas y/o columnas deben tener nombres asociados:
notas <- matrix(runif(9, 1, 10), nrow = 3)
rownames(notas) <- c("Juan", "María", "Jaime")
colnames(notas) <- paste("Examen", 1:3)
notas
## Examen 1 Examen 2 Examen 3
## Juan 7.821840 2.014123 4.571020
## María 9.860010 4.396650 5.968692
## Jaime 4.693878 2.406851 7.745703
notas[, "Examen 3"] # sólo el examen 3
## Juan María Jaime
## 4.571020 5.968692 7.745703
notas[c("Juan", "Jaime"), ]
## Examen 1 Examen 2 Examen 3
## Juan 7.821840 2.014123 4.571020
## Jaime 4.693878 2.406851 7.745703
Observa que en el primer ejemplo, al seleccionarse una única columna, R devuelve un vector en lugar de una matriz. Más adelante comentaremos cómo podemos hacer que R devuelva una matriz con una única columna.
6.6.5 Indexación lineal
Aunque no se use habitualmente, una matriz puede ser indexada con un único vector de índices. El resultado de esta indexación es un vector con los elementos de la matriz que ocupan los índices lineales indicados. Los índices lineales hacen referencia al vector asociado a la matriz, éste se puede obtener con la función c
.
(m <- matrix(sample(10, 6), nrow = 2))
## [,1] [,2] [,3]
## [1,] 9 7 5
## [2,] 3 1 10
c(m) # vector asociado a m
## [1] 9 3 7 1 5 10
Como vemos, el vector asociado se construye concatenando las columnas de la matriz, y es a este vector al que se le aplica la indexación lineal:
6.7 Indexado que genera una única fila o columna
Si al indexar una matriz el resultado tiene una única fila o columna, R, por defecto, devuelve un vector con la fila o columna seleccionada, en lugar de devolver una matriz. Este comportamiento por defecto no es quizás lo más indicado. En el siguiente código se ejemplifica cómo cambiar el comportamiento por defecto, para poder obtener una matriz:
(m <- matrix(1:10, nrow = 2))
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 3 5 7 9
## [2,] 2 4 6 8 10
m[1, ] # fila 1, se obtiene un vector
## [1] 1 3 5 7 9
m[, 2] # columna 2, se obtiene un vector
## [1] 3 4
m[1, , drop = FALSE] # se obtiene una matriz
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 3 5 7 9
m[, 2, drop = FALSE] # se obtiene una matriz
## [,1]
## [1,] 3
## [2,] 4
6.8 Concatenación de matrices
Se puede concatenar verticalmente matrices con el mismo número de columnas con rbind
. Para concatenar horizontalmente matrices con el mismo número de filas se usa la función cbind
:
(m1 <- matrix(c(1, 1, 2, 2), nrow = 2))
## [,1] [,2]
## [1,] 1 2
## [2,] 1 2
(m2 <- m1 ^ 2)
## [,1] [,2]
## [1,] 1 4
## [2,] 1 4
cbind(m1, m2) # cbind concatena las columnas
## [,1] [,2] [,3] [,4]
## [1,] 1 2 1 4
## [2,] 1 2 1 4
rbind(m1, m1 * 3) # rbind concatena las filas
## [,1] [,2]
## [1,] 1 2
## [2,] 1 2
## [3,] 3 6
## [4,] 3 6
Las funciones cbind
y rbind
admiten el reciclado:
(m <- matrix(1:6, nrow = 3))
## [,1] [,2]
## [1,] 1 4
## [2,] 2 5
## [3,] 3 6
cbind(m , 0) # añade una columna de ceros
## [,1] [,2] [,3]
## [1,] 1 4 0
## [2,] 2 5 0
## [3,] 3 6 0
También se puede usar rbind
y cbind
para crear una matriz concatenando vectores:
6.9 Arrays
Los vectores y matrices son un caso particular de arrays. Los vectores son arrays de una dimensión y las matrices de dos dimensiones. Dado que los arrays de 3 o más dimensiones se utilizan con poca frecuencia no los vamos a cubrir en estos apuntes. Sólo vamos a ver un ejemplo. Supongamos que queremos almacenar las 3 notas de n alumnos en 2 semestres. Esto se puede almacenar de una forma ordenada en un array tridimensional nx3x2:
n <- 5
notas <- array(runif(n*3*2, max = 10), dim = c(n, 3, 2))
notas
## , , 1
##
## [,1] [,2] [,3]
## [1,] 2.025852 3.481609 2.501262
## [2,] 6.475442 1.413403 4.852748
## [3,] 5.689355 4.902850 1.078265
## [4,] 2.932403 7.832303 8.690019
## [5,] 7.226437 3.107789 6.202055
##
## , , 2
##
## [,1] [,2] [,3]
## [1,] 3.33405509 3.33707212 0.991434
## [2,] 3.99848249 1.67320594 5.687474
## [3,] 9.37977419 0.67608627 4.656393
## [4,] 4.29522662 0.06766415 4.145739
## [5,] 0.06464175 1.37047550 2.071032
notas[, , 1] # notas del primer semestre
## [,1] [,2] [,3]
## [1,] 2.025852 3.481609 2.501262
## [2,] 6.475442 1.413403 4.852748
## [3,] 5.689355 4.902850 1.078265
## [4,] 2.932403 7.832303 8.690019
## [5,] 7.226437 3.107789 6.202055
notas[1, , 2] # notas del alumno 1 en el segundo semestre
## [1] 3.334055 3.337072 0.991434
6.10 Ejercicios
Crea una matriz 5x5 cuya primera fila sean unos, la segunda doses y así sucesivamente.
Crea la matriz
matrix(sample(100, size = 12), nrow = 3)
y haz que todas las columnas aparezcan ordenadas.Crea una matriz con
matrix(rnorm(15), nrow = 5)
. Selecciona las filas de la matriz cuya suma es positiva.Haz un pequeño programa que solicite un entero y cree una matriz de tres columnas con su tabla de multiplicar. Por ejemplo, si se hubiera introducido el 3 se crearía la matriz (se muestran sólo las tres primeras filas):
3 1 3
3 2 6
3 3 9
Dada una matriz, calcula el índice de la fila cuya suma de elementos es mayor. Sugerencia: usa
apply
(orowSums
) y la funciónwhich.max
, que devuelve el índice del primer máximo de un vector numérico.Una matriz cuadrada es simétrica si es igual a su traspuesta. Escribe una expresión que dada una matriz
m
genere un valor lógico indicando sim
es simétrica. Nota: puedes usar la funciónall
.Dada la matriz obtenida con
matrix(rnorm(10), nrow = 2)
, genera una expresión para calcular la cantidad de valores mayores que 0 en la matriz y otra expresión para determinar si hay más valores positivos que negativos en la matriz.Cada fila de una matriz representa el resultado de un alumno en 3 exámenes:
set.seed(2) (m <- matrix(sample(1:10, size = 12, replace = TRUE), nrow = 4)) ## [,1] [,2] [,3] ## [1,] 5 1 1 ## [2,] 6 1 3 ## [3,] 6 9 6 ## [4,] 8 2 2
Calcula la nota final de los alumnos si la ponderación de los exámenes es 0.2, 0.3 y 0.5 respectivamente.
Dada la matriz:
Selecciona las filas de la matriz cuya suma es positiva.
Dada una matriz, añádele una columna con la suma de los elementos de cada fila de la matriz original.
Dada una matriz, obtén otra matriz formada por una permutación de sus filas.
6.11 Soluciones a los ejercicios de matrices
# Matriz con 1 en primera filas, 2 en segunda, ...
matrix(1:5, nrow = 5, ncol = 5) # usando reciclado
## [,1] [,2] [,3] [,4] [,5]
## [1,] 1 1 1 1 1
## [2,] 2 2 2 2 2
## [3,] 3 3 3 3 3
## [4,] 4 4 4 4 4
## [5,] 5 5 5 5 5
# Ordenar las columnas de una matriz
(m <- matrix(sample(100, size = 12), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 90 56 79 46
## [2,] 51 92 23 18
## [3,] 54 13 98 80
apply(m, 2, sort)
## [,1] [,2] [,3] [,4]
## [1,] 51 13 23 18
## [2,] 54 56 79 46
## [3,] 90 92 98 80
# Filtrar filas cuya suma es positiva
(m <- matrix(rnorm(15), nrow = 5))
## [,1] [,2] [,3]
## [1,] 0.1251427 -0.3024286 1.2373051
## [2,] -1.4869821 -0.6511225 -1.0054358
## [3,] 0.3392875 -1.0966331 0.9421038
## [4,] 0.1668925 -0.9394679 0.5749584
## [5,] 1.9752061 2.3021163 0.9759743
m[rowSums(m) > 0, ]
## [,1] [,2] [,3]
## [1,] 0.1251427 -0.3024286 1.2373051
## [2,] 0.3392875 -1.0966331 0.9421038
## [3,] 1.9752061 2.3021163 0.9759743
# Tabla de multiplicar
n <- as.integer(readline("Número: "))
matrix(c(rep(n, 10), 1:10, 1:10 * n), nrow = 10)
cbind(n, 1:10, 1:10 * n) # otra posibilidad
# Índice de la fila cuya suma es mayor
(m <- matrix(sample(100, size = 12), nrow = 3))
## [,1] [,2] [,3] [,4]
## [1,] 76 97 20 4
## [2,] 60 15 11 3
## [3,] 90 81 9 28
which.max(rowSums(m))
## [1] 3
# Comprobar si una matriz es simétrica
(m <- matrix(c(1, 2, 4, 2, 1, 6, 4, 6, 7), nrow = 3))
## [,1] [,2] [,3]
## [1,] 1 2 4
## [2,] 2 1 6
## [3,] 4 6 7
ncol(m) == nrow(m) && all(m == t(m))
## [1] TRUE
m[1, 3] <- 0
ncol(m) == nrow(m) && all(m == t(m))
## [1] FALSE
# Contar valores que verifican una condición
(m <- matrix(rnorm(10), nrow = 2))
## [,1] [,2] [,3] [,4] [,5]
## [1,] -0.4602446 -0.06921116 0.1877261 -0.5918348 -0.9249531
## [2,] -0.7243285 1.46324856 1.0220229 -0.1122007 0.7533048
sum(m > 0) # cantidad de valores mayores que 0
## [1] 4
sum(m > 0) > sum(m < 0) # ¿más valores positivos que negativos?
## [1] FALSE
# Calcular la nota final ponderada
set.seed(2)
(m <- matrix(sample(1:10, size = 12, replace = TRUE), nrow = 4))
## [,1] [,2] [,3]
## [1,] 5 1 1
## [2,] 6 1 3
## [3,] 6 9 6
## [4,] 8 2 2
m[, 1] * 0.2 + m [, 2] * 0.3 + m[, 3] * 0.5
## [1] 1.8 3.0 6.9 3.2
c(0.2, 0.3, 0.5) %*% t(m) # otra posibilidad, usando producto de matrices
## [,1] [,2] [,3] [,4]
## [1,] 1.8 3 6.9 3.2
# Seleccionar las filas de una matriz cuya suma es positiva
set.seed(5)
(m <- matrix(rnorm(15), nrow = 5))
## [,1] [,2] [,3]
## [1,] -0.84085548 -0.6029080 1.2276303
## [2,] 1.38435934 -0.4721664 -0.8017795
## [3,] -1.25549186 -0.6353713 -1.0803926
## [4,] 0.07014277 -0.2857736 -0.1575344
## [5,] 1.71144087 0.1381082 -1.0717600
m[rowSums(m) > 0, ]
## [,1] [,2] [,3]
## [1,] 1.384359 -0.4721664 -0.8017795
## [2,] 1.711441 0.1381082 -1.0717600
# Añadir una columna con la suma de las filas
(m <- matrix(sample(0:1, size = 9, replace = TRUE), nrow = 3))
## [,1] [,2] [,3]
## [1,] 1 1 1
## [2,] 0 1 0
## [3,] 0 1 0
m <- cbind(m, rowSums(m))
m
## [,1] [,2] [,3] [,4]
## [1,] 1 1 1 3
## [2,] 0 1 0 1
## [3,] 0 1 0 1
# La función addmargins sirve para este propósito
(m <- matrix(sample(0:1, size = 9, replace = TRUE), nrow = 3))
## [,1] [,2] [,3]
## [1,] 0 1 1
## [2,] 0 1 0
## [3,] 1 1 1
addmargins(m)
## Sum
## 0 1 1 2
## 0 1 0 1
## 1 1 1 3
## Sum 1 3 2 6