Trilhas
/
R
R
/
Programa
/
Matrizes e arrays
Ma

Matrizes e arrays em R

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

Sobre Matrizes e arrays

Ao lidar com coleções de dados atômicos homogêneos (por exemplo, todos numéricos ou todos do tipo caractere), as seguintes estruturas estão disponíveis no R:

  • vector para dados unidimensionais, acessados com um único índice.
  • matrix para dados bidimensionais, retangulares (e 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 seçã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 isso na linguagem base).

Matrizes

Uma matriz é, essencialmente, apenas um vetor, mais a contagem de linhas e colunas que define o formato.

Existe uma função matrix() para criá-las:

# 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

Repare que os dados são armazenados em ordem por colunas (ou seja, de cima para baixo e só depois para a coluna seguinte). Isso é igual ao Fortran, ao Matlab e ao Julia, mas é diferente de C/C++ e do NumPy.

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

Algumas outras funções produzem uma matriz como saída, 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

Isso funciona exatamente como a indexação de vetores, com a diferença de que é preciso especificar a(s) linha(s) e a(s) coluna(s), nessa ordem. Deixe uma dimensão vazia para obter a linha ou a coluna inteira.

Usando a mesma matriz do exemplo anterior:

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

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

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

Esse tipo de dimension reduction nem sempre é o que você quer, principalmente em um pipeline de funções que espera receber matrizes.

Especificar drop=FALSE garante que uma matriz de entrada continue sendo uma matriz na saída. Repare 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 em uma matriz de uma única coluna.

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

Assim como acontece com 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

Adição e remoção de linhas/colunas

No caso de vetores, a função c() bastava para inserir e acrescentar dados. As matrizes têm mais estrutura, então exigem mais cuidado ao especificar o que você quer.

As funções rbind() e cbind() anexam 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

Para remover uma linha ou uma coluna, usa-se indexação negativa, como no caso dos 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 vetor também funcionam em matrizes, aplicadas a todos os elementos. No entanto, elas podem perder a estrutura de matriz e retornar 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

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

Aqui a redução de dimensão é intencional, então os valores retornados são vetores.

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

Do mesmo modo, existem as funções rowMeans() e colMeans() para calcular médias.

Funções gerais que operam sobre matrizes

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

No entanto, matrizes e arrays estão, em boa parte, fora do escopo das entradas do Tidyverse (pelo menos por enquanto).

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

Os parâmetros, na 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 extras para a função.

A função rowMeans() existe no R base, mas também poderíamos simulá-la 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 seção pressupõe um conhecimento de matemática que está fora do escopo do Exercism.

Pule esta parte se ela não fizer sentido para você.

No entanto, o R é uma linguagem de ciência de dados, e muitos usuários têm formação em matemática aplicada e estatística. Este resumo breve pode interessar a alguns estudantes.

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

No entanto, em décadas passadas o R também era visto como uma possível linguagem de engenharia, competindo diretamente com o Matlab.

Talvez para surpresa de muitos, o R base ainda dá suporte a toda uma gama de operações de álgebra linear com vetores e matrizes. Não está claro o quanto elas aparecem hoje no código de quem usa a linguagem, mas essas técnicas continuam muito importantes internamente para implementar funções estatísticas.

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

Advanced

A discussão mais completa sobre álgebra linear dentro do Exercism está no syllabus de Julia (dos mesmos autores do syllabus de R). Acreditamos que Julia (ou Python, em contextos em que o módulo numpy.linalg está disponível) seria hoje uma linguagem mais apropriada do que o R para trabalhar com álgebra linear a sério.

Diagonais

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

A função diag() se comporta de maneira diferente, dependendo do argumento que recebe.

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) retorna 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 você esperaria.

Até livros sérios de R costumam acrescentar comentários como "vá entender" neste ponto.

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. Use Conj(t(A)) para obter a transposta conjugada (adjunta) da matriz A.

Para arrays de dimensões maiores, aperm() permuta as dimensões.

Para a inversa de uma matriz quadrada A, podemos usar solve(A). Para alguns de nós, essa sintaxe é ... inesperada.

Multiplicar uma matriz pela sua inversa dá a matriz identidade (a menos de 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 usuais, como +, -, * e ^, operam sobre matrizes elemento a elemento. Se houver incompatibilidade de dimensões, valem as regras usuais 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 outros tipos de multiplicação de vetores e matrizes está além do escopo desta página, mas, em resumo bem breve:

Editar via GitHub O link abre em uma nova janela ou aba