Cheatsheet R
Linguagem para estatística, análise de dados e visualização
R
Básico
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) # TRUEOs 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 palavrasR 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ívelUm 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"] # 10A 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
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 2while 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
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 * 2Uma 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") # TRUEO 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
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ísticoUm 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
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
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 versionadoO 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
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
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.