R nasceu para estatística, e isso molda tudo na linguagem: o vetor é o cidadão de primeira classe, a indexação começa em 1, e operações são vetorizadas por padrão. Se você vem de Python, vai encontrar decisões de design que parecem estranhas no início e brilhantes depois. Este complemento cobre o R "puro" — a base sobre a qual o tidyverse e o tidymodels de Fundamentos de R para ML são construídos.
Dominar os fundamentos da linguagem R: atribuição, tipos, vetores, recycling, fatores, listas, data.frames, controle de fluxo, funções e a família apply — o suficiente para ler e escrever código R idiomático em projetos de ML.
Atribuição e o operador <-
Em R, a atribuição idiomática usa <- (embora = funcione na maioria dos contextos).
x <- 10 # forma idiomática
y = 5 # funciona, mas evite em código de nível superior
z <- x + y
print(z) # 15
z # no console, digitar o nome já imprime
# Atribuição para a direita também existe (raro)
42 -> resposta
Por que <- e não =? Historicamente, = é reservado para argumentos de função (mean(x, na.rm = TRUE)), e separar os dois evita ambiguidades. Use <- para variáveis, = para argumentos.
Tipos atômicos
R tem seis tipos básicos; os que você usará o tempo todo são quatro:
inteiro <- 42L # o "L" força integer
numerico <- 3.14 # double (padrão para números)
texto <- "machine learning"
logico <- TRUE # ou FALSE (também T / F, mas prefira por extenso)
class(inteiro) # "integer"
class(numerico) # "numeric"
class(texto) # "character"
class(logico) # "logical"
# Conversões explícitas
as.numeric("3.5") # 3.5
as.integer(3.9) # 3 (trunca)
as.character(100) # "100"
as.logical(0) # FALSE
O vetor é tudo
Em R não existe escalar — o que parece um número é um vetor de comprimento 1. Isso torna a vetorização natural.
v <- c(10, 20, 30, 40, 50) # c() = "combine"
length(v) # 5
v[1] # 10 (indexação começa em 1!)
v[length(v)] # 50 (não existe v[-1] para "último")
# Operações são vetorizadas por padrão
v * 2 # 20 40 60 80 100
v + 100 # 110 120 130 140 150
sqrt(v) # raiz de cada elemento
# Vetores são homogêneos: um só tipo. R "promove" se misturar:
misto <- c(1, "dois", TRUE) # tudo vira character
class(misto) # "character"
Indexação — o ponto que mais confunde quem vem do Python
v <- c(10, 20, 30, 40, 50)
v[1] # 10 — começa em 1, não em 0
v[c(1, 3, 5)] # 10 30 50 — indexa por vetor de posições
v[2:4] # 20 30 40 — intervalo (inclusivo nas duas pontas)
v[-1] # 20 30 40 50 — índice NEGATIVO REMOVE o elemento!
v[-c(1, 2)] # 30 40 50 — remove os dois primeiros
v[v > 25] # 30 40 50 — indexação lógica (máscara)
A armadilha clássica: em Python v[-1] é o último elemento; em R v[-1] remove o primeiro. Para o último, use tail(v, 1) ou v[length(v)].
Recycling (reciclagem)
Se dois vetores têm comprimentos diferentes, R "recicla" o menor. Poderoso e perigoso:
c(1, 2, 3, 4) + c(10, 20) # 11 22 13 24 (10,20 é reciclado)
c(1, 2, 3) + c(10, 20) # aviso: comprimento não é múltiplo
Valores faltantes: NA, NULL, NaN
x <- c(1, 2, NA, 4)
mean(x) # NA — qualquer conta com NA vira NA
mean(x, na.rm = TRUE) # 2.333 — remove NAs antes
is.na(x) # FALSE FALSE TRUE FALSE
sum(is.na(x)) # 1 — conta faltantes
# NA (faltante) ≠ NULL (ausência de objeto) ≠ NaN (0/0)
NULL # objeto vazio, comprimento 0
0 / 0 # NaN
na.rm = TRUE é um argumento que você vai digitar milhares de vezes. Faltantes se propagam silenciosamente — sempre verifique com is.na.
Fatores — categorias com nível
O fator é o tipo de R para variáveis categóricas. Fundamental em modelagem, porque muitos modelos tratam fatores automaticamente.
regiao <- factor(c("sul", "norte", "sul", "leste", "norte"))
regiao
levels(regiao) # "leste" "norte" "sul" (ordem alfabética)
as.integer(regiao) # 3 2 3 1 2 (codificação interna)
# Fator ordenado (para variáveis ordinais)
escolaridade <- factor(
c("médio", "superior", "fundamental"),
levels = c("fundamental", "médio", "superior"),
ordered = TRUE
)
escolaridade < "superior" # TRUE TRUE FALSE — comparação faz sentido
Cuidado: converter fator para número com as.numeric direto retorna os códigos internos, não os valores. Para fatores que representam números, use as.numeric(as.character(f)).
Listas — o contêiner heterogêneo
Vetores são homogêneos; listas guardam qualquer coisa, inclusive outras listas. Modelos treinados em R são, por baixo, listas.
pessoa <- list(
nome = "Ana",
idade = 30,
scores = c(0.82, 0.85, 0.79),
ativo = TRUE
)
pessoa$nome # "Ana" — acesso por nome com $
pessoa[["idade"]] # 30 — acesso por nome com [[ ]]
pessoa[["scores"]][2] # 0.85
str(pessoa) # inspeciona a estrutura (muito útil)
names(pessoa) # "nome" "idade" "scores" "ativo"
[ ] retorna uma sub-lista; [[ ]] retorna o elemento em si. Essa distinção confunde no começo — [[ ]] "entra" no elemento, [ ] "fatia" a lista.
data.frame — a tabela
O data.frame é a estrutura tabular base (colunas podem ter tipos diferentes; cada coluna é um vetor). O tibble, visto em Fundamentos de R para ML, é uma versão modernizada dele.
df <- data.frame(
idade = c(25, 42, 31),
renda = c(3200, 8500, 4100),
regiao = c("sul", "sul", "norte")
)
df$idade # coluna como vetor
df[1, ] # primeira linha (todas as colunas)
df[, "renda"] # coluna renda
df[df$idade > 30, ] # filtro por condição
nrow(df); ncol(df) # 3 3
names(df) # "idade" "renda" "regiao"
summary(df) # resumo estatístico por coluna
head(df, 2) # primeiras 2 linhas
Controle de fluxo
# if / else
nota <- 7.5
if (nota >= 7) {
print("aprovado")
} else if (nota >= 5) {
print("recuperação")
} else {
print("reprovado")
}
# for — evite quando puder vetorizar
total <- 0
for (i in 1:5) {
total <- total + i
}
total # 15
# while
n <- 10
while (n > 0) {
n <- n - 2
}
# ifelse VETORIZADO — opera sobre vetores inteiros
idades <- c(15, 22, 17, 40)
ifelse(idades >= 18, "adulto", "menor")
# "menor" "adulto" "menor" "adulto"
Em R, if só aceita uma condição de comprimento 1. Para decisões sobre vetores inteiros, use ifelse() (vetorizado) ou, em versões recentes, dplyr::case_when().
Funções
# Definição
media_ponderada <- function(valores, pesos) {
sum(valores * pesos) / sum(pesos)
}
media_ponderada(c(8, 6, 9), c(0.5, 0.3, 0.2)) # 7.6
# A última expressão é o valor de retorno (return é opcional)
quadrado <- function(x) x^2 # função de uma linha
# Argumentos com valor padrão
normalizar <- function(x, centro = TRUE, escala = TRUE) {
if (centro) x <- x - mean(x)
if (escala) x <- x / sd(x)
x
}
normalizar(c(1, 2, 3, 4, 5))
R é uma linguagem funcional: funções são objetos de primeira classe, podem ser passadas como argumento e retornadas — a base da família apply.
A família apply — vetorização de alto nível
Em vez de loops, R aplica funções sobre estruturas. É mais idiomático e legível.
m <- matrix(1:12, nrow = 3)
apply(m, 1, sum) # soma por LINHA (margin=1): 22 26 30
apply(m, 2, sum) # soma por COLUNA (margin=2): 6 15 24 33
# lapply: aplica função a cada elemento, retorna LISTA
lapply(1:3, function(x) x^2) # list(1, 4, 9)
# sapply: como lapply, mas simplifica para vetor quando possível
sapply(1:3, function(x) x^2) # 1 4 9
# vapply: sapply com tipo de retorno declarado (mais seguro)
vapply(1:3, function(x) x^2, numeric(1))
# map (purrr) é a versão moderna e consistente
Regra prática: sapply/vapply para resultados simples, lapply quando precisar de lista, apply sobre linhas/colunas de matrizes. No mundo tidyverse, purrr::map_* substitui tudo isso com uma API mais previsível.
Instalando e carregando pacotes
install.packages("tidyverse") # instala (uma vez)
library(tidyverse) # carrega (toda sessão)
# Usar função sem carregar o pacote inteiro
dplyr::filter(df, idade > 30)
# Verificar se está instalado
requireNamespace("xgboost", quietly = TRUE)
O <- é a atribuição idiomática; o = fica para argumentos de função. Não existem escalares em R: tudo é vetor, e a indexação começa em 1. O índice negativo não conta do fim como em Python — ele remove elementos, e essa é a primeira armadilha para quem chega. Vetores são homogêneos, enquanto listas e data.frames guardam tipos mistos; os fatores representam categorias, e convertê-los para número sem pensar é fonte clássica de erro silencioso. O NA se propaga por onde passa, daí na.rm = TRUE e is.na(). Prefira vetorização e a família apply ou o purrr::map a laços explícitos. E guarde a distinção que mais confunde: [[ ]] extrai o elemento, [ ] fatia mantendo a estrutura.
Exercícios
-
Qual a diferença entre
v[-1]em R ev[-1]em Python?✓ Resposta:Em Python,
v[-1]retorna o último elemento (indexação negativa a partir do fim). Em R,v[-1]remove o primeiro elemento e retorna todos os outros. Para obter o último elemento em R, usetail(v, 1)ouv[length(v)]. Essa é uma das confusões mais comuns na migração entre as linguagens. -
O que o código abaixo imprime e por quê? > ``
r > c(1, 2, 3, 4, 5, 6) + c(10, 20, 30) >``✓ Resposta:Imprime
11 22 33 14 25 36. O vetor menorc(10, 20, 30)é reciclado (recycling): como seu comprimento (3) é múltiplo do comprimento do maior (6), R o repete duas vezes —c(10, 20, 30, 10, 20, 30)— e soma elemento a elemento. Se os comprimentos não fossem múltiplos, R ainda faria a operação, mas emitiria um aviso. -
Por que
as.numeric(fator)pode dar um resultado errado quando o fator representa números, e como corrigir?✓ Resposta:Um fator armazena internamente códigos inteiros (1, 2, 3...) que apontam para os níveis (
levels), ordenados alfabeticamente por padrão.as.numeric(fator)retorna esses códigos, não os valores originais. Por exemplo,factor(c("10","20","5"))tem níveis"10","20","5"→ códigos1,2,3≠ valores. A correção éas.numeric(as.character(fator)), que primeiro recupera o texto original e depois o converte para número. -
Escreva uma função
resumo_vetor(x)que retorne uma lista com média, desvio-padrão e número de valores faltantes dex, ignorandoNAnos cálculos.✓ Resposta:resumo_vetor <- function(x) { list( media = mean(x, na.rm = TRUE), desvio = sd(x, na.rm = TRUE), n_na = sum(is.na(x)) ) } resumo_vetor(c(1, 2, NA, 4, 5)) # $media 3 $desvio 1.826 $n_na 1O
na.rm = TRUEfaz média e desvio ignorarem faltantes;sum(is.na(x))conta quantosNAexistem. Retornar uma lista permite valores de tipos/estruturas diferentes. -
Explique a diferença entre
df[1, ],df[, 1]edf[1]para umdata.frame.✓ Resposta:df[1, ]seleciona a primeira linha (todas as colunas), retornando um data.frame de uma linha.df[, 1]seleciona a primeira coluna (todas as linhas) e, por padrão, retorna um vetor (comdrop = TRUE).df[1]usa a sintaxe de lista: seleciona o primeiro elemento da lista de colunas, retornando um data.frame de uma coluna (mantém a estrutura). Ou seja, a vírgula distingue seleção linha/coluna; sem vírgula, o data.frame se comporta como lista de colunas.