Vetores e Noções Básicas
Variáveis, Tipos e Atribuição
R usa <- como operador de atribuição preferido (= funciona, mas pode ser ambíguo em argumentos de função). Tudo em R é um vetor — mesmo um único número é um vetor de comprimento 1. Os tipos principais são character, numeric (double), integer, logical e complex. NA representa dados ausentes e se propaga pelas operações (use na.rm=TRUE para ignorar). NULL é a ausência de um valor (um objeto vazio), distinto de NA. Sempre verifique NAs antes da análise.
# assignment operators (all equivalent for simple values)
name <- "Alice" # preferred
age = 30L # = also works (avoid in functions)
30L -> age2 # rightward assignment
# basic types (called "modes" in R)
class("text") # "character"
class(42) # "numeric" (double)
class(42L) # "integer"
class(3.14) # "numeric"
class(TRUE) # "logical"
class(2 + 3i) # "complex"
# check and convert types
is.numeric(age) # TRUE
is.character(name) # TRUE
as.integer(3.9) # 3 (truncates, not rounds)
as.character(42) # "42"
as.numeric("3.14") # 3.14
# special values
NA # missing value (Not Available)
NULL # null object (no value)
NaN # Not a Number (0/0)
Inf # infinity (1/0)
is.na(NA) # TRUE
is.null(NULL) # TRUECriação e Indexação de Vetores
c() combina valores em um vetor — a função R mais fundamental. R é indexado em 1 (primeiro elemento é [1], não [0]). Índices negativos EXCLUEM elementos: nums[-1] remove o primeiro. Indexação lógica (nums[nums > 3]) filtra por condição — extremamente poderosa. Vetores podem ter nomes, permitindo acesso por rótulo. Todos os elementos de um vetor devem ser do mesmo tipo; se você misturar tipos, R os coerce (ex.: c(1, 'a') vira c('1', 'a')).
# create vectors with c() (combine)
nums <- c(1, 2, 3, 4, 5)
chars <- c("a", "b", "c")
logs <- c(TRUE, FALSE, TRUE)
# sequences
1:5 # 1 2 3 4 5
seq(1, 10, by = 2) # 1 3 5 7 9
seq(0, 1, length.out = 5) # 0 0.25 0.5 0.75 1
rep(1:3, times = 2) # 1 2 3 1 2 3
rep(1:3, each = 2) # 1 1 2 2 3 3
# indexing (1-indexed!)
nums[1] # 1 (first element)
nums[length(nums)] # 5 (last element)
nums[c(1, 3, 5)] # 1 3 5 (multiple indices)
nums[-1] # 2 3 4 5 (all EXCEPT first)
nums[-c(1, 2)] # 3 4 5 (exclude first two)
nums[2:4] # 2 3 4 (range)
# logical indexing
nums[nums > 2] # 3 4 5
nums[nums %% 2 == 0] # 2 4 (even numbers)
nums[nums > 2] <- 0 # modify in place
# named vectors
ages <- c(alice = 30, bob = 25, carol = 28)
ages["alice"] # 30
ages[c("alice", "bob")] # 30 25Operações e Funções de Vetores
A vetorização é a assinatura do R — operações se aplicam elemento a elemento automaticamente, sem necessidade de loops. Recycling reutiliza o vetor mais curto para igualar o mais longo (c(1,2,3,4) + c(10,20) dá 11,22,13,24). order() retorna os índices que ordenariam o vetor — essencial para ordenar um vetor por outro. unique() remove duplicatas. Todas essas funções são código C otimizado por baixo, tornando R rápido para operações vetoriais.
# arithmetic is element-wise (vectorized)
a <- c(1, 2, 3)
b <- c(4, 5, 6)
a + b # 5 7 9
a * b # 4 10 18
a ^ 2 # 1 4 9
a / b # 0.25 0.4 0.5
# recycling: shorter vector repeats
c(1, 2, 3, 4) + c(10, 20) # 11 22 13 24
# summary functions
sum(a) # 6
mean(a) # 2
median(a) # 2
sd(a) # 1
var(a) # 1
min(a); max(a) # 1; 3
range(a) # 1 3
cumsum(a) # 1 3 6
cumprod(a) # 1 2 6
# sorting and ordering
sort(c(3, 1, 2)) # 1 2 3
sort(c(3, 1, 2), decreasing = TRUE) # 3 2 1
order(c(3, 1, 2)) # 2 3 1 (indices that would sort)
v[order(v)] # sort using order
# useful functions
length(a) # 3
unique(c(1, 1, 2, 2, 3)) # 1 2 3
rev(a) # 3 2 1
head(a, 2) # 1 2 (first n)
tail(a, 2) # 2 3 (last n)Manipulação de Caracteres e Strings
paste/paste0 são as funções de concatenação de strings do R — paste0 não tem separador (como o + do Python). substr extrai substrings (indexado em 1). gsub substitui todas as ocorrências; sub substitui apenas a primeira. grep retorna índices de elementos correspondentes; grepl retorna um vetor lógico (mais útil para filtrar). R usa regex POSIX estendido por padrão. sprintf fornece formatação estilo C. O pacote stringr (tidyverse) oferece uma API mais limpa e consistente.
# paste and paste0 (concatenation)
paste("Hello", "World") # "Hello World"
paste("Hello", "World", sep = "_") # "Hello_World"
paste0("a", "b", "c") # "abc" (no separator)
paste(c("a", "b", "c"), collapse = "-") # "a-b-c"
paste("file", 1:3, ".csv", sep = "") # "file1.csv" "file2.csv" "file3.csv"
# case conversion
toupper("hello") # "HELLO"
tolower("WORLD") # "world"
# substring
substr("Hello World", 1, 5) # "Hello"
nchar("Hello") # 5 (character count)
# split and replace
strsplit("a,b,c", ",")[[1]] # "a" "b" "c"
gsub("o", "0", "Hello World") # "Hell0 W0rld" (all matches)
sub("o", "0", "Hello World") # "Hell0 World" (first match only)
gsub("[0-9]+", "N", "a1b22c333") # "aNbNcN" (regex)
# grep and grepl (pattern matching)
grep("^A", c("Alice", "Bob", "Anna")) # 1 3 (indices)
grepl("^A", c("Alice", "Bob")) # TRUE FALSE
# sprintf (C-style formatting)
sprintf("Pi = %.2f", pi) # "Pi = 3.14"
sprintf("%s is %d", "Alice", 30) # "Alice is 30"Valores Ausentes e Coerção
NA (Not Available) representa dados ausentes e se propaga pela maioria das operações — sempre use na.rm=TRUE ou filtre-os. NULL é diferente: é a ausência de um valor e é removido dos vetores. R coerce para o tipo mais geral ao combinar (logical < integer < numeric < character). as.numeric em strings não numéricas produz NA com um aviso. ifelse é o operador ternário vectorizado — extremamente útil para criar variáveis categóricas a partir de contínuas.
# NA propagation and handling
x <- c(1, 2, NA, 4, 5)
mean(x) # NA (NA propagates!)
mean(x, na.rm = TRUE) # 3 (skip NA)
sum(x, na.rm = TRUE) # 12
is.na(x) # FALSE FALSE TRUE FALSE FALSE
sum(is.na(x)) # 1 (count of NAs)
x[!is.na(x)] # 1 2 4 5 (remove NAs)
# NULL vs NA
length(c(1, NA, 3)) # 3 (NA is an element)
length(c(1, NULL, 3)) # 2 (NULL is dropped)
# type coercion hierarchy: logical < integer < double < character
c(TRUE, 1) # 1 1 (logical -> numeric)
c(1L, 2.5) # 1.0 2.5 (integer -> double)
c(1, "a") # "1" "a" (numeric -> character)
# explicit coercion
as.numeric(c("1", "2", "abc")) # 1 2 NA (with warning)
as.logical(c(0, 1, 2)) # FALSE TRUE TRUE
factor(c("low", "high", "low")) # factor with 2 levels
# ifelse vectorized conditional
ifelse(x > 2, "big", "small") # "small" "small" NA "big" "big"Estruturas de Dados
Listas (Contêineres Heterogêneos)
Listas são a estrutura de dados mais flexível do R — podem conter elementos de qualquer tipo e tamanho (como dicts do Python ou objetos do JavaScript). O operador $ é um atalho conveniente para acesso nomeado. A distinção crítica: [ ] retorna uma sublista (ainda uma lista), enquanto [[ ]] extrai o elemento real. Esta é a fonte nº 1 de confusão para iniciantes em R. Use [[ ]] quando quiser o valor em si, [ ] quando quiser subconjuntar. lapply/sapply iteram sobre os elementos da lista aplicando uma função.
# lists can hold different types and sizes
user <- list(
name = "Alice",
age = 30,
scores = c(90, 85, 88),
active = TRUE
)
# access by name ($ or [[]])
user$name # "Alice"
user[["age"]] # 30
user[["scores"]][2] # 85
# [] returns a sublist (list); [[]] returns the element
user["name"] # list with one element
user[["name"]] # "Alice" (the string itself)
user[1:2] # sublist with first 2 elements
# modify and add
user$age <- 31
user$email <- "[email protected]" # add new element
user[["scores"]] <- NULL # remove element
# iterate over a list
for (key in names(user)) {
cat(key, ":", user[[key]], "\n")
}
# lapply and sapply on lists
lapply(user$scores, sqrt) # list of square roots
sapply(user$scores, sqrt) # vector of square rootsData Frames
Data frames são a principal estrutura de dados tabular do R — como uma planilha ou tabela SQL onde cada coluna pode ser de um tipo diferente. Acesse colunas com $ ou [[ ]]; filtre linhas com indexação lógica (df[df$age > 25, ]). subset() é uma alternativa mais limpa. cbind adiciona colunas; rbind adiciona linhas. str() mostra a estrutura (tipos e prévia). Sempre defina stringsAsFactors=FALSE (ou use R 4.0+ onde este é o padrão) para manter strings como characters, não factors.
# create a data frame (like a table/spreadsheet)
df <- data.frame(
name = c("Alice", "Bob", "Carol"),
age = c(30, 25, 28),
score = c(90, 85, 88),
stringsAsFactors = FALSE
)
# dimensions
nrow(df) # 3
ncol(df) # 3
dim(df) # 3 3
names(df) # "name" "age" "score"
str(df) # structure summary
head(df, 2) # first 2 rows
# access columns
df$name # vector (by name)
df[["age"]] # vector (by name, alternative)
df[, "age"] # vector (column)
df[, 2] # vector (by index)
df[2] # data frame with one column
# access rows
df[1, ] # first row (as data frame)
df[1:2, ] # first 2 rows
df[c(1, 3), ] # rows 1 and 3
# filter rows (logical indexing)
df[df$age > 26, ] # rows where age > 26
df[df$score >= 88, c("name", "score")]
subset(df, age > 26, select = c(name, score))
# add and modify columns
df$grade <- c("A", "B", "A") # add column
df$age <- df$age + 1 # modify column
df <- cbind(df, pass = df$score > 60) # column bind
# add rows
new_row <- data.frame(name = "Dan", age = 22, score = 75, grade = "C")
df <- rbind(df, new_row)Matrizes e Arrays
Matrizes são arrays 2D onde TODOS os elementos devem ser do mesmo tipo (ao contrário de data frames). %*% é multiplicação de matrizes; * é elemento a elemento. solve() calcula a inversa da matriz; det() o determinante. rowSums/colSums/rowMeans/colMeans são atalhos rápidos embutidos. apply(m, MARGIN, FUN) é a forma geral de aplicar uma função entre linhas (MARGIN=1) ou colunas (MARGIN=2). Arrays estendem matrizes para n dimensões. Para análise de dados, prefira data frames; use matrizes para álgebra linear.
# create a matrix (2D, all same type)
m <- matrix(1:12, nrow = 3, ncol = 4)
# [,1] [,2] [,3] [,4]
# [1,] 1 4 7 10
# [2,] 2 5 8 11
# [3,] 3 6 9 12
# fill by row instead of column
m2 <- matrix(1:6, nrow = 2, byrow = TRUE)
# dimensions and attributes
dim(m) # 3 4
nrow(m) # 3
ncol(m) # 4
rownames(m) <- c("r1", "r2", "r3")
colnames(m) <- c("c1", "c2", "c3", "c4")
# indexing
m[2, 3] # 8 (single element)
m[1, ] # 1 4 7 10 (first row)
m[, 2] # 4 5 6 (second column)
m[1:2, 2:3] # 2x2 submatrix
m["r1", "c2"] # 4 (by name)
# matrix operations
t(m) # transpose
m * m # element-wise multiply
m %*% t(m) # matrix multiplication
solve(m[, 1:3]) # inverse (square matrix)
det(matrix(1:4, 2)) # determinant
rowSums(m) # sum of each row
colMeans(m) # mean of each column
apply(m, 1, sum) # row sums (general)
apply(m, 2, max) # column maxima
# arrays (n-dimensional)
arr <- array(1:24, dim = c(2, 3, 4)) # 2x3x4 array
arr[1, 2, 3] # single elementFatores e Dados Categóricos
Fatores armazenam dados categóricos de forma eficiente como códigos inteiros com mapeamentos de rótulos — essenciais para modelagem estatística (lm, glm usam fatores para agrupamento). Uma armadilha comum: as.numeric(factor) fornece os CÓDIGOS inteiros, não os valores originais — sempre converta via as.character primeiro. cut() agrupa dados contínuos em níveis de fator. ordered=TRUE cria fatores ordinais que suportam operadores de comparação. relevel muda a categoria de referência (importante para interpretação de regressão). table() produz contagens de frequência.
# factors represent categorical data (stored as integers with labels)
gender <- factor(c("male", "female", "male", "female"))
print(gender)
# [1] male female male female
# Levels: female male
levels(gender) # "female" "male" (sorted alphabetically)
table(gender) # frequency table
nlevels(gender) # 2
# ordered factor (for ordinal data)
size <- factor(c("M", "S", "L", "XL"),
levels = c("S", "M", "L", "XL"),
ordered = TRUE)
size[1] > size[2] # TRUE (M > S)
# convert to factor and back
nums <- factor(c(1, 2, 3, 2, 1))
as.numeric(nums) # 1 2 3 2 1 (level codes, NOT original values!)
as.numeric(as.character(nums)) # 1 2 3 2 1 (correct way)
# relevel (change reference level)
gender <- relevel(gender, ref = "male")
# cut: convert numeric to factor (binning)
ages <- c(15, 25, 35, 45, 55, 65)
age_groups <- cut(ages, breaks = c(0, 18, 35, 50, 100),
labels = c("child", "young", "adult", "senior"))
table(age_groups) # frequency per group
# interaction of factors
f1 <- factor(c("A", "A", "B", "B"))
f2 <- factor(c("X", "Y", "X", "Y"))
interaction(f1, f2) # A.X A.Y B.X B.YListas para Data Frames e Reshaping
Remodelar entre formatos wide e long é uma tarefa comum de data wrangling. pivot_longer/pivot_wider (tidyr, tidyverse) são as funções modernas e intuitivas. O formato wide tem uma linha por sujeito com colunas para cada ponto no tempo; o formato long tem uma linha por observação. O formato long é preferido para ggplot2 e a maioria das análises. split() divide um data frame em uma lista por um fator; do.call(rbind, ...) recombiná. A função reshape() do R base é poderosa, mas tem uma interface confusa — prefira tidyr.
# convert list to data frame
my_list <- list(
a = 1:3,
b = c("x", "y", "z")
)
df <- as.data.frame(my_list)
# stack multiple vectors
do.call(rbind, list(
data.frame(name = "A", val = 1),
data.frame(name = "B", val = 2)
))
# reshape: wide to long
library(tidyr)
wide_df <- data.frame(
id = 1:2,
q1 = c(10, 20),
q2 = c(15, 25)
)
long_df <- pivot_longer(wide_df, cols = starts_with("q"),
names_to = "quarter", values_to = "value")
# long to wide
pivot_wider(long_df, names_from = quarter, values_from = value)
# base R reshape (without tidyr)
reshape(wide_df, direction = "long",
varying = c("q1", "q2"), v.names = "value",
timevar = "quarter", idvar = "id")
# split and combine
split_results <- split(df, df$group) # list of data frames by group
combined <- do.call(rbind, split_results) # recombine
# melt and cast (reshape2 package, legacy)
# library(reshape2)
# melt(wide_df, id.vars = "id")
# dcast(long_df, id ~ quarter, value.var = "value")Controle de Fluxo e Funções
If / Else e Switch
O if/else do R exige chaves para corpos multilinha e o else deve estar na mesma linha da chave de fechamento (ou R achará que o if está completo). ifelse(test, yes, no) é vectorizado — aplica-se a vetores inteiros de uma vez, retornando um vetor de resultados. Para múltiplas condições, case_when do dplyr é muito mais limpo que ifelse aninhado. switch despacha em uma string (ou posição numérica) — uma alternativa limpa para longas cadeias if-else.
# if-else if-else
score <- 85
if (score >= 90) {
grade <- "A"
} else if (score >= 80) {
grade <- "B"
} else {
grade <- "C"
}
# ifelse (vectorized ternary)
ages <- c(15, 25, 35, 45)
ifelse(ages >= 18, "adult", "minor")
# "minor" "adult" "adult" "adult"
# nested ifelse (avoid deep nesting!)
ifelse(ages < 18, "minor",
ifelse(ages < 65, "adult", "senior"))
# dplyr::case_when is cleaner for multiple conditions
# case_when(
# ages < 18 ~ "minor",
# ages < 65 ~ "adult",
# TRUE ~ "senior"
# )
# switch (dispatch on a value)
day_type <- function(day) {
switch(day,
"Mon" = "weekday",
"Tue" = "weekday",
"Wed" = "weekday",
"Thu" = "weekday",
"Fri" = "weekday",
"Sat" = "weekend",
"Sun" = "weekend",
"unknown"
)
}Loops: For, While, Repeat
loops for no R iteram sobre elementos (ou uma sequência). seq_along(x) é mais seguro que 1:length(x) quando x pode estar vazio (retorna integer(0) em vez de c(1,0)). next pula para a próxima iteração (como continue); break sai. repeat é um loop infinito que deve ser interrompido explicitamente. SEMPRE pré-aloque vetores de resultado (result <- numeric(N)) — crescer um vetor em um loop com c() é O(n²) e extremamente lento. No entanto, prefira operações vectorizadas ou a família apply em vez de loops quando possível.
# for loop
for (i in 1:5) {
print(i)
}
# iterate over a vector
fruits <- c("apple", "banana", "cherry")
for (fruit in fruits) {
print(paste("Fruit:", fruit))
}
# iterate with index
for (i in seq_along(fruits)) {
print(paste(i, fruits[i]))
}
# while loop
count <- 0
while (count < 5) {
count <- count + 1
if (count == 3) next # skip (like continue)
print(count)
}
# repeat loop (infinite, must break)
i <- 0
repeat {
i <- i + 1
if (i >= 3) break # exit loop
print(i)
}
# preallocate for speed (IMPORTANT!)
result <- numeric(1000)
for (i in 1:1000) {
result[i] <- i^2
}
# nested loops
mat <- matrix(0, 3, 3)
for (i in 1:3) {
for (j in 1:3) {
mat[i, j] <- i * j
}
}Definição de Funções e Argumentos
Funções em R retornam a última expressão avaliada automaticamente (não é preciso return explícito, embora return() seja mais claro para saídas antecipadas). Argumentos padrão tornam as funções flexíveis. ... (reticências) captura argumentos extras para repassar — essencial para funções wrapper. Argumentos nomeados podem estar em qualquer ordem. R usa avaliação preguiçosa: argumentos só são avaliados quando usados pela primeira vez, então argumentos não usados não causam erros. Retorne múltiplos valores empacotando-os em uma lista.
# basic function (last expression is returned)
add <- function(a, b) {
a + b
}
add(3, 4) # 7
# explicit return
is_positive <- function(x) {
if (x > 0) return(TRUE)
FALSE
}
# default arguments
greet <- function(name, greeting = "Hello", punctuation = "!") {
paste0(greeting, ", ", name, punctuation)
}
greet("Alice") # "Hello, Alice!"
greet("Bob", greeting = "Hi") # "Hi, Bob!"
greet(name = "Carol", punctuation = "?") # named args
# ... (ellipsis: pass arguments through)
my_plot <- function(x, y, ...) {
plot(x, y, col = "blue", ...)
}
my_plot(1:10, 1:10, main = "My Plot", type = "l")
# return multiple values via list
stats <- function(x) {
list(mean = mean(x), sd = sd(x), n = length(x))
}
result <- stats(1:10)
result$mean # 5.5
# lazy evaluation (args evaluated only when used)
f <- function(a, b) {
a * 2 # b is never used, so not evaluated
}
f(5) # 10 (no error despite missing b)A Família Apply
A família apply substitui loops por iteração funcional — mais idiomático e frequentemente mais rápido. lapply sempre retorna uma lista; sapply tenta simplificar para um vetor/matriz (conveniente, mas imprevisível); vapply é a versão segura com tipo de retorno garantido. apply funciona em matrizes (MARGIN=1 para linhas, 2 para colunas). tapply agrupa dados por um fator e aplica uma função — como um mini GROUP BY. replicate repete simulações aleatórias. Para data frames, o pacote purrr (tidyverse) oferece uma família map() mais limpa e consistente.
# lapply: apply function to each element of a list, returns list
my_list <- list(a = 1:3, b = 4:6, c = 7:9)
lapply(my_list, mean) # list: 2, 5, 8
lapply(my_list, sum) # list: 6, 15, 24
# sapply: like lapply but simplifies to vector/matrix
sapply(my_list, mean) # 2 5 8 (named vector)
sapply(my_list, range) # matrix with 2 rows
# vapply: like sapply but with guaranteed output type (safer)
vapply(my_list, mean, numeric(1)) # always numeric vector
# apply: apply function over matrix/array margins
m <- matrix(1:12, nrow = 3)
apply(m, 1, sum) # row sums: 22 26 30
apply(m, 2, mean) # column means: 2 5 8 11
apply(m, c(1, 2), sqrt) # element-wise sqrt
# mapply: multivariate apply (vectorized over multiple args)
mapply(rep, 1:3, 3:1) # rep(1,3), rep(2,2), rep(3,1)
# tapply: apply function by groups
df <- data.frame(
group = c("A", "A", "B", "B", "B"),
value = c(10, 20, 30, 40, 50)
)
tapply(df$value, df$group, mean) # A: 15, B: 40
# replicate: repeat an expression n times
replicate(3, mean(rnorm(10))) # 3 random meansEscopo e Ambientes
R usa escopo léxico: funções procuram variáveis livres no ambiente onde foram definidas (não onde são chamadas). Isso habilita closures — funções que capturam seu ambiente envolvente. O operador <<- atribui a uma variável no ambiente pai (super-assignment), que é como closures mantêm estado (como o exemplo do contador). Cada chamada de função cria um novo ambiente. O caminho de busca (search()) determina onde R procura objetos — globalenv é seu workspace, seguido pelos pacotes anexados.
# lexical scoping: functions look up variables in defining environment
y <- 10
f <- function(x) {
x + y # y found in global env
}
f(5) # 15
# local variables shadow globals
g <- function(x) {
y <- 100 # local y
x + y
}
g(5) # 105
y # 10 (global unchanged)
# <<- assigns to parent environment (super-assignment)
counter <- function() {
count <- 0
function() {
count <<- count + 1 # modifies count in enclosing scope
count
}
}
c1 <- counter()
c1() # 1
c1() # 2
# search path for variables
search() # shows environments: globalenv, package namespaces
ls() # list objects in current environment
ls(envir = .GlobalEnv) # explicitly global
exists("y") # TRUE if variable exists
# new environment (isolated scope)
e <- new.env()
e$x <- 42
e$x # 42 (separate from global x)Manipulação de Dados (dplyr e tidyr)
dplyr: Filter, Select e Arrange
dplyr (parte do tidyverse) fornece verbos intuitivos para manipulação de dados que espelham operações SQL. filter seleciona linhas por condição; select escolhe colunas; arrange ordena. O operador %in% testa pertinência. Funções auxiliares como starts_with, ends_with, contains tornam a seleção de colunas flexível. rename() muda nomes de colunas sem copiar. Esses verbos se compõem com o pipe (%>%) para pipelines de dados legíveis. dplyr é muito mais rápido que R base para dados grandes porque usa C++ internamente.
library(dplyr)
df <- data.frame(
name = c("Alice", "Bob", "Carol", "Dan"),
age = c(30, 25, 28, 35),
dept = c("Eng", "Sales", "Eng", "Sales"),
salary = c(80000, 50000, 75000, 90000)
)
# filter rows (like WHERE in SQL)
filter(df, age > 26)
filter(df, dept == "Eng" & salary > 70000)
filter(df, dept %in% c("Eng", "Sales"))
filter(df, age > 26 | salary > 85000)
# select columns (like SELECT in SQL)
select(df, name, age)
select(df, name:dept) # range of columns
select(df, -salary) # exclude column
select(df, starts_with("s")) # columns starting with 's'
select(df, ends_with("e")) # columns ending with 'e'
select(df, contains("am")) # columns containing 'am'
select(df, everything()) # all columns (useful for reordering)
# arrange (sort, like ORDER BY)
arrange(df, age) # ascending
arrange(df, desc(age)) # descending
arrange(df, dept, desc(salary)) # sort by dept, then salary desc
# rename columns
rename(df, years = age, department = dept)
# distinct rows
distinct(df, dept) # unique departments
distinct(df, dept, .keep_all = TRUE) # keep all columnsdplyr: Mutate, Summarize e Group By
mutate adiciona ou modifica colunas (vectorizado). summarize reduz cada grupo a uma única linha de resumo. O poder vem de group_by + summarize — o equivalente em R do GROUP BY do SQL. n() conta linhas; across() aplica uma função a múltiplas colunas (novo no dplyr 1.0). Funções de janela (rank, cumsum, lag, lead) operam dentro de grupos, permitindo cálculos como 'rank dentro do departamento'. O pipe %>% encadeia operações da esquerda para a direita, tornando pipelines complexos legíveis.
library(dplyr)
# mutate: add/modify columns
df %>%
mutate(
bonus = salary * 0.1,
total = salary + bonus,
category = ifelse(age > 30, "senior", "junior")
)
# transmute: like mutate but only keeps new columns
transmute(df, name, annual = salary, monthly = salary / 12)
# summarize (reduces to single row per group)
summarize(df,
avg_salary = mean(salary),
max_age = max(age),
n = n()
)
# group_by + summarize (like GROUP BY in SQL)
df %>%
group_by(dept) %>%
summarize(
count = n(),
avg_salary = mean(salary),
avg_age = mean(age)
) %>%
arrange(desc(avg_salary))
# multiple summaries
df %>%
group_by(dept) %>%
summarize(across(everything(), list(mean, sd)))
# count and tally
count(df, dept) # count per dept
df %>% group_by(dept) %>% tally()
# window functions (within groups)
df %>%
group_by(dept) %>%
mutate(
rank = rank(desc(salary)),
cumsum = cumsum(salary)
) %>%
arrange(dept, rank)O Operador Pipe (%>%)
O pipe (%>% do magrittr/dplyr) passa o lado esquerdo como primeiro argumento para o lado direito, transformando chamadas de função aninhadas em pipelines legíveis da esquerda para a direita. Esta é a característica definidora do estilo tidyverse. O ponto (.) representa os dados do pipe quando você precisa deles explicitamente. %$% expõe nomes de colunas; %<>% atribui de volta; %T>% continua o pipe após um efeito colateral (como plotar). R 4.1+ tem um pipe nativo |>, mas é menos flexível (sem placeholder de ponto). Pipes tornam o código de data wrangling dramaticamente mais legível.
library(magrittr) # or library(dplyr)
# without pipe: nested, hard to read
result <- arrange(
filter(
select(df, name, age, salary),
age > 25
),
desc(salary)
)
# with pipe: linear, readable
result <- df %>%
select(name, age, salary) %>%
filter(age > 25) %>%
arrange(desc(salary))
# the dot (.) refers to the piped data
df %>% plot(.$age, .$salary) # . = df
# %$% exposes column names (magrittr)
df %$% cor(age, salary) # correlation
# %<>% assigns result back (compound assignment)
df %<>% filter(age > 20)
# T pipe: returns first argument (for side effects)
rnorm(100) %T>%
hist() %>% # plot histogram (returns input)
mean() # then compute mean
# native pipe (R 4.1+): |>
df |>
subset(age > 25) |>
colMeans()Juntando Data Frames
As funções de join do dplyr espelham joins do SQL: inner_join (interseção), left_join (todas as linhas da esquerda), right_join, full_join (união). semi_join filtra para linhas com correspondências (sem adicionar colunas); anti_join encontra linhas sem correspondências — ambos úteis para validação de dados. O argumento by especifica a chave de join; use um vetor nomeado (c('id' = 'emp_id')) quando os nomes das colunas diferem. Joins são muito mais rápidos que merge() do R base. Sempre verifique contagens de linhas antes e depois de juntar para detectar duplicatas inesperadas.
library(dplyr)
employees <- data.frame(
id = c(1, 2, 3, 4),
name = c("Alice", "Bob", "Carol", "Dan")
)
salaries <- data.frame(
id = c(1, 2, 3, 5),
salary = c(80000, 50000, 75000, 60000)
)
# inner join: only matching rows
inner_join(employees, salaries, by = "id")
# id name salary
# 1 Alice 80000
# 2 Bob 50000
# 3 Carol 75000
# left join: all rows from left, NA for non-matches
left_join(employees, salaries, by = "id")
# Dan has NA salary
# right join: all rows from right
right_join(employees, salaries, by = "id")
# id 5 has NA name
# full join: all rows from both
full_join(employees, salaries, by = "id")
# different column names
left_join(employees, salaries, by = c("id" = "id"))
# semi join: rows in left that have a match (no right columns)
semi_join(employees, salaries, by = "id")
# anti join: rows in left with NO match
anti_join(employees, salaries, by = "id")
# Dan (id 4 has no salary)
# multiple keys
left_join(df1, df2, by = c("dept" = "department", "year" = "yr"))tidyr: Reshaping de Dados
tidyr (tidyverse) lida com reshaping de dados. pivot_longer/wider substituem os legados gather/spread — são mais intuitivos e flexíveis. Dados tidy têm uma linha por observação e uma coluna por variável; pivot_longer converte dados wide para esse formato (necessário para ggplot2). separate/unite dividem e mesclam colunas. separate_rows explode valores delimitados em múltiplas linhas. drop_na/replace_na/fill lidam com dados ausentes de forma limpa. Esses verbos se compõem com dplyr via pipe para pipelines de dados poderosos.
library(tidyr)
# wide to long
wide_df <- data.frame(
id = 1:2,
q1_sales = c(100, 200),
q2_sales = c(150, 250),
q3_sales = c(120, 220)
)
long_df <- wide_df %>%
pivot_longer(
cols = starts_with("q"),
names_to = "quarter",
values_to = "sales"
)
# long to wide
long_df %>%
pivot_wider(
names_from = quarter,
values_from = sales
)
# separate one column into multiple
df <- data.frame(id = 1, name = "Alice_Smith")
df %>% separate(name, into = c("first", "last"), sep = "_")
# unite multiple columns into one
df %>% unite("full_name", first, last, sep = " ")
# separate_rows: split delimited values into rows
df <- data.frame(id = 1:2, tags = c("a,b,c", "x,y"))
df %>% separate_rows(tags, sep = ",")
# drop NA values
df %>% drop_na() # drop rows with any NA
df %>% drop_na(salary) # drop rows where salary is NA
# replace NA with a value
df %>% replace_na(list(salary = 0, name = "Unknown"))
# fill missing values (carry forward)
df %>% fill(salary, .direction = "down")Estatística e Modelagem
Estatística Descritiva
summary() é a forma mais rápida de obter uma visão geral de quaisquer dados — mostra min, quartis, mediana, média e max. sd() e var() computam as estatísticas da AMOSTRA (n-1). cor() mede associação linear (Pearson) ou de rank (Spearman). Sempre use na.rm=TRUE com dados do mundo real contendo NAs. Para data frames, summary(df) fornece estatísticas por coluna. Os pacotes psych e Hmisc fornecem estatísticas descritivas estendidas.