DevTools

Cheatsheet R

Linguagem para estatística, análise de dados e visualização

Voltar às linguagens
R
66 cards encontrados
Categorias:
Versões:

Básico


9 cards
Variáveis e atribuição
# Atribuição (forma preferida)
x <- 10
nome <- "Ana"

# Outras formas
y = 20          # também funciona
30 -> z         # para a direita (raro)

# Constantes integradas
pi              # 3.141593
LETTERS         # "A"..."Z"
letters         # "a"..."z"

# Remover objetos da memória
rm(x)
rm(list = ls()) # limpar tudo

Em R, a atribuição faz-se sobretudo com o operador <-. O sinal = também funciona, mas fica por convenção reservado aos argumentos de funções. rm() remove objetos da memória.

Operações vectorizadas
v <- c(10, 20, 30, 40, 50)

# Aritmética (elemento a elemento)
v * 2          # 20,40,60,80,100
v + c(1, 2, 3, 4, 5)
v ^ 2

# Estatísticas
sum(v)         # 150
mean(v)        # 30
median(v)      # 30
sd(v)          # desvio padrão
min(v); max(v)
range(v)       # mínimo e máximo

# Lógicos
any(v > 40)    # TRUE
all(v > 0)     # TRUE
which(v > 25)  # posições: 3 4 5

As operações são vectorizadas: aplicam-se a todos os elementos de uma vez, sem precisar de ciclos. Funções como sum(), mean() e sd() resumem o vetor.

Valores especiais
# NA: valor em falta (missing)
x <- c(1, NA, 3)
is.na(x)              # FALSE TRUE FALSE
mean(x)               # NA
mean(x, na.rm = TRUE) # 2

# NULL: ausência de valor (vazio)
y <- NULL
is.null(y)            # TRUE
length(y)             # 0

# NaN e Inf
0 / 0             # NaN (not a number)
1 / 0             # Inf (infinito)
is.nan(0 / 0)     # TRUE
is.finite(Inf)    # FALSE

NA representa um dado em falta e propaga-se nos cálculos (usa na.rm = TRUE para ignorar). NULL é a ausência de valor. NaN e Inf surgem de operações matemáticas inválidas.

Tipos de dados
# Tipos atómicos
42L         # integer
3.14        # double (numeric)
"texto"     # character
TRUE        # logical
1 + 2i      # complex

# Ver o tipo
class(x)        # classe
typeof(x)       # tipo interno
is.numeric(x)   # TRUE/FALSE
is.character(x)

# Converter
as.numeric("42")   # 42
as.character(42)   # "42"
as.integer(3.7)    # 3
as.logical(1)      # TRUE

Os seis tipos atómicos são integer, double, character, logical, complex e raw. Usa class() e typeof() para os inspecionar e as.*() para converter.

Strings
s <- "Olá Mundo"

# Juntar texto
paste("Olá", "Mundo")       # "Olá Mundo"
paste0("a", "b")            # "ab" (sem espaço)
paste(1:3, collapse = ", ") # "1, 2, 3"

# Formatar
sprintf("%.2f", pi)             # "3.14"
sprintf("%s tem %d", "Ana", 30)

# Manipular
nchar(s)             # 9
toupper(s)           # "OLÁ MUNDO"
substr(s, 1, 3)      # "Olá"
gsub("o", "0", s)    # substituir
strsplit(s, " ")     # lista de palavras

R trata texto como vetores de character. paste() junta pedaços, sprintf() formata e funções como nchar(), toupper() e gsub() manipulam o conteúdo.

Criar vetores
# Combinar valores
v <- c(1, 2, 3, 4, 5)

# Sequências
v <- 1:10               # 1 a 10
v <- seq(1, 10, by = 2) # 1,3,5,7,9
v <- seq(0, 1, length.out = 5)

# Repetir
v <- rep(0, times = 5)      # 0,0,0,0,0
v <- rep(c(1, 2), each = 3) # 1,1,1,2,2,2

# Vetor vazio de um tipo
v <- numeric(5)
v <- character(3)

Um vetor é a estrutura mais básica de R e guarda elementos do mesmo tipo. c() combina valores, : e seq() criam sequências e rep() repete padrões.

Fatores
# Variável categórica
sexo <- factor(c("M", "F", "F", "M"))

# Com ordem definida
nota <- factor(
  c("B", "A", "C", "A"),
  levels = c("C", "B", "A"),
  ordered = TRUE
)

# Com rótulos
grupo <- factor(
  c(1, 2, 1, 2),
  labels = c("Controlo", "Tratamento")
)

# Operações
levels(sexo)     # "F" "M"
nlevels(sexo)    # 2
table(sexo)      # contagem por nível

Um factor guarda variáveis categóricas com um conjunto fixo de levels. É essencial em modelos estatísticos e gráficos. ordered = TRUE cria níveis com ordem.

Aceder a vetores
v <- c(10, 20, 30, 40, 50)

# Por posição (começa em 1!)
v[1]          # primeiro
v[-1]         # todos exceto o 1.º
v[2:4]        # posições 2 a 4
v[c(1, 3, 5)] # posições específicas

# Por condição lógica
v[v > 25]     # 30, 40, 50

# Por nome
names(v) <- c("a", "b", "c", "d", "e")
v["a"]        # 10

A indexação em R começa em 1, não em 0. Índices negativos excluem elementos e um vetor lógico seleciona os que são TRUE. Com names() podes aceder pelo nome.

Operadores
# Aritméticos
10 %% 3    # resto: 1
10 %/% 3   # divisão inteira: 3
2 ^ 10     # potência: 1024

# Comparação
==  !=  >  <  >=  <=

# Lógicos
&  |  !    # vectorizados
&& ||      # escalares (para if)

# Pertence a
3 %in% c(1, 2, 3)   # TRUE

# Pipes (encadear operações)
x |> sum() |> sqrt()       # nativo (R 4.1+)
x %>% sum() %>% sqrt()     # magrittr

R tem operadores como %% (resto) e %/% (divisão inteira). %in% testa pertença e os pipes |> (nativo) e %>% (magrittr) encadeiam funções da esquerda para a direita.

Controlo de Fluxo


8 cards
If / Else
x <- 5

if (x > 0) {
  print("Positivo")
} else if (x < 0) {
  print("Negativo")
} else {
  print("Zero")
}

# switch por valor
dia <- switch("2",
  "1" = "Segunda",
  "2" = "Terça",
  "Outro"
)

O if avalia uma condição lógica e executa um bloco. Podes encadear else if e else. switch() é uma alternativa limpa para comparar um valor com várias opções.

Família apply
mat <- matrix(1:12, nrow = 3)

# apply: por linha (1) ou coluna (2)
apply(mat, 1, sum)    # soma por linha
apply(mat, 2, mean)   # média por coluna

# lapply: lista -> lista
lapply(1:5, function(x) x ^ 2)

# sapply: lista -> vetor (simplificado)
sapply(1:5, function(x) x ^ 2)

# vapply: tipo de retorno garantido
vapply(1:5, sqrt, numeric(1))

# mapply: vários argumentos
mapply(function(x, y) x + y, 1:3, 4:6)

# tapply: por grupo
tapply(valores, grupos, mean)

A família apply aplica uma função a elementos sem escrever ciclos. lapply() devolve uma lista, sapply() simplifica para vetor e vapply() garante o tipo de retorno, sendo mais seguro.

ifelse vectorizado
notas <- c(12, 8, 15, 6)

# Aplica a cada elemento
resultado <- ifelse(notas >= 10, "Aprovado", "Reprovado")
# "Aprovado" "Reprovado" "Aprovado" "Reprovado"

# Aninhado
classe <- ifelse(notas >= 14, "Alta",
  ifelse(notas >= 10, "Média", "Baixa"))

# case_when (dplyr, mais legível)
library(dplyr)
df |> mutate(nivel = case_when(
  notas >= 14 ~ "Alta",
  notas >= 10 ~ "Média",
  TRUE ~ "Baixa"
))

Ao contrário do if, a função ifelse() é vectorizada: avalia um vetor inteiro de uma vez. Para várias condições, case_when() do dplyr é mais legível.

purrr e map
library(purrr)

# map: retorna lista
map(1:5, ~ .x ^ 2)

# Tipos específicos
map_dbl(1:5, sqrt)          # double
map_chr(1:5, as.character)  # texto
map_lgl(1:5, ~ .x > 3)      # lógico

# Dois argumentos
map2(nomes, idades, ~ paste(.x, .y))

# Filtrar
keep(1:10, ~ .x %% 2 == 0)     # pares
discard(1:10, ~ .x > 5)

# Reduzir a um valor
reduce(list(df1, df2, df3), merge)

O pacote purrr traz programação funcional moderna. map() aplica uma função e as variantes map_dbl() e map_chr() garantem o tipo. A fórmula ~ .x é uma função anónima abreviada.

For loops
# Iterar uma sequência
for (i in 1:5) {
  print(i)
}

# Percorrer elementos
for (x in c("a", "b", "c")) {
  cat(x, "\n")
}

# Com índice seguro
v <- c(10, 20, 30)
for (i in seq_along(v)) {
  cat(i, ":", v[i], "\n")
}

# break e next
for (i in 1:10) {
  if (i == 3) next   # salta o 3
  if (i == 7) break  # pára no 7
  print(i)
}

O ciclo for percorre cada elemento de um vetor. seq_along() gera índices seguros. next salta uma iteração e break termina o ciclo. Em R, prefere operações vectorizadas a ciclos.

Condições vectorizadas
v <- c(5, 2, 8, 1, 9)

# Posições que cumprem a condição
which(v > 4)      # 1 3 5
which.max(v)      # 5 (posição do máximo)
which.min(v)      # 4

# Verificações globais
any(v > 8)        # TRUE (algum?)
all(v > 0)        # TRUE (todos?)

# cut: dividir em intervalos
grupos <- cut(idades,
  breaks = c(0, 18, 65, Inf),
  labels = c("Jovem", "Adulto", "Idoso")
)

which() devolve as posições que satisfazem uma condição; which.max() e which.min() dão a posição do extremo. any() e all() testam o vetor todo e cut() agrupa valores em intervalos.

While e Repeat
# while: repete enquanto for verdadeiro
x <- 1
while (x <= 5) {
  print(x)
  x <- x + 1
}

# repeat: loop infinito (precisa de break)
x <- 1
repeat {
  if (x > 5) break
  print(x)
  x <- x + 1
}

# Convergência numérica
valor <- 1
repeat {
  novo <- (valor + 2 / valor) / 2
  if (abs(novo - valor) < 1e-6) break
  valor <- novo
}
valor   # raiz quadrada de 2

while repete enquanto a condição for verdadeira. repeat cria um ciclo infinito que só pára com break — útil para algoritmos de convergência.

Tratamento de erros
resultado <- tryCatch({
  log("abc")              # gera erro
}, error = function(e) {
  message("Erro: ", e$message)
  NA                      # valor de recurso
}, warning = function(w) {
  message("Aviso: ", w$message)
  NULL
}, finally = {
  message("Concluído")    # corre sempre
})

# Sinalizar condições
stop("Erro fatal")        # pára com erro
warning("Cuidado")        # aviso
message("Informação")     # mensagem

# Validar entradas
stopifnot(is.numeric(x), x > 0)

tryCatch() captura erros, avisos e executa código final com finally. stop() lança um erro, warning() um aviso e stopifnot() valida condições de entrada de forma compacta.

Funções


8 cards
Definir funções
# Função básica
soma <- function(a, b) {
  a + b
}

# Com valores por defeito
saudar <- function(nome, sauda = "Olá") {
  paste(sauda, nome)
}
saudar("Ana")             # "Olá Ana"
saudar("Ana", "Bom dia")  # "Bom dia Ana"

# Argumentos por nome
saudar(sauda = "Hi", nome = "Bob")

# Retorno implícito (última expressão)
dobro <- function(x) x * 2

Uma função cria-se com function(). O valor por defeito define-se com = nos argumentos. R devolve automaticamente a última expressão avaliada, sem precisar de return().

Funções como objetos
# Atribuir a uma variável
f <- sum
f(1, 2, 3)        # 6

# Passar como argumento
aplicar <- function(dados, funcao) {
  funcao(dados)
}
aplicar(1:10, mean)   # 5.5
aplicar(1:10, sum)    # 55

# Retornar uma função
potencia <- function(n) {
  function(x) x ^ n
}
quadrado <- potencia(2)
quadrado(5)           # 25

# Composição
compor <- function(f, g) function(x) f(g(x))
sqrt_log <- compor(sqrt, log)

# Negate: inverter uma função lógica
nao_na <- Negate(is.na)

Em R, funções são objetos de primeira classe: podes atribuí-las a variáveis, passá-las como argumentos e devolvê-las. Negate() cria uma função que inverte o resultado lógico de outra.

Funções anónimas
# Forma clássica
sapply(1:5, function(x) x ^ 2)

# Shorthand (R 4.1+)
sapply(1:5, \(x) x ^ 2)

# Vários argumentos
mapply(\(x, y) x + y, 1:3, 4:6)

# Corpo com várias linhas
sapply(1:5, \(x) {
  y <- x ^ 2
  y + 1
})

# Passar a funções de ordem superior
Filter(\(x) x > 3, 1:10)
Map(\(a, b) a * b, 1:3, 4:6)

Uma função anónima não tem nome e usa-se diretamente como argumento. Desde o R 4.1 podes escrever \(x) em vez de function(x). São ideais para as famílias apply e Map.

Métodos S3
# Criar um objeto com classe
criar_pessoa <- function(nome, idade) {
  structure(
    list(nome = nome, idade = idade),
    class = "Pessoa"
  )
}

# Método para o genérico print
print.Pessoa <- function(x, ...) {
  cat(x$nome, "tem", x$idade, "anos\n")
}

# Novo genérico com UseMethod
saudar <- function(x) UseMethod("saudar")
saudar.Pessoa <- function(x) paste("Olá,", x$nome)
saudar.default <- function(x) "Olá!"

# Usar
p <- criar_pessoa("Ana", 30)
class(p)              # "Pessoa"
inherits(p, "Pessoa") # TRUE

O sistema S3 é o mais comum em R. Um objeto é uma lista com um atributo class. Métodos como print.Pessoa são despachados por UseMethod() conforme a classe do objeto.

Escopo léxico e closures
x <- 10
funcao <- function() {
  x <- 20      # variável local
  x
}
funcao()       # 20
x              # 10 (global intacto)

# Closure: função que guarda o ambiente
criar_mult <- function(fator) {
  function(x) x * fator
}
triplo <- criar_mult(3)
triplo(5)      # 15

# Superatribuição (evitar)
contador <- 0
incrementar <- function() {
  contador <<- contador + 1
}

R usa escopo léxico: uma função procura variáveis primeiro no seu ambiente e depois no ambiente onde foi criada. Uma closure guarda esse ambiente, permitindo estado persistente. <<- altera variáveis fora da função (usar com cautela).

Recursão
# Fatorial
fatorial <- function(n) {
  if (n <= 1) return(1)
  n * fatorial(n - 1)
}

# Fibonacci
fib <- function(n) {
  if (n <= 1) return(n)
  fib(n - 1) + fib(n - 2)
}

# Recall: auto-referência segura
f <- function(n) {
  if (n == 0) return(1)
  n * Recall(n - 1)
}

# Com memoization (guardar resultados)
memo <- new.env()
fib_memo <- function(n) {
  if (n <= 1) return(n)
  chave <- as.character(n)
  if (is.null(memo[[chave]]))
    memo[[chave]] <- fib_memo(n-1) + fib_memo(n-2)
  memo[[chave]]
}

Uma função recursiva chama-se a si própria até atingir um caso base. Recall() refere-se à própria função de forma segura. A memoization guarda resultados num environment para evitar recálculos.

Argumentos avançados
# ... (dots): argumentos variáveis
meu_plot <- function(x, ...) {
  plot(x, ...)
}
meu_plot(1:10, col = "red", pch = 19)

# missing(): argumento fornecido?
f <- function(a, b) {
  if (missing(b)) b <- a
  a + b
}

# match.arg: validar opções
f <- function(cor = c("red", "blue", "green")) {
  cor <- match.arg(cor)
  cor
}

# on.exit: limpeza garantida
ler <- function(path) {
  con <- file(path)
  on.exit(close(con))
  readLines(con)
}

# do.call: chamar com lista de args
do.call(paste, list("a", "b", "c"))

Os ... (dots) aceitam argumentos variáveis e repassam-nos a outras funções. missing() verifica se um argumento foi dado, match.arg() valida opções e on.exit() garante limpeza mesmo em caso de erro.

Debugging
# browser(): pausa interativa
minha_funcao <- function(x) {
  browser()      # REPL de depuração
  x * 2
}

# debug(): depurar função inteira
debug(minha_funcao)
minha_funcao(5)
undebug(minha_funcao)

# traceback(): pilha de chamadas
traceback()

# options(error): recuperar de erros
options(error = recover)

# Validar e medir tempo
stopifnot(is.numeric(x), x > 0)
system.time(resultado <- calculo())

browser() pausa a execução para inspecionar variáveis; debug() ativa a depuração de uma função. traceback() mostra a pilha de chamadas após um erro e system.time() mede a duração.

Estruturas de Dados


8 cards
Data frames
# Criar
df <- data.frame(
  nome = c("Ana", "Rui", "Eva"),
  idade = c(30, 25, 35),
  nota = c(14, 16, 12)
)

# Aceder
df$nome         # coluna por nome
df[["nome"]]    # idem
df[1, 2]        # linha 1, coluna 2
df[1:2, ]       # linhas 1 e 2
df[, "idade"]   # coluna idade

# Informação
nrow(df); ncol(df)
dim(df)         # 3 3
str(df)         # estrutura
head(df)        # primeiras linhas
summary(df)     # resumo estatístico

Um data.frame é uma tabela com colunas de tipos diferentes, a estrutura central para dados em R. Acede-se a colunas com $ ou [[]]. str() mostra a estrutura e summary() um resumo.

Modificar data frames
df <- data.frame(x = 1:3, y = c("a", "b", "c"))

# Adicionar coluna
df$z <- df$x * 2
df <- transform(df, total = x + z)

# Remover coluna
df$z <- NULL

# Renomear
names(df)[1] <- "valor"

# Filtrar linhas
df[df$x > 1, ]
subset(df, x > 1 & y != "a")

# Ordenar
df[order(df$x), ]         # crescente
df[order(-df$x), ]        # decrescente

# Cruzar tabelas
merge(df1, df2, by = "id")

Podes criar colunas com $ ou transform() e removê-las atribuindo NULL. order() devolve a ordem de ordenação e merge() cruza dois data.frame por uma coluna comum.

Listas
# Lista heterogénea
lst <- list(
  nome = "Ana",
  idade = 30,
  notas = c(14, 16, 12),
  ativo = TRUE
)

# Aceder
lst$nome        # "Ana"
lst[[1]]        # 1.º elemento
lst[1]          # sub-lista
lst[["nome"]]   # "Ana"

# Modificar
lst$email <- "a@b.c"     # adicionar
lst$idade <- NULL        # remover

# Achatar
unlist(lst)     # vetor simples

# length(lst)  # número de elementos

Uma list guarda elementos de tipos diferentes, incluindo outras listas. [[]] extrai o elemento em si, enquanto [] devolve uma sub-lista. unlist() achata a lista para um vetor.

Tibbles
library(tibble)

# Criar tibble
tb <- tibble(
  nome = c("Ana", "Rui"),
  idade = c(30, 25)
)

# Por linhas
tribble(
  ~nome, ~idade,
  "Ana", 30,
  "Rui", 25
)

# Colunas-lista
tb2 <- tibble(
  id = 1:3,
  dados = list(c(1, 2), c(3, 4, 5), c(6))
)

# Converter
as_tibble(df)
as.data.frame(tb)

Um tibble é um data.frame moderno do tidyverse: não converte texto em fatores, preserva nomes de colunas e imprime de forma mais clara. tribble() cria-o por linhas e aceita colunas-lista.

Matrizes
# Criar (preenchido por coluna)
m <- matrix(1:12, nrow = 3, ncol = 4)

# Por linha
m <- matrix(1:12, nrow = 3, byrow = TRUE)

# Aceder
m[1, 2]       # linha 1, coluna 2
m[1, ]        # linha 1
m[, 2]        # coluna 2
m[1:2, 1:2]   # sub-matriz

# Álgebra linear
t(m)          # transposta
m %*% n       # produto matricial
solve(m)      # inversa
det(m)        # determinante
eigen(m)      # autovalores e autovetores

Uma matrix é um vetor bidimensional com elementos do mesmo tipo. Por defeito preenche-se por colunas (byrow = TRUE muda isso). %*% faz produto matricial e solve() calcula a inversa.

Datas e tempos
library(lubridate)

# Criar datas (ordem dos componentes)
ymd("2024-03-15")
dmy("15/03/2024")
ymd_hms("2024-03-15 14:30:00")

# Extrair componentes
year(d); month(d); day(d)
wday(d, label = TRUE)   # dia da semana

# Aritmética
d + days(7)
d - months(1)
interval(d1, d2) / days(1)   # nº de dias

# Base R
Sys.Date()
as.Date("2024-01-01")
format(Sys.Date(), "%d/%m/%Y")
difftime(d2, d1, units = "days")

O pacote lubridate simplifica datas: ymd() e dmy() interpretam a ordem dos componentes. Soma períodos com days() e months(). Em R base, usa as.Date() e difftime().

Arrays e combinação
# Array com n dimensões
arr <- array(1:24, dim = c(2, 3, 4))
arr[1, 2, 3]

# Combinar por linhas
rbind(c(1, 2), c(3, 4))

# Combinar por colunas
cbind(c(1, 2), c(3, 4))

# Juntar data frames
rbind(df1, df2)     # empilhar linhas
cbind(df1, df2)     # lado a lado

# Nomear dimensões
dimnames(m) <- list(
  c("l1", "l2", "l3"),
  c("c1", "c2", "c3", "c4")
)

Um array generaliza a matriz para mais dimensões. rbind() junta por linhas e cbind() por colunas, tanto para vetores como para data.frame. dimnames() dá nomes às dimensões.

Inspeção e conversão
x <- c(1, 2, 3)

# Inspecionar
class(x)          # "numeric"
typeof(x)         # "double"
str(x)            # estrutura compacta
length(x)         # 3
attributes(x)     # atributos

# Converter entre tipos
as.integer(c(1.5, 2.9))   # 1 2
as.character(1:3)         # "1" "2" "3"
as.numeric(c("1", "2"))   # 1 2
as.logical(c(0, 1, 2))    # FALSE TRUE TRUE

# Reestruturar
unlist(list(1, 2, 3))
as.vector(matrix(1:6))

class() e typeof() revelam o tipo; str() mostra a estrutura de forma compacta. As funções as.*() convertem entre tipos — atenção que as.integer() trunca os decimais.

Manipulação de Dados


9 cards
dplyr — verbos básicos
library(dplyr)

# filter: selecionar linhas
df |> filter(idade > 25, ativo == TRUE)

# select: escolher colunas
df |> select(nome, idade)
df |> select(-nota)             # remover

# mutate: criar/alterar colunas
df |> mutate(
  dobro = idade * 2,
  senior = ifelse(idade > 30, "S", "J")
)

# arrange: ordenar
df |> arrange(desc(idade), nome)

# slice: por posição
df |> slice(1:5)
df |> slice_max(nota, n = 3)

# rename
df |> rename(id = codigo)

O dplyr manipula dados com verbos claros ligados pelo pipe |>. filter() seleciona linhas, select() colunas, mutate() cria colunas, arrange() ordena e rename() renomeia.

across — várias colunas
library(dplyr)

# Aplicar a várias colunas
df |> mutate(across(c(x, y, z), ~ .x * 2))

# Por tipo
df |> mutate(across(where(is.numeric), round, 2))

# Várias funções de uma vez
df |> summarise(
  across(
    where(is.numeric),
    list(media = mean, sd = sd),
    na.rm = TRUE
  )
)

# Condições sobre colunas
df |> filter(if_any(ends_with("_p"), ~ .x > 0.5))
df |> filter(if_all(starts_with("q"), ~ .x > 0))

# Renomear em massa
df |> rename_with(toupper)

across() aplica a mesma função a várias colunas de uma vez, combinando com where() ou seletores. if_any() e if_all() filtram linhas segundo condições em várias colunas.

stringr — texto
library(stringr)

# Detetar e contar
str_detect(s, "padrão")
str_count(s, "a")

# Extrair e substituir
str_extract(s, "\\d+")
str_replace(s, "velho", "novo")
str_replace_all(s, "\\d", "#")

# Manipular
str_pad("42", 5, pad = "0")   # "00042"
str_trim("  texto  ")
str_to_title("olá mundo")

# Dividir e juntar
str_split(s, ",")
str_c("a", "b", "c", sep = "-")
str_flatten(c("a", "b"), collapse = ", ")

O pacote stringr oferece funções de texto com nomes consistentes (todas começam em str_). str_detect() procura padrões, str_replace() substitui e str_pad() preenche.

dplyr — agrupar e resumir
library(dplyr)

df |>
  group_by(departamento) |>
  summarise(
    media = mean(salario),
    total = n(),
    max_sal = max(salario),
    .groups = "drop"
  )

# Vários grupos
df |>
  group_by(ano, mes) |>
  summarise(vendas = sum(valor))

# mutate por grupo
df |>
  group_by(equipa) |>
  mutate(rank = rank(-pontos))

# Contagem rápida
df |> count(categoria, sort = TRUE)

group_by() divide os dados em grupos e summarise() calcula resumos por grupo, como mean() e n() (contagem). count() é um atalho para contar por categoria.

tidyr — reshape
library(tidyr)

# Largo -> longo
df |> pivot_longer(
  cols = c(ano1, ano2, ano3),
  names_to = "ano",
  values_to = "valor"
)

# Longo -> largo
df |> pivot_wider(
  names_from = categoria,
  values_from = valor
)

# Separar / unir colunas
df |> separate(nome, c("primeiro", "ultimo"), " ")
df |> unite("completo", primeiro, ultimo, sep = " ")

O tidyr muda a forma dos dados. pivot_longer() passa colunas para linhas (formato longo) e pivot_wider() faz o contrário. separate() divide uma coluna e unite() junta várias.

dplyr — joins
library(dplyr)

# Manter só correspondências
inner_join(pedidos, clientes, by = "id")

# Todos da esquerda
left_join(pedidos, clientes, by = "id")

# Todos da direita
right_join(pedidos, clientes, by = "id")

# Todos de ambos
full_join(df1, df2, by = "id")

# Filtro: só os que existem
semi_join(pedidos, clientes, by = "id")

# Exclusão: os que não existem
anti_join(pedidos, clientes, by = "id")

# Chaves com nomes diferentes
left_join(a, b, by = c("id" = "codigo"))

Os join combinam tabelas por uma chave. left_join() mantém todas as linhas da esquerda; inner_join() só as correspondências. semi_join() e anti_join() filtram sem adicionar colunas.

tidyr — NAs e nesting
library(tidyr)

# Preencher NAs para baixo
df |> fill(coluna, .direction = "down")

# Remover linhas com NA
df |> drop_na()
df |> drop_na(idade, nome)   # só estas cols

# Substituir NAs
df |> replace_na(list(idade = 0))

# Aninhar dados por grupo
df |> nest(dados = -grupo)

# Desaninhar
df |> unnest(dados)

fill() preenche valores em falta com o último valor conhecido. drop_na() remove linhas com NA e replace_na() substitui-os. nest() guarda sub-tabelas numa coluna-lista.

Seleção de colunas (tidyselect)
library(dplyr)

# Por padrão de nome
df |> select(starts_with("pre"))
df |> select(ends_with("_id"))
df |> select(contains("data"))
df |> select(matches("^col\\d+$"))

# Por tipo
df |> select(where(is.numeric))
df |> select(where(is.character))

# Variável com nomes
cols <- c("a", "b", "c")
df |> select(all_of(cols))   # erro se faltar
df |> select(any_of(cols))   # tolerante

# Reordenar
df |> select(id, everything())

O tidyselect escolhe colunas por padrões: starts_with(), ends_with(), contains() e matches() (regex). where() seleciona por tipo e everything() apanha as restantes.

Dados em falta (base R)
v <- c(1, NA, 3, NA, 5)

# Detetar
is.na(v)                  # vetor lógico
sum(is.na(v))             # total de NAs
colSums(is.na(df))        # por coluna

# Remover
na.omit(df)               # linhas com NA
df[complete.cases(df), ]

# Substituir
v[is.na(v)] <- 0
ifelse(is.na(v), 0, v)

# Ignorar nos cálculos
mean(v, na.rm = TRUE)
sum(v, na.rm = TRUE)

is.na() deteta valores em falta. na.omit() e complete.cases() removem-nos; também podes substituí-los por índice. A opção na.rm = TRUE ignora os NA nas funções estatísticas.

Avançado


8 cards
Ler e escrever dados
# CSV (base R)
df <- read.csv("dados.csv")
write.csv(df, "out.csv", row.names = FALSE)

# readr (mais rápido, tibble)
library(readr)
df <- read_csv("dados.csv")
write_csv(df, "out.csv")

# Excel
library(readxl)
df <- read_excel("dados.xlsx", sheet = 1)

# RDS (um objeto R)
saveRDS(objeto, "dados.rds")
objeto <- readRDS("dados.rds")

# Linhas de texto
linhas <- readLines("ficheiro.txt")
writeLines(linhas, "out.txt")

read.csv() lê ficheiros CSV em R base; read_csv() do readr é mais rápido e devolve um tibble. saveRDS() e readRDS() guardam objetos R em formato nativo.

Programação paralela
library(parallel)

# Nº de núcleos disponíveis
detectCores()

# Criar cluster
cl <- makeCluster(4)

# lapply paralelo
resultados <- parLapply(cl, dados, funcao)

# Exportar variáveis/funções
clusterExport(cl, c("dados", "funcao"))

# Terminar (importante!)
stopCluster(cl)

# Alternativa simples (só Unix)
mclapply(dados, funcao, mc.cores = 4)

O pacote parallel distribui trabalho por vários núcleos. makeCluster() cria os workers, parLapply() aplica em paralelo e stopCluster() liberta os recursos. Em Unix, mclapply() é mais simples.

Pacotes
# Instalar do CRAN
install.packages("dplyr")
install.packages(c("ggplot2", "tidyr"))

# Carregar
library(dplyr)
require(ggplot2)     # devolve TRUE/FALSE

# Usar sem carregar
dplyr::filter(df, x > 1)

# Informação
installed.packages()
packageVersion("dplyr")
sessionInfo()

# Atualizar
update.packages()

install.packages() instala do CRAN e library() carrega o pacote para a sessão. O operador :: usa uma função sem carregar o pacote, evitando conflitos de nomes. sessionInfo() lista os pacotes ativos.

Classes R6
library(R6)

# Definir classe
Pessoa <- R6Class("Pessoa",
  public = list(
    nome = NULL,
    idade = NULL,
    initialize = function(nome, idade) {
      self$nome <- nome
      self$idade <- idade
    },
    saudar = function() {
      paste("Olá,", self$nome)
    }
  ),
  private = list(
    segredo = "privado"
  )
)

# Usar
p <- Pessoa$new("Ana", 30)
p$saudar()
p$nome <- "Rui"

R6 traz programação orientada a objetos por referência (ao contrário do S3). public e private definem membros, initialize() é o construtor e self refere o objeto. Cria-se com $new().

data.table
library(data.table)

# Ler muito rápido
dt <- fread("grande.csv")

# Sintaxe: dt[i, j, by]
# Filtrar (i), calcular (j), agrupar (by)
dt[idade > 25, .(media = mean(salario)), by = dep]

# Adicionar/alterar colunas por referência
dt[, novo := salario * 2]

# Várias operações
dt[, .(total = .N, media = mean(valor)), by = grupo]

# Ordenar
setorder(dt, -idade)

# Converter
setDT(df)        # data.frame -> data.table
as.data.table(df)

O data.table é ideal para dados grandes. A sintaxe dt[i, j, by] filtra, calcula e agrupa numa só expressão. := modifica colunas por referência (sem copiar) e fread() lê ficheiros muito rapidamente.

Ambientes (environments)
# Criar ambiente
env <- new.env()

# Atribuir e aceder
env$x <- 10
env[["y"]] <- 20
env$x          # 10

# Listar e verificar
ls(env)
exists("x", envir = env)

# Remover
rm("x", envir = env)

# Ambiente global e de funções
globalenv()
environment()

# Usado em closures e memoization
cache <- new.env()

Um environment associa nomes a valores, como uma lista, mas sem ordem e por referência. É a base do escopo em R e do sistema de objetos. Usa-se muito para caches e estado mutável, como em memoization.

Performance
# Vectorizar (evitar ciclos)
# Mau:
r <- numeric(length(x))
for (i in seq_along(x)) r[i] <- x[i] * 2
# Bom:
r <- x * 2

# Pré-alocar resultados
resultado <- numeric(n)

# apply em vez de for
resultados <- lapply(lista, processar)

# Medir tempo
system.time(calculo())

# Comparar métodos
microbenchmark::microbenchmark(
  metodo1(), metodo2(), times = 100
)

# Profiling
Rprof(); codigo(); Rprof(NULL)
summaryRprof()

A regra de ouro é vectorizar: operações sobre vetores inteiros são muito mais rápidas que ciclos. Pré-aloca resultados com numeric(), usa lapply() e mede com system.time() ou microbenchmark().

Projetos e reprodutibilidade
# renv: fixar versões de pacotes
renv::init()        # criar renv.lock
renv::snapshot()    # guardar estado atual
renv::restore()     # restaurar do lockfile

# devtools: criar pacotes
devtools::create("meuPacote")
devtools::document()   # gerar documentação
devtools::install()
devtools::check()

# usethis: configurar projetos
usethis::create_project("analise")
usethis::use_git()
usethis::use_testthat()

# Boas práticas
# - Um projeto por pasta (.Rproj)
# - Caminhos relativos
# - renv.lock versionado

O renv torna as análises reproduzíveis ao fixar versões de pacotes num renv.lock. O devtools e o usethis ajudam a criar pacotes e projetos. Usa sempre caminhos relativos e um projeto por pasta.

Visualização


8 cards
ggplot2 — básico
library(ggplot2)

# Estrutura: dados + estética + geometria
ggplot(df, aes(x = idade, y = salario)) +
  geom_point()

# Adicionar linha de tendência
ggplot(df, aes(x = idade, y = salario)) +
  geom_point() +
  geom_smooth(method = "lm")

# Título e eixos
ggplot(df, aes(x = idade, y = salario)) +
  geom_point() +
  labs(
    title = "Salário por idade",
    x = "Idade",
    y = "Salário (€)"
  )

O ggplot2 constrói gráficos por camadas: ggplot() define os dados, aes() a estética (eixos, cores) e os geom_*() o tipo de gráfico. As camadas somam-se com +.

Facets
library(ggplot2)

# Um painel por nível de uma variável
ggplot(df, aes(x = idade, y = salario)) +
  geom_point() +
  facet_wrap(~grupo)

# Grelha com duas variáveis
ggplot(df, aes(x = idade, y = salario)) +
  geom_point() +
  facet_grid(sexo ~ escalao)

# Escalas livres por painel
ggplot(df, aes(x = ano, y = vendas)) +
  geom_line() +
  facet_wrap(~produto, scales = "free_y")

Os facets dividem o gráfico em vários painéis. facet_wrap() cria painéis por uma variável e facet_grid() uma grelha com duas. scales = "free_y" deixa cada painel ter a sua escala.

Geoms essenciais
library(ggplot2)

# Histograma
ggplot(df, aes(x = idade)) +
  geom_histogram(bins = 20, fill = "steelblue")

# Boxplot
ggplot(df, aes(x = grupo, y = valor)) +
  geom_boxplot()

# Barras (contagem)
ggplot(df, aes(x = categoria, fill = tipo)) +
  geom_bar(position = "dodge")

# Linhas
ggplot(df, aes(x = ano, y = vendas)) +
  geom_line()

# Densidade
ggplot(df, aes(x = valor, fill = grupo)) +
  geom_density(alpha = 0.4)

Cada geom desenha um tipo de gráfico: geom_histogram() para distribuições, geom_boxplot() para resumos, geom_bar() para contagens e geom_line() para séries. alpha controla a transparência.

Camadas múltiplas
library(ggplot2)

ggplot(df, aes(x = ano, y = valor)) +
  # Linha por série (agrupada)
  geom_line(aes(group = id, color = id), alpha = 0.4) +
  # Pontos por cima
  geom_point(aes(color = id)) +
  # Linha de referência vertical
  geom_vline(xintercept = 2020, linetype = "dashed") +
  # Texto anotado
  annotate("text", x = 2021, y = 100, label = "Evento") +
  theme_minimal()

Podes sobrepor várias camadas: geom_line() com geom_point() por cima, linhas de referência com geom_vline() e anotações com annotate(). A ordem das camadas define o que fica por cima.

Cores e escalas
library(ggplot2)

p <- ggplot(df, aes(x, y, color = grupo)) +
  geom_point()

# Cores manuais
p + scale_color_manual(values = c("red", "blue"))

# Paletas ColorBrewer
p + scale_color_brewer(palette = "Set2")

# Eixos
p + scale_x_continuous(limits = c(0, 100))
p + scale_y_log10()

# Preenchimento (fill)
ggplot(df, aes(x, fill = tipo)) +
  geom_bar() +
  scale_fill_brewer(palette = "Pastel1")

As scale_*() controlam cores e eixos. scale_color_manual() define cores fixas e scale_color_brewer() usa paletas do ColorBrewer. Usa color para linhas/pontos e fill para preenchimentos.

Gráficos base R
# Dispersão
plot(df$idade, df$salario,
  main = "Título", xlab = "Idade", ylab = "Salário")

# Histograma
hist(df$idade, breaks = 20, col = "lightblue")

# Boxplot
boxplot(valor ~ grupo, data = df)

# Barras
barplot(table(df$categoria), col = "gray")

# Linhas
plot(1:10, type = "l", col = "red")
lines(1:10, 10:1, col = "blue")

# Parâmetros gráficos
par(mfrow = c(1, 2))   # 2 gráficos lado a lado

R base inclui gráficos sem pacotes extra: plot() para dispersão, hist() para histogramas, boxplot() e barplot(). par(mfrow) dispõe vários gráficos na mesma janela.

Temas e labels
library(ggplot2)

p <- ggplot(df, aes(x, y)) + geom_point()

# Temas prontos
p + theme_minimal()
p + theme_classic()
p + theme_dark()

# Personalizar elementos
p + theme(
  plot.title = element_text(size = 16, face = "bold"),
  legend.position = "bottom",
  panel.grid = element_blank()
)

# Rótulos completos
p + labs(
  title = "Título",
  subtitle = "Subtítulo",
  caption = "Fonte: dados",
  color = "Grupo"
)

Os temas prontos como theme_minimal() mudam o aspeto global. theme() ajusta elementos individuais com element_text() e element_blank(). labs() define títulos e legendas.

Guardar gráficos
library(ggplot2)

p <- ggplot(df, aes(x, y)) + geom_point()

# ggsave (recomendado)
ggsave("grafico.png", p, width = 10, height = 6, dpi = 300)

# Formato pelo sufixo
ggsave("grafico.pdf", p)
ggsave("grafico.svg", p)

# Dispositivos base R
png("grafico.png", width = 800, height = 600)
plot(1:10)
dev.off()        # fechar e gravar

pdf("grafico.pdf")
hist(df$idade)
dev.off()

ggsave() grava o último gráfico; o formato é detetado pelo sufixo (.png, .pdf, .svg). Em R base, abre um dispositivo como png(), desenha e fecha com dev.off().

Estatística e Modelos


8 cards
Estatística descritiva
v <- c(12, 15, 18, 22, 25, 30)

# Medidas de tendência central
mean(v)       # média
median(v)     # mediana

# Dispersão
sd(v)         # desvio padrão
var(v)        # variância
IQR(v)        # intervalo interquartil
range(v)      # mínimo e máximo

# Quantis
quantile(v)
quantile(v, probs = c(0.1, 0.9))

# Resumo completo
summary(v)
summary(df)   # de todas as colunas

R calcula estatísticas diretamente: mean() e median() para o centro, sd() e var() para a dispersão. quantile() dá os quartis e summary() um resumo rápido de cinco números.

Regressão linear
# Ajustar o modelo
modelo <- lm(salario ~ idade + experiencia, data = df)

# Resumo (coeficientes, R², p-values)
summary(modelo)

# Coeficientes
coef(modelo)
confint(modelo)     # intervalos de confiança

# Prever novos valores
predict(modelo, newdata = novos)

# Fórmulas
lm(y ~ x1 * x2, data = df)        # com interação
lm(y ~ x1 + I(x1^2), data = df)   # termo quadrático
lm(y ~ ., data = df)              # todas as colunas

lm() ajusta uma regressão linear com a fórmula y ~ x. summary() mostra os coeficientes, o R² e os p-values. * adiciona interações e I() permite termos como x^2.

Correlação
# Coeficiente de Pearson
cor(x, y)

# Outros métodos
cor(x, y, method = "spearman")
cor(x, y, method = "kendall")

# Com teste de hipótese
cor.test(x, y)   # valor + p-value

# Matriz de correlação
cor(df[, c("idade", "salario", "nota")])

# Ignorar NAs
cor(x, y, use = "complete.obs")

cor() calcula a correlação entre duas variáveis (por defeito Pearson). cor.test() acrescenta o p-value e o intervalo de confiança. Com várias colunas, devolve uma matriz de correlação.

GLM e regressão logística
# Regressão logística (resultado binário)
modelo <- glm(aprovado ~ nota + horas,
  data = df, family = binomial)

summary(modelo)

# Probabilidades previstas
predict(modelo, newdata = novos, type = "response")

# Regressão de Poisson (contagens)
glm(contagem ~ x, data = df, family = poisson)

# Outras famílias
glm(y ~ x, family = Gamma)
glm(y ~ x, family = gaussian)

# Razões de probabilidades (odds ratios)
exp(coef(modelo))

glm() generaliza a regressão linear através do argumento family. binomial faz regressão logística e poisson modela contagens. type = "response" devolve probabilidades em vez de log-odds.

Distribuições
# Quatro prefixos: d, p, q, r

# r: gerar valores aleatórios
rnorm(100, mean = 0, sd = 1)     # normal
runif(100, min = 0, max = 1)     # uniforme
rbinom(100, size = 10, prob = 0.5)

# d: densidade / probabilidade
dnorm(0)              # densidade em 0
dbinom(5, 10, 0.5)

# p: probabilidade acumulada
pnorm(1.96)           # cerca de 0.975

# q: quantil (inverso de p)
qnorm(0.975)          # cerca de 1.96
qt(0.975, df = 10)

Cada distribuição tem quatro funções: r* gera valores aleatórios, d* dá a densidade, p* a probabilidade acumulada e q* o quantil. Exemplos: rnorm(), pnorm(), qnorm().

Diagnóstico e comparação
modelo <- lm(y ~ x1 + x2, data = df)

# Gráficos de diagnóstico (4)
plot(modelo)

# Intervalos de confiança
confint(modelo)

# Tabela ANOVA
anova(modelo)

# Comparar modelos
m1 <- lm(y ~ x1, data = df)
m2 <- lm(y ~ x1 + x2, data = df)
AIC(m1, m2)        # menor é melhor
anova(m1, m2)      # teste F

# Resíduos
resid(modelo)
fitted(modelo)

plot(modelo) gera quatro gráficos de diagnóstico dos resíduos. AIC() compara modelos (menor é melhor) e anova() testa se adicionar variáveis melhora o ajuste. resid() e fitted() extraem resíduos e valores ajustados.

Testes de hipóteses
# Teste t (uma amostra)
t.test(v, mu = 0)

# Teste t (duas amostras)
t.test(grupo1, grupo2)
t.test(valor ~ grupo, data = df)

# Qui-quadrado
chisq.test(tabela)

# Normalidade
shapiro.test(v)

# Mann-Whitney (não paramétrico)
wilcox.test(grupo1, grupo2)

# ANOVA
modelo <- aov(valor ~ grupo, data = df)
summary(modelo)
TukeyHSD(modelo)   # comparações múltiplas

t.test() compara médias, chisq.test() testa associações em tabelas e shapiro.test() verifica a normalidade. aov() faz análise de variância e TukeyHSD() compara grupos entre si.

broom — modelos tidy
library(broom)

modelo <- lm(salario ~ idade + experiencia, data = df)

# Coeficientes como data frame
tidy(modelo)

# Resumo do modelo (R², AIC...)
glance(modelo)

# Predições com resíduos
augment(modelo)

# Funciona com muitos modelos
tidy(glm(aprovado ~ nota, data = df, family = binomial))
tidy(t.test(grupo1, grupo2))
glance(aov(valor ~ grupo, data = df))

O pacote broom converte resultados de modelos em data.frame limpos. tidy() dá os coeficientes, glance() um resumo de uma linha e augment() os dados com predições e resíduos.