Percursos
/
R
R
/
Programa
/
Matrizes e arrays
Ma

Matrizes e arrays em R

{one: "1 exercício", many: "%{count} exercícios", other: "%{count} exercícios"}

Sobre Matrizes e arrays

Quando trabalhas com coleções de dados atómicos homogéneos (por exemplo, todos numéricos ou todos de caráter), estão disponíveis em R as seguintes estruturas:

  • vector para dados unidimensionais, acedidos com um único índice.
  • matrix para dados bidimensionais retangulares (não irregulares).
  • array para o caso geral de N dimensões. Uma matrix é apenas um array com N=2.
Note

Para quem programa em Python: Esta secção pode parecer familiar.

Qualquer semelhança com o NumPy não é coincidência (mas o R é um pouco mais antigo e já tem tudo isto na linguagem base).

Matrizes

Uma matriz é essencialmente apenas um vetor, mais o número de linhas e de colunas para definir a forma.

Existe uma função matrix() para as criar:

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

# empty matrix to be filled in later
matrix(nrow=2, ncol=3)
#>      [,1] [,2] [,3]
#> [1,]   NA   NA   NA
#> [2,]   NA   NA   NA

Repara que os dados são armazenados em ordem por colunas (ou seja, primeiro para baixo, depois para o lado). Isto é o mesmo que em Fortran, Matlab e Julia, mas é diferente de C/C++ e NumPy.

Descobre as dimensões de uma matriz com dim() para obter linhas e colunas, ou com nrow() ou ncol() para obter uma única dimensão. Usar length() devolve apenas o comprimento do vetor subjacente: o número total de elementos (6 no exemplo acima).

Algumas outras funções produzem resultados em forma de matriz, por exemplo str_split_fixed().

library(stringr)

str_split_fixed(c("split some words", "and some more"), " ", 3)
#>      [,1]    [,2]   [,3]   
#> [1,] "split" "some" "words"
#> [2,] "and"   "some" "more" 

Indexação de matrizes

Isto funciona exatamente da mesma forma que a indexação de vetores, exceto pela necessidade de especificar a(s) linha(s) e a(s) coluna(s), por essa ordem. Deixa uma dimensão vazia para obteres a linha ou a coluna inteira.

Usando a mesma matriz que acima:

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

m[2, 3]    # row 2, col 3
#> [1] 6

m[1:2, 3]  # rows 1:2, col 3
#> [1] 5 6

 m[2, ]    # all of row 2
#> [1] 2 4 6

Repara que um subconjunto com uma única linha e/ou coluna é devolvido como um vetor, não como uma matriz.

class(m)
#> [1] "matrix" "array" 
class(m[2,])
#> [1] "integer"

Este tipo de dimension reduction nem sempre é desejado, especialmente numa cadeia de funções que espera receber matrizes.

Especificar drop=FALSE garante que uma matriz à entrada devolve uma matriz. Repara que uma matriz com apenas uma linha ou uma coluna não é do mesmo tipo que um vetor.

m[2, , drop = FALSE]
#>      [,1] [,2] [,3]
#> [1,]    2    4    6

A função as.matrix() converte um vetor numa matriz de uma só coluna.

as.matrix(1:3)
#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3

Tal como com os vetores, partes de uma matriz podem ser modificadas simplesmente atribuindo novos valores.

m[, 2] <- c(10, 20)
m
#>      [,1] [,2] [,3]
#> [1,]    1   10    5
#> [2,]    2   20    6

Adicionar e eliminar linhas/colunas

Com vetores, a função c() era suficiente para inserir e acrescentar dados. As matrizes têm mais estrutura, por isso é preciso mais cuidado a especificar o que é necessário.

As funções rbind() e cbind() ligam novas linhas ou colunas à matriz.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
m2 <- matrix(7:9, nrow = 1, ncol = 3)

rbind(m, m2)
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
#> [3,]    7    8    9

Eliminar uma linha ou coluna usa indexação negativa, tal como nos vetores.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m[, -2]
     #> [,1] [,2]
#> [1,]    1    5
#> [2,]    2    6

Filtragem e funções

Todas as funções de vetores também funcionam em matrizes, aplicadas a todos os elementos. No entanto, podem perder a estrutura de matriz e devolver um vetor.

sqrt(m)
#>          [,1]     [,2]     [,3]
#> [1,] 1.000000 1.732051 2.236068
#> [2,] 1.414214 2.000000 2.449490

sum(m)
#> [1] 21

m[m > 3]
#> [1] 4 5 6

Para matrizes, as funções colSums() e rowSums() também podem ser úteis.

Neste caso, a redução de dimensões é intencional, por isso os valores devolvidos são vetores.

# m as defined above
rowSums(m)
#> [1]  9 12
colSums(m)
#> [1]  3  7 11

De forma semelhante, existem as funções rowMeans() e colMeans() para calcular médias.

Funções gerais que operam sobre matrizes

Noutros conceitos, vimos que as bibliotecas do Tidyverse podem simplificar operações sobre vetores e listas (purrr) ou dataframes (dplyr).

No entanto, matrizes e arrays estão, na sua maioria, fora do âmbito das entradas do Tidyverse (pelo menos para já).

A função apply() faz parte do R base e continua a ser a melhor ferramenta para um controlo mais fino sobre a programação com matrizes.

Os parâmetros, por ordem, são:

  1. A matriz de entrada.
  2. A dimensão sobre a qual operar (1 para linhas, 2 para colunas).
  3. A função a aplicar.
  4. Opcionalmente, quaisquer parâmetros extra para a função.

A função rowMeans() existe no R base, mas também a podíamos simular com apply() e mean().

randm <- sample(1:10, 16, replace = TRUE) |> matrix(4, 4)
randm
#>      [,1] [,2] [,3] [,4]
#> [1,]    4    9    8    3
#> [2,]    6    8    5    5
#> [3,]    4    2    1    2
#> [4,]    4    2    5    4

apply(randm, 1, mean)
#> [1] 6.00 6.00 2.25 3.75

rowMeans(randm)
#> [1] 6.00 6.00 2.25 3.75

Álgebra linear

Note

Esta secção pressupõe um nível de conhecimentos matemáticos fora do âmbito do Exercism.

Salta-a se não fizer sentido para ti.

No entanto, o R é uma linguagem de ciência de dados, e muitos utilizadores têm formação em matemática aplicada e estatística. Este resumo conciso pode ser do interesse de alguns estudantes.

O R moderno é "obviamente" uma linguagem de ciência de dados: concebido para trabalhar principalmente com dados tabulares e popular entre utilizadores como estatísticos, epidemiologistas e cientistas sociais. O principal concorrente nesta área é o ecossistema científico do Python, que inclui pacotes como o Pandas ou o Polars.

No entanto, nas décadas anteriores, o R também era visto como uma potencial linguagem de engenharia, competindo diretamente com o Matlab.

Talvez surpreendentemente, o R base ainda suporta toda uma gama de operações de álgebra linear sobre vetores e matrizes. Não é claro até que ponto estas ainda aparecem no código do utilizador final, mas estas técnicas continuam a ser muito importantes internamente para implementar funções estatísticas.

A álgebra linear em R ainda é ensinada em alguns cursos universitários, como esta aula de econometria. Um bom guia para a matemática (não para a programação) está em 3Blue1Brown.

Advanced

A discussão mais completa sobre álgebra linear no Exercism está no programa de estudos de Julia (os mesmos autores do programa de estudos de R). Acreditamos que a Julia (ou o Python, em contextos em que o módulo numpy.linalg está disponível) seria agora uma linguagem mais adequada para trabalho sério de álgebra linear do que o R.

Diagonais

A diagonal principal de uma matriz quadrada (N x N)) A vai do canto superior esquerdo (A[1,1]) ao canto inferior direito (A[N,N]).

A função diag() comporta-se de forma diferente consoante o argumento que lhe é passado.

Com um vetor v, diag(v) cria uma matriz diagonal.

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

Com uma matriz quadrada A, diag(A) devolve os elementos da diagonal principal como um vetor.

A
#>      [,1] [,2] [,3]
#> [1,]   10    5    2
#> [2,]    9    3    7
#> [3,]    8   10    4
diag(A)
#> [1] 10  3  4

Com um único número inteiro k, diag(k) cria uma matriz identidade k × k: talvez não seja o que adivinharias.

Até os manuais de R mais sérios tendem a acrescentar comentários como "Vai-se lá saber" nesta altura.

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

Transposta e inversa

Para trocar linhas e colunas, a função t() transpõe qualquer matriz.

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

Para uma matriz de valores complexos, o resultado não é o conjugado complexo. Usa Conj(t(A)) para obter a transposta conjugada (adjunta) da matriz A.

Para arrays de dimensão superior, aperm() permuta as dimensões.

Para a inversa de uma matriz quadrada A, podemos usar solve(A). Alguns de nós acham esta sintaxe ... inesperada.

Multiplicar uma matriz pela sua inversa dá a matriz identidade (sujeito a erros de arredondamento muito pequenos).

A <- matrix(sample(1:10, 9, replace = TRUE), 3, 3)
A
#>      [,1] [,2] [,3]
#> [1,]   10    5    2
#> [2,]    9    3    7
#> [3,]    8   10    4

solve(A)
#>             [,1]        [,2]        [,3]
#> [1,]  0.16666667  0.00000000 -0.08333333
#> [2,] -0.05747126 -0.06896552  0.14942529
#> [3,] -0.18965517  0.17241379  0.04310345

A %*% solve(A) |> round(digits = 10)
     [,1] [,2] [,3]
#> [1,]    1    0    0
#> [2,]    0    1    0
#> [3,]    0    0    1

Multiplicação

Os operadores aritméticos habituais, como +, -, *, ^, operam sobre matrizes elemento a elemento. Se houver uma incompatibilidade de dimensões, aplicam-se as regras habituais de reciclagem.

m
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6

m * m
#>      [,1] [,2] [,3]
#> [1,]    1    9   25
#> [2,]    4   16   36

# same calculation, different syntax
m ^ 2
#>      [,1] [,2] [,3]
#> [1,]    1    9   25
#> [2,]    4   16   36

Explicar os vários outros tipos de multiplicação de vetores e matrizes está fora do nosso âmbito, mas, em resumo muito breve:

Editar via GitHub A ligação abre numa nova janela ou separador