Introdução

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 um padrão conhecido como curva de Preston: países com renda muito baixa tendem a apresentar grandes ganhos de expectativa de vida à medida que o PIB cresce, enquanto em níveis de renda mais altos os ganhos marginais diminuem. Isso sugere uma relação crescente e fortemente não linear, favorecendo modelos que consigam capturar curvaturas e mudanças de inclinação ao longo do domínio de \(X\).

Nosso objetivo é ajustar um modelo de regressão não linear por meio de expansões de Fourier, uma base de funções senoidais e cossenoidais que oferece flexibilidade para aproximar formas complexas sem impor, a priori, uma estrutura polinomial específica. Em vez de escolher manualmente transformações de \(X\), deixamos que a combinação de harmônicos descreva a forma da curva, controlando a complexidade via número de termos \(m\) e escolhendo esse hiperparâmetro por validação cruzada (com AIC/BIC como apoio).

Perguntas que orientam a análise

Contribuições do relatório

  1. Pré-processamento com normalização de \(X\) para \((0,1)\), o que torna a base de Fourier mais estável.
  2. Ajuste de uma família de modelos com diferentes números de harmônicos \(m\).
  3. Seleção de modelo orientada a desempenho preditivo (CV) e parcimônia (AIC/BIC).
  4. Visualizações interpretáveis em escala normalizada e na escala original do PIB.
  5. Diagnóstico de resíduos e influência (Cook), discutindo limitações e possíveis extensões.

Limitações e escopo

A organização do restante do trabalho segue: (i) descrição dos dados e normalização, (ii) construção da base de Fourier e seleção de \(m\), (iii) ajuste final e avaliação de incerteza, e (iv) diagnóstico e conclusões.


Dados e Pré-processamento

Utilizamos a base World Development Indicators (WDI), que consolida estatísticas socioeconômicas comparáveis entre países. Para este estudo selecionamos duas variáveis:

  • PIB per capita (USD) — proxy de renda média (variável explicativa \(X\));
  • Expectativa de vida (anos) — indicador de saúde e bem-estar (variável resposta \(Y\)).

A amostra é transversal (um registro por país). Não inferimos causalidade; buscamos caracterizar a associação entre renda e longevidade. Antes de ajustar modelos, realizamos limpeza mínima: remoção de ausências (NA), checagem de tipos numéricos e padronização de nomes de colunas. Lembramos que fatores omitidos (educação, saneamento, proteção social) podem influenciar \(Y\); portanto, os resultados devem ser lidos como descritivos.

Caminho fixo informado pelo usuário

Para garantir reprodutibilidade no seu ambiente, o relatório aponta explicitamente para o arquivo:

D:/Script_R/Aprendizado_Maquina_TrabGrupo/worldDevelopmentIndicators.csv

Esse caminho fixo evita erros de “arquivo não encontrado” quando o working directory muda. Se o projeto for compartilhado, recomenda-se manter o .csv na mesma pasta do .Rmd e usar caminho relativo.

# 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)

# Garantir colunas esperadas
stopifnot(all(c("GDPercapita", "LifeExpectancy") %in% names(wdi)))

# Normalização x ∈ (0,1)
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

# Cheque rápido
cat("OK: wdi criado com", nrow(wdi), "linhas.\\n")
## OK: wdi criado com 211 linhas.\n
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

Normalização

Como a regressão será feita com base de Fourier, normalizamos o preditor para o intervalo \((0,1)\):

\[ x = \frac{X - x_{\min}}{x_{\max} - x_{\min}} \quad \text{com } x \in (0,1). \]

Essa transformação coloca \(X\) numa escala padrão, preserva a ordem entre observações e facilita comparar a escolha de \(m\) entre bases. Em todos os gráficos, mostramos a curva tanto em \(x\) normalizado quanto na escala original do PIB.


Metodologia

O modelo ajustado tem a forma:

\[ \hat{y}(x) = \beta_0 + \sum_{k=1}^{m} \big[a_k \sin(2\pi k x) + b_k \cos(2\pi k x)\big], \]

onde \(m\) representa o número de harmônicos. A escolha de \(m\) é feita por validação cruzada (CV) — para priorizar desempenho preditivo — e com apoio de critérios de informação (AIC/BIC) — para parcimônia.


Construção da Base de Fourier

fourier_basis <- function(x, m) {
  n <- length(x)
  if (m < 1) return(data.frame())
  out <- as.data.frame(matrix(NA_real_, nrow = n, ncol = 2*m))
  cn <- character(2*m)
  for (k in 1:m) {
    out[[2*k-1]] <- sin(2*pi*k*x)
    out[[2*k]]   <- cos(2*pi*k*x)
    cn[2*k-1] <- paste0("sin",k)
    cn[2*k]   <- paste0("cos",k)
  }
  names(out) <- cn
  out
}

fit_fourier <- function(df, m) {
  X <- fourier_basis(df$x, m)
  dat <- data.frame(y = df$y, X)
  lm(y ~ ., data = dat)
}

Seleção do Modelo

cv_fourier <- function(df, m, K=5, seed=123){
  set.seed(seed)
  n <- nrow(df)
  folds <- sample(rep(1:K, length.out=n))
  rmse <- numeric(K)
  for (k in 1:K) {
    train <- df[folds != k,]
    test  <- df[folds == k,]
    model <- fit_fourier(train,m)
    preds <- predict(model, newdata=fourier_basis(test$x,m))
    rmse[k] <- sqrt(mean((test$y - preds)^2))
  }
  mean(rmse)
}

Mmax <- 10
results <- data.frame(m=integer(), cv_rmse=numeric(), AIC=numeric(), BIC=numeric())
for (m in 1:Mmax) {
  model_m <- fit_fourier(wdi,m)
  cv_m <- cv_fourier(wdi,m)
  results <- rbind(results, data.frame(m=m, cv_rmse=cv_m, AIC=AIC(model_m), BIC=BIC(model_m)))
}
results
best_m <- results$m[which.min(results$cv_rmse)]
best_m
## [1] 3

Ajuste Final

final_model <- fit_fourier(wdi, best_m)
summary(final_model)
## 
## Call:
## lm(formula = y ~ ., data = dat)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -24.0410  -2.5184   0.7698   3.8918  21.6134 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  75.8326     1.0534  71.990  < 2e-16 ***
## sin1         -0.2121     1.6467  -0.129    0.898    
## cos1         -7.3859     1.4760  -5.004 1.21e-06 ***
## sin2          1.9198     1.7157   1.119    0.264    
## cos2         -4.9157     1.2059  -4.076 6.55e-05 ***
## sin3          4.3068     0.9770   4.408 1.68e-05 ***
## cos3         -0.9528     1.3201  -0.722    0.471    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6.446 on 204 degrees of freedom
## Multiple R-squared:  0.508,  Adjusted R-squared:  0.4935 
## F-statistic:  35.1 on 6 and 204 DF,  p-value: < 2.2e-16

Predições e Intervalos de Confiança

grid <- data.frame(x=seq(0,1,length.out=400))
Xgrid <- fourier_basis(grid$x, best_m)
pred <- predict(final_model, newdata=Xgrid, se.fit=TRUE)

grid$yhat <- as.numeric(pred$fit)
grid$lo <- as.numeric(pred$fit - 1.96*pred$se.fit)
grid$hi <- as.numeric(pred$fit + 1.96*pred$se.fit)
grid$gdp <- grid$x*(xmax-xmin)+xmin

Resultados Gráficos

# Normalizado
plot(wdi$x, wdi$y, pch=16, xlab="Normalized GDP per capita", ylab="Life Expectancy",
     main=paste("Fourier Fit (m =", best_m,")"))
lines(grid$x, grid$yhat, lwd=2)
lines(grid$x, grid$lo, lty=2)
lines(grid$x, grid$hi, lty=2)

# Escala original
plot(wdi$GDPercapita, wdi$y, pch=16, xlab="GDP per capita", ylab="Life Expectancy",
     main=paste("Fourier Fit vs GDP (m =", best_m,")"))
lines(grid$gdp, grid$yhat, lwd=2)
lines(grid$gdp, grid$lo, lty=2)
lines(grid$gdp, grid$hi, lty=2)


Diagnósticos

par(mfrow=c(1,2))
plot(final_model, which=1) # resíduos vs ajustados
plot(final_model, which=2) # QQ-plot

par(mfrow=c(1,1))
plot(cooks.distance(final_model), type="h", ylab="Cook's distance",
     main="Influence (Cook's distance)")
abline(h=4/nrow(wdi), lty=2)