A regressão linear é um dos métodos mais utilizados em modelagem estatística. Ela parte do pressuposto de que existe uma relação linear entre as variáveis explicativas e a variável resposta, o que implica que a relação entre elas pode ser explicada por uma reta. Em termos práticos, espera-se uma relação aditiva e constante entre as variáveis.
No entanto, essa premissa frequentemente é quebrada, principalmente em situações onde os dados apresentam comportamentos não lineares. Um exemplo claro disso pode ser observado ao estudar a relação entre as variáveis medv (valor mediano das propriedades) e indus (proporção de acres destinados a negócios não varejistas) no famoso Boston Housing Dataset. Sabe-se de antemão que essa relação não é linear, o que pode levar a falhas significativas na modelagem.
Quando a linearidade dos dados não é alcançada, ocorrem problemas na distribuição dos resíduos, o que indica que o modelo não está capturando a verdadeira relação entre as variáveis. Isso pode gerar previsões enviesadas e imprecisas, onde o modelo tende a subestimar ou superestimar a variável resposta em diferentes regiões dos dados. Além disso, a falha em capturar relações não lineares pode resultar em uma modelagem inadequada para diferentes subgrupos dos dados.
Neste projeto, o objetivo é identificar essa falta
de linearidade entre as variáveis medv e
indus, demonstrando como é possível
detectar esse problema e aplicar soluções. Entre as
estratégias, podemos usar técnicas como transformações de
variáveis, modelos polinomiais e
splines para ajustar a relação de forma mais precisa e
minimizar o erro nas previsões.
“Não existe modelo de regressão perfeito, existe o péssimo, o ruim e o menos pior.”
Essa frase nos lembra que, em análise de dados, a busca por um modelo ideal pode ser ilusória. O objetivo é encontrar uma solução que ofereça o melhor ajuste possível, minimizando os erros e as distorções.
O primeiro passo na análise é verificar algumas informações
fundamentais sobre as variáveis envolvidas no modelo. Começamos
investigando as correlações (Pearson e Spearman) entre
as variáveis indus e medv, com o objetivo de
identificar se há uma relação linear ou não linear entre elas. A
correlação de Pearson foi de 0.48 e a
correlação de Spearman foi de 0.59.
Esses valores indicam que existe uma correlação
moderada entre as variáveis, sugerindo que, embora a
relação não seja perfeitamente linear, há espaço para
ajustes que poderiam melhorar a linearidade.
medvAlém das correlações, também investigamos a distribuição da
variável resposta medv. Embora a distribuição não
impacte diretamente a linearidade da relação entre indus e
medv, pode indicar problemas como
heterocedasticidade (variação não constante dos
resíduos). Nesse caso, observamos uma alta assimetria à
direita na distribuição de medv.
Essa assimetria não afeta necessariamente a modelagem linear diretamente, mas pode sugerir que o erro do modelo não será distribuído uniformemente ao longo dos valores preditos, o que pode levar a previsões enviesadas ou inconsistências nos resíduos. Isso é algo que devemos ter em mente ao avançar com a modelagem e buscar soluções para melhorar o ajuste.
#Casas mais caras estão sitiadas em regiões de alto comportamento industrial?
#Primeiro vamos entender a correlação e a distribuição de Y
y = Boston$medv #variável dependente Medv - preço médio das casas
x = Boston$indus #variável independente Indus - área industrial da cidade
#Correlações
pearson_corr <- round(cor(y, x, method = 'pearson'), 2)
spearman_corr <- round(cor(y, x, method = 'spearman'), 2)
cat("Correlação Pearson entre X e Y:", pearson_corr, "\n")
## Correlação Pearson entre X e Y: -0.48
cat("Correlação Spearman entre X e Y:", spearman_corr, "\n")
## Correlação Spearman entre X e Y: -0.58
#Dimensão de Y
cat("Comprimento de y:", length(y), "\n")
## Comprimento de y: 506
# Visualizando a distribuição de y
library(e1071)
hist(y, breaks = 20, freq = FALSE, main = "Histograma da Distribuição de Y", xlab = "Y")
lines(density(y), col = "blue", lwd = 2)
# Teste de normalidade e estatísticas de assimetria e curtose
shapiro_result <- shapiro.test(y)
skewness_value <- skewness(y)
kurtosis_value <- kurtosis(y)
# Exibindo os resultados
cat("Resultado do Teste de Shapiro-Wilk:", shapiro_result$p.value, "\n")
## Resultado do Teste de Shapiro-Wilk: 4.941386e-16
cat("Assimetria (Skewness):", skewness_value, "\n")
## Assimetria (Skewness): 1.101537
cat("Curtose (Kurtosis):", kurtosis_value, "\n")
## Curtose (Kurtosis): 1.450984
# Scatter plot com formatação
plot(Boston$indus, Boston$medv, pch = 19, col = "blue",
xlab = 'Indus', ylab = 'MedV',
main = 'Scatter Plot da relação entre \nMedian Property Value e Non-Retail Business Acres por Town')
Ao avaliar o gráfico de resíduos versus valores
ajustados (Residuals vs Fitted), podemos observar que a forma
dos resíduos parece se aproximar de uma curva em U.
Esse padrão indica que a relação entre as variáveis medv e
indus não pode ser adequadamente explicada por uma
reta linear, reforçando a ideia de que há uma não
linearidade significativa na interação entre essas
variáveis.
Além disso, identificamos a presença de outliers e
uma distribuição assimétrica à direita nos resíduos,
sugerindo valores elevados de assimetria (skewness) e
curtose (kurtosis). Essa observação é consistente com a
análise preliminar da distribuição da variável medv. Outro
ponto importante é o gráfico Scale-Location, que
demonstra um padrão nos resíduos. Isso indica que pode haver problemas
relacionados à heterocedasticidade, o que reforça a
ideia de que estamos interpretando incorretamente a relação entre
medv e indus.
Embora o estudo tenha identificado esses problemas de linearidade, é importante notar que nossa análise está focada especificamente no primeiro aspecto — a falta de linearidade entre as variáveis. Questões como a presença de outliers e o comportamento dos resíduos em relação à homocedasticidade são importantes, mas estão fora do escopo desta investigação.
Existem várias maneiras de identificar a falta de linearidade em um modelo de regressão. Uma das minhas formas favoritas é através do gráfico de resíduos parciais, especialmente quando trabalhamos com modelos de regressão linear múltipla.
A fórmula para o resíduo parcial é dada por:
\[ \text{Resíduo Parcial} = \text{Resíduo} + \hat{\beta}_i X_i \]
Esse gráfico permite isolar o relacionamento entre
uma variável independente (X_i) e a variável dependente
(Y), levando em consideração todas as outras
variáveis do modelo. No nosso caso, como estamos lidando apenas
com uma variável explicativa (indus), ele nos ajuda a
entender a contribuição individual dessa variável para o modelo.
Em termos práticos, um resíduo parcial pode ser
interpretado como o valor resultante da combinação da previsão
de uma variável isolada (X_i) com o
resíduo da equação completa. Esse gráfico é
particularmente útil para verificar se a relação entre uma variável
específica e a resposta é linear, enquanto os efeitos das demais
variáveis são controlados.
Outra abordagem útil é o uso do gráfico
crPlot(modelo, variável = '') do pacote
car no R. Esse gráfico visualiza a relação
entre a variável de interesse e o resíduo ajustado, permitindo uma
inspeção mais detalhada da linearidade.
Com base na nossa análise, comprovamos que não existe uma relação
linear adequada entre medv e indus. Portanto,
não podemos utilizar uma regressão linear simples para explicar essa
relação de forma precisa.
Diante desse cenário, existem várias alternativas. Uma das abordagens
mais comuns seria aplicar transformações matemáticas em
X ou Y. No entanto, essas transformações
raramente resolvem completamente o problema da não linearidade. Outra
abordagem seria o uso de regressões polinomiais.
Embora uma regressão polinomial possa capturar a não linearidade, ela apresenta um problema significativo: esses modelos são construídos de forma global, ou seja, aplicam a mesma função polinomial a todos os dados. Isso pode ser problemático se houver diferentes regiões nos dados onde a variação da relação muda. Nesse caso, o polinômio pode ainda falhar em explicar a complexidade dos dados.
Para lidar com essa limitação, podemos recorrer a uma abordagem que permite segmentar o modelo em regiões distintas ou nós (knots). Essa técnica é conhecida como Splines, e uma das mais comuns é a Piecewise Splines.
A ideia por trás das Piecewise Splines é aplicar diferentes regressões (lineares, quadráticas ou cúbicas) em cada região dos dados, separadas por nós. A fórmula para um Piecewise Cubic Spline é dada por:
\[ y_i = \begin{cases} \beta_{01} + \beta_{11} x_i + \beta_{21} x_i^2 + \beta_{31} x_i^3 + \epsilon_i & \text{se} \ x_i < c \\ \beta_{02} + \beta_{12} x_i + \beta_{22} x_i^2 + \beta_{32} x_i^3 + \epsilon_i & \text{se} \ x_i \geq c \end{cases} \]
Onde c define o nó (ou a região de
quebra no plano cartesiano).
Por convenção, utilizamos o Piecewise Cubic porque ele geralmente oferece melhor desempenho em relação ao polinômio quadrático, capturando de forma mais suave as transições entre as regiões.
Em termos práticos, aplicamos duas funções polinomiais
diferentes aos dados. Uma função é usada para o subconjunto
onde x_i < c e a outra para x_i ≥ c. Cada
uma dessas funções é ajustada separadamente usando o método de
mínimos quadrados, o que permite maior flexibilidade
para capturar as variações em diferentes partes dos dados.
Para evitar que o modelo apresente descontinuidades nas transições entre as regiões, podemos impor restrições que garantam a suavidade nos nós. Isso significa que as funções polinomiais nas diferentes regiões devem se encontrar nos pontos de quebra. Podemos impor até três restrições nas Piecewise Splines:
Essas restrições fazem com que o modelo seja mais suave e contínuo, evitando quebras abruptas entre as regiões.
Além das Piecewise Splines, temos outras variações como as Natural Splines e Smoothing Splines.
As Natural Splines são Piecewise Splines cúbicas, mas com restrições nas pontas (extremos). Isso é importante porque, nas Piecewise Splines, geralmente observamos variações muito grandes nas extremidades, o que pode gerar intervalos de confiança extensos e instabilidade nos modelos. As Natural Splines adicionam restrições para que a função spline seja quase linear nos extremos, melhorando a estabilidade.
As Smoothing Splines são mais complexas e poderosas, mas não as abordaremos em detalhes aqui. Elas funcionam criando quebras nos dados por meio de uma penalização na função de custo, o que pode dificultar a interpretação dos intervalos de confiança e dos erros padrão. Embora sejam eficientes, prefiro evitá-las em favor de métodos mais simples e fáceis de interpretar, como as Piecewise Splines.
sort.x = sort(x)
cuts <- summary(x)[c(2,3,5)] #Em primeiro momento vamos usar a famosa convenção teórica de definir os nós nos quartis 25, 50 e 75 da distribuição de X
#Para construir Piecewise Splines usamos o bs() para alterar a forma de X -> uma funçao em X
#Knots definimos as quebras e degree os graus polinomiais a serem usados
spline.bs = lm(y ~ bs(x, knots = cuts, degree = 3))
pred.bs = predict(spline.bs, newdata = list(x = sort.x), se = TRUE)
se.bands.bs = cbind(pred.bs$fit + 2 * pred.bs$se.fit,
pred.bs$fit - 2 * pred.bs$se.fit)
y.lab = 'Median Property Value'
x.lab = 'Non-retail business acres per town'
plot(x, y, cex.lab = 1.1, col="darkgrey", xlab = x.lab, ylab = y.lab,
main = "Cubic Spline", bty = 'l')
lines(sort.x, pred.bs$fit, lwd = 2, col = "red")
matlines(sort.x, se.bands.bs, lwd = 2, col = "red", lty = 3)
#Veja como a modelagem (o encaixe do modelo) parece um pouco melhor (visualmente)
Você deve estar se perguntando: Onde devemos posicionar os nós (knots) e quantos devemos usar, certo?
A escolha dos nós é fundamental para capturar as variações na relação
entre X e Y. Devemos posicioná-los onde
acreditamos que a variação entre as variáveis começa a mudar
significativamente, ou seja, em pontos onde há uma curva
acentuada ou uma expansão na relação. Esses
pontos representam mudanças estruturais no comportamento dos dados e,
portanto, são locais ideais para aplicar os nós.
Embora possamos definir manualmente onde posicionar os nós, existe uma abordagem mais moderna e sofisticada que permite ao algoritmo ajustar automaticamente a posição dos nós com base na complexidade do modelo que desejamos.
Em vez de escolher as posições exatas dos nós, podemos definir a
quantidade de graus de liberdade (degrees of freedom,
ou df) que queremos que o modelo utilize. A quantidade de
nós será então determinada automaticamente pela fórmula:
\[ \text{Knots} = df - \text{graus} + 1 \]
O modelo aplicará os nós em pontos de mudança da variação, de acordo com o número de graus de liberdade especificado. Isso permite que o modelo se ajuste de maneira mais dinâmica às mudanças nos dados.
Quanto maior o número de graus de liberdade utilizado, mais flexível será o modelo. No entanto, essa flexibilidade adicional também pode aumentar o risco de overfitting, onde o modelo se ajusta demais aos dados de treino, perdendo capacidade de generalização.
Uma maneira de encontrar o número ideal de graus de liberdade,
especialmente em cenários de previsão, é avaliar o trade-off
entre viés e variância. Podemos testar diferentes valores de
df e observar como o modelo se comporta em termos de
erro de previsão e estabilidade.
Essa abordagem de definir graus de liberdade em vez de nós pode ser
aplicada tanto em Piecewise Splines quanto em
Natural Splines. Em ambos os casos, a definição dos
df permite que o modelo ajuste automaticamente a posição
dos nós de forma otimizada, capturando as variações nos dados sem a
necessidade de intervenções manuais.
# Definir uma semente para reprodutibilidade
set.seed(123)
# Definir o número de folds para cross-validation
k <- 20
# Definir uma faixa de graus de liberdade a serem testados
df_values <- seq(1, 24, by = 2) # Graus de liberdade de 3 a 30
mean_squared_errors_test <- numeric(length(df_values)) # Para armazenar os erros de teste
mean_squared_errors_train <- numeric(length(df_values)) # Para armazenar os erros de treinamento
# Realizar k-fold cross-validation para cada número de graus de liberdade
for (i in seq_along(df_values)) {
df <- df_values[i]
# Criar uma estrutura para os folds
folds <- createFolds(Boston$medv, k = k, list = TRUE)
fold_errors_test <- numeric(k) # Armazenar os erros de cada fold para teste
fold_errors_train <- numeric(k) # Armazenar os erros de cada fold para treinamento
# Loop sobre cada fold
for (j in 1:k) {
# Criar conjunto de treino e teste
train_set <- Boston[-folds[[j]], ]
test_set <- Boston[folds[[j]], ]
# Ajustar o modelo spline com o número atual de graus de liberdade
modelo <- lm(medv ~ bs(indus, df = df), data = train_set)
# Fazer previsões no conjunto de teste
predictions_test <- predict(modelo, newdata = test_set)
predictions_train <- predict(modelo, newdata = train_set)
# Calcular o erro quadrático médio (MSE) para este fold
fold_errors_test[j] <- mean((test_set$medv - predictions_test)^2)
fold_errors_train[j] <- mean((train_set$medv - predictions_train)^2)
}
# Calcular o erro médio para este número de graus de liberdade
mean_squared_errors_test[i] <- mean(fold_errors_test)
mean_squared_errors_train[i] <- mean(fold_errors_train)
}
# Criar um data frame para armazenar os resultados
results <- data.frame(
DF = df_values,
MSE_Test = mean_squared_errors_test,
MSE_Train = mean_squared_errors_train
)
print(results)
## DF MSE_Test MSE_Train
## 1 1 61.51387 61.02982
## 2 3 61.88965 61.03319
## 3 5 62.10340 60.70588
## 4 7 62.00885 59.61618
## 5 9 61.06686 58.88041
## 6 11 58.47850 54.63621
## 7 13 55.80849 53.15543
## 8 15 55.80756 52.72706
## 9 17 55.64548 51.97902
## 10 19 55.09152 51.80888
## 11 21 55.73629 52.04513
## 12 23 80.15632 50.78438
# Identificar os índices dos dois menores erros de teste
min_indices <- order(results$MSE_Test)[1:2]
# Criar o gráfico
ggplot(results) +
geom_line(aes(x = DF, y = MSE_Test, color = "Teste")) +
geom_line(aes(x = DF, y = MSE_Train, color = "Treinamento")) +
geom_point(aes(x = DF, y = MSE_Test, color = "Teste")) +
geom_point(aes(x = DF, y = MSE_Train, color = "Treinamento")) +
geom_point(data = results[min_indices, ], aes(x = DF, y = MSE_Test), color = "tomato3", size = 5) +
geom_text(data = results[min_indices, ], aes(x = DF, y = MSE_Test, label = paste("DF:", DF, "\nMSE:", round(MSE_Test, 2))), vjust = -1, color = "red", size=3) +
labs(title = "Erro Quadrático Médio por Graus de Liberdade",
x = "Graus de Liberdade",
y = "Erro Quadrático Médio (MSE)") +
scale_color_manual(name = "Conjunto", values = c("Teste" = "red", "Treinamento" = "blue")) +
theme_minimal()
Agora que entendemos a importância dos graus de
liberdade e como eles impactam a flexibilidade do modelo, vamos
construir o nosso modelo de Splines usando um número
“ideal” de df. Vamos também explorar os pontos onde o
modelo decidiu aplicar os nós.
Ao definir o modelo, podemos controlar diretamente os graus de
liberdade, e o algoritmo ajustará os nós automaticamente com base nos
pontos de maior variação em X.
Depois de construir o modelo, podemos verificar onde os
nós foram posicionados usando a função
attr(). Isso nos permite acessar os valores de
X onde as quebras ocorreram.
# Ajustar o modelo de spline
#Optei com DF = 17 (focamos em deixar o menos flexível e com menor erro)
spline.bs <- lm(y ~ bs(x, df=17, degree=3))
pred.bs <- predict(spline.bs, newdata = list(x = sort.x), se = TRUE)
se.bands.bs <- cbind(pred.bs$fit + 2 * pred.bs$se.fit,
pred.bs$fit - 2 * pred.bs$se.fit)
# Criar data frame para ggplot
data_plot <- data.frame(
x = sort.x,
y_fit = pred.bs$fit,
lower_ci = se.bands.bs[, 2],
upper_ci = se.bands.bs[, 1]
)
# Obter os nós do spline
knots <- attr(bs(x, df = 17, degree = 3), "knots")
# Calcular RMSE
rmse <- sqrt(mean((y - predict(spline.bs))^2))
# Criar o gráfico
ggplot(data_plot, aes(x = x)) +
geom_point(aes(y = y), color = "darkgrey") +
geom_line(aes(y = y_fit), color = "red", size = 1) +
geom_ribbon(aes(ymin = lower_ci, ymax = upper_ci), fill = "red", alpha = 0.2) +
geom_vline(xintercept = knots, linetype = "dashed", color = "blue", size = 0.8) +
labs(
title = "Cubic Spline with Confidence Interval",
subtitle = paste("Modelo de Spline cúbico ajustado com DF = 17 RMSE =", round(rmse, 2)),
x = x.lab,
y = y.lab
) + theme_minimal()
Após o ajuste com splines, conseguimos resolver o
problema de linearidade que observamos anteriormente. O
modelo agora reflete melhor a relação entre medv e
indus.
No entanto, ainda enfrentamos desafios relacionados à heterocedasticidade e à presença de outliers. Esses problemas indicam que:
Heterocedasticidade: A variabilidade dos resíduos pode sugerir que nosso modelo ainda não está capturando todas as nuances da relação entre as variáveis. É possível que existam preditores adicionais que explicam essa variação.
Outliers: A presença de valores extremos sugere
que os dados de medv podem estar mal interpretados, ou que
precisamos incluir mais variáveis explicativas no modelo.
Contudo, não vamos abordar esses aspectos neste
estudo, pois nosso foco principal foi na correção da linearidade entre
medv e indus.
par(mfrow = c(2,2))
plot(spline.bs)
#podemos confirmar, com os gráficos que nos resta um problemas de heterocedastividade
#provável que por conta de alguma variável ou relação que precisamos capturar
#para explicar a ponta desse modelo (onde a variância aumenta)
Vamos, por fim, apenas verificar a distribuição dos resíduos e podemos notar uma distribuição provável quasipoisson ou binomial negativa, talvez usar um glm() da familia dessas com o splines fosse o ideal no nosso cenário
# Calcular os resíduos do modelo
residuals <- residuals(spline.bs)
# Calcular média, skewness e kurtosis
mean_residuals <- mean(residuals)
skewness_residuals <- skewness(residuals)
kurtosis_residuals <- kurtosis(residuals)
# Criar data frame dos resíduos
residuals_df <- data.frame(residuals)
ggplot(residuals_df, aes(x = residuals)) +
geom_histogram(aes(y = ..density..), bins = 30, fill = "lightblue", color = "black", alpha = 0.7) +
geom_density(color = "red", size = 1) + # Curva de densidade
geom_vline(aes(xintercept = mean_residuals), linetype = "dashed", color = "blue", size = 1) +
labs(
title = "Histograma dos Resíduos do Modelo",
x = "Resíduos",
y = "Densidade"
) +
annotate("text", x = mean_residuals + 2.2, y = 0.08, label = paste("Média:", round(mean_residuals, 4)), color = "blue", size = 4, vjust = -1) +
annotate("text", x = mean_residuals + 20.8, y = 0.1, label = paste("Skewness:", round(skewness_residuals, 4)), color = "red", size = 4) +
annotate("text", x = mean_residuals + 20.25, y = 0.09, label = paste("Kurtosis:", round(kurtosis_residuals, 4)), color = "red", size = 4) +
theme_minimal(base_family = "sans") +
theme(
plot.title = element_text(size = 16, face = "bold"),
axis.title = element_text(size = 14),
axis.text = element_text(size = 12)
) +
theme_classic()
Por fim, vamos verificar os resultados desse modelo, vamos? Podemo notar bons resultados mesmo em uma modelo bem complicada
# Ajustar o modelo de spline
spline.bs <- lm(y ~ bs(x, df=17, degree=3))
# Previsões
pred.bs <- predict(spline.bs, newdata = list(x = sort.x), se = TRUE)
# Cálculo das métricas
mse <- mean((y - predict(spline.bs))^2)
rmse <- sqrt(mse)
rss <- sum((y - predict(spline.bs))^2)
mape <- mean(abs((y - predict(spline.bs)) / y)) * 100
r_squared_adj <- summary(spline.bs)$adj.r.squared
# Criar tabela de métricas
metrics_table <- tibble(
Metric = c("MSE", "RMSE", "RSS", "MAPE", "R² Ajustado"),
Value = round(c(mse, rmse, rss, mape, r_squared_adj), 3)
)
# Criar tabela para IC das previsões
se.bands.bs <- cbind(pred.bs$fit + 2 * pred.bs$se.fit,
pred.bs$fit - 2 * pred.bs$se.fit)
ic_table <- data.frame(
DF = sort.x,
Fit = pred.bs$fit,
Lower_CI = se.bands.bs[,2],
Upper_CI = se.bands.bs[,1]
)
# Visualizar a tabela de métricas
metrics_table %>%
kable(caption = "Tabela de Métricas do Modelo") %>%
kable_styling(full_width = F)
| Metric | Value |
|---|---|
| MSE | 52.097 |
| RMSE | 7.218 |
| RSS | 26361.226 |
| MAPE | 25.133 |
| R² Ajustado | 0.361 |
# Exibir apenas o cabeçalho da tabela de IC das previsões
head(ic_table) %>%
kable(caption = "Cabeçalho da Tabela de Intervalo de Confiança das Previsões") %>%
kable_styling(full_width = F)
| DF | Fit | Lower_CI | Upper_CI |
|---|---|---|---|
| 0.46 | 47.13581 | 34.14091 | 60.13072 |
| 0.74 | 37.75682 | 30.64060 | 44.87304 |
| 1.21 | 30.01824 | 25.60682 | 34.42967 |
| 1.22 | 29.94003 | 25.55568 | 34.32439 |
| 1.25 | 29.72324 | 25.42106 | 34.02542 |
| 1.25 | 29.72324 | 25.42106 | 34.02542 |
O Natural Splines segue a mesma lógica que os
Piecewise Splines, sendo uma alternativa viável para o
ajuste de modelos. Assim como os Piecewise Splines, os Natural Splines
também minimizam o Residual Sum of Squares (RSS), o que
permite que ambos sejam utilizados com a função lm() em R
para ajustes de modelos lineares.
Os Natural Splines introduzem restrições adicionais nas extremidades, garantindo que as funções spline se aproximem de uma linha reta fora dos nós. Isso ajuda a evitar flutuações excessivas nas bordas, melhorando a estabilidade do modelo, especialmente em casos onde os dados podem apresentar variabilidade elevada.
Os Smoothing Splines, por outro lado, aplicam uma abordagem diferente. Eles introduzem uma penalização na função de custo, permitindo que o modelo ajuste a suavidade da curva. Essa suavização pode ser poderosa, mas torna a interpretação dos intervalos de confiança, erros padrão e testes estatísticos mais complexos.
Ambos os métodos oferecem flexibilidade na modelagem de relações não lineares e são opções a serem consideradas quando se trabalha com dados que não se encaixam bem em um modelo de regressão linear simples.
# Natural cubic spline.
spline.ns = lm(y ~ ns(x, df=17))
pred.ns = predict(spline.ns, newdata = list(x = sort.x), se = TRUE)
se.bands.ns = cbind(pred.ns$fit + 2 * pred.ns$se.fit,
pred.ns$fit - 2 * pred.ns$se.fit)
# Smoothing spline
spline.smooth = smooth.spline(x, y, df = 17)
par(mfrow = c(1,3))
# Natural cubic spline.
plot(x, y, cex.lab = 1.1, col="darkgrey", xlab = x.lab, ylab = y.lab,
main = "Natural Cubic Spline", bty = 'l')
lines(sort.x, pred.ns$fit, lwd = 2, col = "orange")
matlines(sort.x, se.bands.ns, lwd = 2, col = "orange2", lty = 3)
# Smoothing spline.
plot(x, y, cex.lab = 1.1, col="darkgrey", xlab = x.lab, ylab = y.lab,
main = "Smoothing Spline (13 df)", bty = 'l')
lines(spline.smooth, lwd = 2, col = "brown")
# Piecewise Cubic Spline
plot(x, y, cex.lab = 1.1, col="darkgrey", xlab = x.lab, ylab = y.lab,
main = "Piecewise Cubic Spline \nNosso modelo", bty = 'l')
lines(sort.x, pred.bs$fit, lwd = 2, col = "steelblue")
matlines(sort.x, se.bands.bs, lwd = 2, col = "steelblue", lty = 3)
# Conclusões
Neste documento, exploramos a relação entre as variáveis
medv e indus utilizando abordagens de
regressão não linear, especificamente através do uso de splines. A
análise inicial dos resíduos revelou a presença de não linearidade,
sugerindo que um modelo linear simples não seria suficiente para
capturar a complexidade da relação entre as variáveis.
A utilização de gráficos de resíduos parciais permitiu isolar o efeito de cada variável, proporcionando uma visão mais clara de suas interações. Além disso, a consideração de outliers e a heterocedasticidade demonstraram a necessidade de ajustes adicionais no modelo, como a introdução de variáveis preditoras ou transformações.
A introdução de splines, especialmente as Piecewise Splines,
mostrou-se uma solução eficaz para modelar a não linearidade, permitindo
uma flexibilidade maior na representação das relações complexas entre
medv e indus. A discussão sobre a escolha do
número de nós com base nos graus de liberdade enfatiza a importância do
trade-off entre viés e variância, um aspecto crucial na construção de
modelos preditivos robustos.
Para aprofundar a compreensão sobre esses tópicos, as seguintes referências são recomendadas:
Esses materiais oferecem uma base sólida para entender não apenas as técnicas discutidas, mas também outras abordagens estatísticas e práticas que podem ser aplicadas em análises de dados complexas.