Este relatório investiga a relação entre PIB per capita e expectativa de vida utilizando dados do World Development Indicators (WDI). A literatura descreve a curva de Preston: países de baixa renda apresentam grandes ganhos de expectativa de vida quando a renda cresce, enquanto, em níveis mais altos de renda, os ganhos marginais diminuem. Essa forma sugere uma relação não linear entre renda (\(X\)) e vida (\(Y\)).
A estratégia é ajustar uma regressão usando expansões de Fourier, permitindo capturar curvaturas complexas sem especificar, de antemão, um polinômio. A complexidade é controlada via número de harmônicos \(p\), escolhido por validação cruzada leave-one-out (LOO-CV), e com apoio de AIC/BIC quando útil.
Utilizamos a base WDI com duas variáveis principais: PIB per capita (USD) — preditora \(X\) — e expectativa de vida (anos) — resposta \(Y\). A amostra é transversal (um ponto por país). O objetivo é descritivo, não causal.
Caminho do CSV:
D:/Script_R/Aprendizado_Maquina_TrabGrupo/worldDevelopmentIndicators.csv
# Caminho fixo para o CSV
csv_path <- "D:/Script_R/Aprendizado_Maquina_TrabGrupo/worldDevelopmentIndicators.csv"
if (!file.exists(csv_path)) stop("CSV não encontrado em: ", csv_path)
# Ler dados
wdi <- read.csv(csv_path, stringsAsFactors = FALSE)
# Conferir colunas esperadas
stopifnot(all(c("GDPercapita", "LifeExpectancy") %in% names(wdi)))
# Remover NAs, se existirem
wdi <- subset(wdi, is.finite(GDPercapita) & is.finite(LifeExpectancy))
# Normalização preliminar (será usada no Q1 e seguintes)
xmin <- min(wdi$GDPercapita, na.rm = TRUE)
xmax <- max(wdi$GDPercapita, na.rm = TRUE)
wdi$x <- (wdi$GDPercapita - xmin) / (xmax - xmin)
wdi$y <- wdi$LifeExpectancy
cat("OK: wdi com", nrow(wdi), "linhas | x em [", round(min(wdi$x),3), ",", round(max(wdi$x),3), "]\n")
## OK: wdi com 211 linhas | x em [ 0 , 1 ]
summary(wdi[,c("GDPercapita","LifeExpectancy")])
## GDPercapita LifeExpectancy
## Min. : 251 Min. :45.33
## 1st Qu.: 1682 1st Qu.:64.06
## Median : 5786 Median :72.49
## Mean : 14150 Mean :70.30
## 3rd Qu.: 16863 3rd Qu.:76.75
## Max. :103858 Max. :83.48
cor(wdi$x, wdi$y)
## [1] 0.5965097
Pergunta 1. Normalize a covariável de modo que \(x \in (0,1)\). Para isso, faça \(x = \frac{x - x_{\min}}{x_{\max}-x_{\min}}\), onde \(x_{\min}\) e \(x_{\max}\) são os valores mínimos e máximos segundo a amostra usada.
Resposta 1. Aplicamos a normalização acima com base
em GDPercapita
. Isso estabiliza a base de Fourier, preserva
a ordem entre observações e facilita a comparação de modelos.
xmin <- min(wdi$GDPercapita, na.rm = TRUE)
xmax <- max(wdi$GDPercapita, na.rm = TRUE)
wdi$x <- (wdi$GDPercapita - xmin) / (xmax - xmin)
range(wdi$x, na.rm = TRUE)
## [1] 0 1
Pergunta 2. Usando mínimos quadrados e validação cruzada leave-one-out, estime o erro quadrático médio das regressões
\(g(x) = \beta_0 + \beta_1 \sin(2\pi x) + \beta_2 \cos(2\pi x) + \cdots + \beta_{2p-1} \sin(2\pi p x) + \beta_{2p} \cos(2\pi p x)\), para \(p=1,\dots,30\).
Resposta 2. Para cada \(p\), ajustamos um modelo
linear com a base de Fourier e calculamos o MSE-LOO
usando a fórmula exata (PRESS):
\(\widehat{y}i^{(-i)} = y_i - e_i/(1-h{ii})\) e
\(\text{MSE-LOO} = \frac{1}{n}\sum_i (e_i/(1-h_{ii}))^2\).
# Base de Fourier com p harmônicos
fourier_basis <- function(x, p) {
if (p < 1) return(data.frame())
n <- length(x)
out <- as.data.frame(matrix(NA_real_, nrow = n, ncol = 2*p))
nm <- character(2*p)
for (k in 1:p) {
out[[2*k-1]] <- sin(2*pi*k*x)
out[[2*k]] <- cos(2*pi*k*x)
nm[2*k-1] <- paste0("sin",k)
nm[2*k] <- paste0("cos",k)
}
names(out) <- nm
out
}
# Ajuste LM para dado p
fit_fourier <- function(df, p) {
X <- fourier_basis(df$x, p)
dat <- data.frame(y = df$y, X)
lm(y ~ ., data = dat)
}
# LOO para LM via PRESS
loo_from_lm <- function(model, y) {
h <- lm.influence(model, do.coef = FALSE)$hat
e <- resid(model)
eps <- .Machine$double.eps^0.5
denom <- pmax(1 - h, eps)
loo_resid <- e / denom
yhat_loo <- y - loo_resid
mse <- mean(loo_resid^2)
rmse <- sqrt(mse)
list(yhat_loo = yhat_loo, mse = mse, rmse = rmse, h = h)
}
p_max <- 30L
risk <- data.frame(p = integer(), mse_loo = numeric(), rmse_loo = numeric())
for (p in 1:p_max) {
m <- fit_fourier(wdi, p)
loo <- loo_from_lm(m, wdi$y)
risk <- rbind(risk, data.frame(p = p, mse_loo = loo$mse, rmse_loo = loo$rmse))
}
head(risk)
Pergunta 3. Plote o gráfico do risco estimado (MSE-LOO) vs \(p\). Qual o valor de \(p\) escolhido? Denotaremos por \(p_{esc}\).
Resposta 3. O \(p_{esc}\) é o \(p\) que minimiza o MSE-LOO. Destacamos essa escolha no gráfico com uma linha tracejada.
pesc <- risk$p[which.min(risk$mse_loo)]
cat("p_esc (mínimo MSE-LOO) =", pesc, "\n")
## p_esc (mínimo MSE-LOO) = 3
plot(risk$p, risk$mse_loo, type="b", pch=19, xlab="p (nº de harmônicos)",
ylab="MSE - LOO", main="Risco preditivo (LOO) por p")
abline(v = pesc, lty = 2, col = "red")
legend("topright", legend = paste("p_esc =", pesc), lty=2, col="red", bty="n")
Pergunta 4. Plote as curvas ajustadas para \(p = 1\), \(p = p_{esc}\) e \(p = 30\) sobre o gráfico de dispersão de \(X\) por \(Y\), usando um grid em \([0,1]\). Qual curva parece mais razoável? Compare com polinômios vistos em aula.
Resposta 4. Comparamos as três curvas. Tipicamente, \(p=1\) é muito rígido; \(p=30\) pode ondular (overfitting); \(p=p_{esc}\) tende a equilibrar viés e variância, oferecendo melhor generalização — análogo ao observado com polinômios de grau intermediário.
pset <- unique(c(1L, pesc, 30L))
cols <- c("steelblue", "tomato", "darkgreen")
grid <- data.frame(x = seq(0, 1, length.out = 400))
plot(wdi$x, wdi$y, pch=16, col=gray(0.2), xlab="x normalizado (0-1)",
ylab="Expectativa de vida", main="Curvas de Fourier sobre dispersão X vs Y")
for (j in seq_along(pset)) {
p <- pset[j]
m <- fit_fourier(wdi, p)
Xg <- fourier_basis(grid$x, p)
yg <- predict(m, newdata = Xg)
lines(grid$x, yg, lwd = 2, col = cols[j])
}
legend("bottomright", legend = paste0("p = ", pset), col = cols, lwd = 2, bty = "n")
Pergunta 5. Plote valores preditos (LOO) versus observados para \(p = 1\), \(p = p_{esc}\) e \(p = 30\). Qual \(p\) parece mais razoável?
Resposta 5. O melhor \(p\) é o que mantém os pontos mais próximos da reta 45° no gráfico Predito LOO vs Observado; valores de \(p\) muito altos tendem a degradar predição fora da amostra.
op <- par(mfrow=c(1, length(pset)), mar=c(4,4,2,1))
for (p in pset) {
m <- fit_fourier(wdi, p)
loo <- loo_from_lm(m, wdi$y)
plot(wdi$y, loo$yhat_loo, pch=16, col=gray(0.2),
xlab="Observado (Y)", ylab="Predito LOO",
main=paste("p =", p))
abline(0,1,col="red",lty=2)
}
par(op)
Pergunta 6. Quais vantagens e desvantagens de se usar validação cruzada do tipo leave-one-out versus data-splitting?
Resposta 6.
- LOO-CV (leave-one-out) — Vantagens: usa
praticamente toda a amostra para treinar; não
desperdiça dados; para modelos lineares, há fórmula fechada
(PRESS), sendo eficiente. Desvantagens: pode ter maior
variância que k-folds quando o modelo é instável; sem
fórmula, é caro computacionalmente.
- Data-splitting (treino/teste) — Vantagens:
simples de implementar; avaliação em dados realmente
“não vistos”. Desvantagens: resultados dependem da
partição; desperdício de amostra (menos dados
para treinar); alta variância; pode levar a escolhas subótimas de
\(p\).
Conclusão: com amostras moderadas e modelos lineares, LOO-CV é preferível; com bases enormes/modelos pesados, usar k-fold (ex.: 5 ou 10) é um meio-termo eficiente.