Todas as informações contidas neste arquivo foram retiradas do capítulo 11 do livro:
Extending the Linear Model with R:
Generalized Linear, Mixed Effects and Nonparametric Regression Models
de Julian J. Faraway
Editora Chapman & Hall/CRC, Taylor & Francis Group (2006)
Na sua forma mais simples, ela é apenas um estimador média móvel.
Mais geralmente, nossa estimativa de \(f\), chamada \(\hat{f}_{\lambda} (x)\), é:
\[ \hat{f}_{\lambda} (x) = \frac{1}{n \lambda} \sum_{j = 1}^{n} k \left( \frac{x - x_{j}}{\lambda} \right) Y_{j} = \frac{1}{n} \sum_{j = 1}^{n} w_{j} Y_{j}, \quad \text{onde} \quad w_{j} = k \left( \frac{x - x_{j}}{\lambda} \right) / \lambda \]
\(k\) é um kernel onde \(\int k = 1\).
O kernel de média móvel é retangular, mas kernels suavizadores podem fornecer melhores resultados.
\(\lambda\) é chamado de largura da banda, largura da janela ou parâmetro suavizador. Ele controla a suavidade da curva ajustada.
Se os \(x\)s são espaçados de forma muito desigual, então este estimador pode fornecer pobres resultados. Este problema é um pouco amenizado pelo estimador Nadaraya-Watson:
\[ f_{\lambda} (x) = \frac{\sum_{j = 1}^{n} w_{j} Y_{j}}{\sum_{j = 1}^{n} w_{j}} \]
Nós vemos que este estimador simplesmente modifica o estimador de média móvel então isso é uma verdadeira média ponderada onde os pesos para cada \(y\) devem somar um.
A vantagem da abordagem não-paramétrica é a proteção contra erros de especificação do modelo.
A implementação de um estimador kernel necessita de duas coisas: o kernel (\(k\)) e o parâmetro suavizador (\(\lambda\)).
A escolha ótima sob algumas suposições padrão é o kernel Epanechnikov:
\[ k(x) = \begin{cases} \frac{3}{4} (1 - x^{2}) & |x| < 1 \\ 0 & \text{caso contrário} \end{cases}\]
A escolha do parâmetro suavizador \(\lambda\) é crítica para a performance do estimador e é muito mais importante que a escolha do kernel.
Nós demonstramos o estimador Nadaraya-Watson a seguir para uma variedade de escolhas de larguras de banda no conjunto de dados Old Faithful na figura abaixo.
Nós utilizamos a função \(\texttt{ksmooth}\) que é parte do pacote base do \(\texttt{R}\). Essa função carece de muitas características/funcionalidades que podem ser encontradas em alguns outros pacotes, mas ela é adequada para uso simples.
O padrão utiliza um kernel uniforme, que é um pouco irregular/rugoso/áspero. Nós mudamos isso para um kernel normal:
g1 <- xyplot(waiting ~ eruptions,
type = c("p", "g"),
pch = 16,
xlab = "Tempo das erupções",
ylab = list(label = "Tempo\nde\nespera",
rot = 0),
main = "Largura da banda: 0.1",
scales = list(x = list(tick.number = 7),
y = list(tick.number = 10)),
par.settings = settings,
faithful) +
layer(panel.lines(ksmooth(faithful$eruptions, faithful$waiting,
"normal",
.1),
lwd = 2,
col = 2))
g2 <- xyplot(waiting ~ eruptions,
type = c("p", "g"),
pch = 16,
xlab = "Tempo das erupções",
ylab = list(label = "Tempo\nde\nespera",
rot = 0),
main = "Largura da banda: 0.5",
scales = list(x = list(tick.number = 7),
y = list(tick.number = 10)),
par.settings = settings,
faithful) +
layer(panel.lines(ksmooth(faithful$eruptions, faithful$waiting,
"normal",
.5),
lwd = 2,
col = 2))
g3 <- xyplot(waiting ~ eruptions,
type = c("p", "g"),
pch = 16,
xlab = "Tempo das erupções",
ylab = list(label = "Tempo\nde\nespera",
rot = 0),
main = "Largura da banda: 2",
scales = list(x = list(tick.number = 7),
y = list(tick.number = 10)),
par.settings = settings,
faithful) +
layer(panel.lines(ksmooth(faithful$eruptions, faithful$waiting,
"normal",
2),
lwd = 2,
col = 2))
print(g1,
position = c(0, 0,
1/3, 1),
more = TRUE)
print(g2,
position = c(1/3, 0,
2/3, 1),
more = TRUE)
print(g3,
position = c(2/3, 0,
1, 1))
Validação cruzada (CV: cross-validation) é um popular método de propósito geral. O critério é:
\[ CV(\lambda) = \frac{1}{n} \sum_{j = 1}^{n} (y_{j} - \hat{f}_{\lambda (j)} (x_{j}))^{2} \]
onde \((j)\) indica que o ponto \(j\) é retirado do ajuste. Nós pegamos o \(\lambda\) que minimiza este critério.
A verdadeira validação cruzada é cara computacionalmente, por isso uma aproximação para ela, conhecida como validação cruzada generalizada ou GCV (generalized cross-validation), é utilizada em alguns momentos.
Nós escolhemos \(\hat{f}\) para minimizar um critério de mínimos quadrados modificado:
\[ \frac{1}{n} \sum (y_{i} - f(x_{i}))^{2} + \lambda \int [{f}''(x)]^{2} \text{d}x \]
onde \(\lambda > 0\) é o parâmetro suavizador e \(\int [{f}''(x)]^{2} \text{d}x\) é uma penalidade irregular (rugosa, aspera, grosseira). Quando \(f\) é irregular, a penalidade é grande, mas quando \(f\) é suave, a penalidade é pequena.
Para esta escolha de penalidade irregular, a solução é de uma particular forma:
\(\hat{f}\) é um spline cúbico. Isso significa que \(\hat{f}\) é um polinômio cúbico segmentado em cada intervalo (\(x_{i}\), \(x_{i + 1}\)). Isso tem a propriedade que \(\hat{f}\), \({\hat{f}}'\) e \({\hat{f}}''\) são contínuas. Dado isso nós sabemos a forma da solução, a estimação é reduzida ao problema paramétrico de estimar os coeficientes de polinômios.
No \(\texttt{R}\), validação cruzada é utilizada para selecionar o parâmetro suavizador por padrão.
Nós mostramos a escolha padrão de suavização para três casos teste. Old Faithful, \(\texttt{exa}\) e \(\texttt{exb}\), sendo os dois últimos do pacote \(\texttt{faraway}\):
g1 <- xyplot(waiting ~ eruptions,
type = c("p", "g"),
pch = 16,
xlab = "Tempo das erupções",
ylab = list(label = "Tempo\nde\nespera",
rot = 0),
scales = list(x = list(tick.number = 7),
y = list(tick.number = 10)),
faithful) +
layer(panel.lines(smooth.spline(faithful$eruptions, faithful$waiting),
lwd = 2,
col = 2))
g2 <- xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
exa) +
layer(panel.lines(exa$x, exa$m,
lwd = 3,
col = 1)) +
layer(panel.lines(smooth.spline(exa$x, exa$y),
lwd = 2,
col = 2))
g3 <- xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
scales = list(y = list(tick.number = 6)),
exb) +
layer(panel.lines(exb$x, exb$m,
lwd = 3,
col = 1)) +
layer(panel.lines(smooth.spline(exb$x, exb$y),
lwd = 2,
col = 2))
print(g1,
position = c(0, 0,
1/3, 1),
more = TRUE)
print(g2,
position = c(1/3, 0,
2/3, 1),
more = TRUE)
print(g3,
position = c(2/3, 0,
1, 1))
Os nós (knots) dos B-splines utilizados para as bases são tipicamente muito pequenos em número do que o tamanho da amostra. O número de nós escolhidos controlam a quantidade da suavização. Para suavização splines, os valores únicos observados de \(x\) são os nós e \(\lambda\) é utilizado para controlar a suavização.
É discutível se o método de regressão spline é paramétrico ou não-paramétrico, porque uma vez que os nós são escolhidos, uma família paramétrica é especificada com um número finito de parâmetros. É a liberdade para escolher o número de nós que faz do método não-paramétrico.
Nós demonstramos algumas regressões por spline aqui. Nós utilizamos splines lineares segmentados nesete exemplo, que é construído e representado graficamente a seguir.
O spline é mostrado no gráfico da esquerda. As funções base são mostradas no gráfico da direita.
rhs <- function(x, c) ifelse(x > c, x - c, 0)
par(mfrow = c(1, 2)) ; curve(rhs(x, .5), 0, 1,
lwd = 2,
las = 1)
(knots <- 0 : 9/10)
[1] 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
dm <- outer(exa$x, knots, rhs)
matplot(exa$x, dm,
type = "l",
xlab = "x",
lwd = 2,
las = 1) ; layout(1)
Agora nós calculamos e mostramos o ajuste da regressão.
Onde o gráfico é mostrado à esquerda. Por as funções base serem segmentos lineares, o ajuste é também de segmentos lineares.
Um melhor ajuste pode ser obtido ajustando os nós que eles sejam mais densos em regiões de maior curvatura. O gráfico é mostrado à direita.
g <- lm(exa$y ~ dm)
g1 <- xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
scales = list(y = list(tick.number = 6)),
exa) +
layer(panel.lines(exa$x, predict(g),
lwd = 2,
col = 2))
newknots <- c(0, .5, .6, .65, .7, .75, .8, .85, .9, .95)
dmn <- outer(exa$x, newknots, rhs)
gn <- lm(exa$y ~ dmn)
g2 <- xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
scales = list(y = list(tick.number = 6)),
exa) +
layer(panel.lines(exa$x, predict(gn),
lwd = 2,
col = 2))
print(g1,
position = c(0, 0,
.5, 1),
more = TRUE)
print(g2,
position = c(.5, 0,
1, 1))
A função \(\texttt{bs()}\) do pacote \(\texttt{splines}\) pode ser utilizada para gerar as apropriadas bases dos splines (splines de alta ordem/grau). O padrão é B-splines cúbicos.
Nós mostramos 12 B-splines cúbicos uniformemente espaçados no intervalo \([0, 1]\). Os splines próximos da borda pegam uma diferente forma como visto no gráfico abaixo.
matplot(bs(seq(0, 1, length.out = 1000),
df = 12),
type = "l",
las = 1,
ylab = "")
Nós podemos utilizar mínimos quadrados para determinar os coeficientes. O ajuste é mostrado no gráfico abaixo.
sm1 <- lm(y ~ bs(x, 12), exa)
xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
exa) +
layer(panel.lines(exa$x, exa$m,
lwd = 3,
col = 1)) +
layer(panel.lines(exa$x, predict(sm1),
lwd = 2,
col = 2))
Primeiro nós selecionamos uma janela. Nós então ajustamos um polinômio aos dados nessa janela. A resposta predita no meio da janela é o valor ajustado. Nós então simplesmente deslizamos a janela sob o alcance dos dados, repetindo o processo de ajuste nas janelas móveis.
A implementação mais bem conhecida desse tipo de suavização é chamada \(lowess\) ou \(loess\).
Um termo linear é a escolha padrão na função \(loess\).
É importante selecionar bem a largura da janela. A escolha padrão pega três quartos dos dados.
Para o dado Old Faithful a escolha padrão é satisfatória, como visto no gráfico da esquerda.
Para o dado \(\texttt{exa}\), a escolha padrão é muito larga. A escolha que minimiza o erro quadrático integrado entre a estimativa e a verdadeira função necessita de um alcance ( proporção da extensão dos dados) de 0.22. Ambos os ajustes são mostrados na gráfico do meio.
Para o dado \(\texttt{exb}\), a escolha ótima de alcance é um (isso é todos os dados). Isso não é uma surpresa já que a verdadeira função é uma constante e então uma suavização máxima é desejada. Nós podemos ver que as qualidades robustas do loess previnem o ajuste de se tornar muito distorcido pelos dois outliers mesmo com a escolha padrão de alcance da suavização.
g1 <- xyplot(waiting ~ eruptions,
type = c("p", "g"),
pch = 16,
xlab = "Tempo das erupções",
ylab = list(label = "Tempo\nde\nespera",
rot = 0),
scales = list(x = list(tick.number = 7),
y = list(tick.number = 10)),
faithful) +
layer(panel.lines(loess(waiting ~ eruptions, faithful)$x[order(faithful$eruptions)],
loess(waiting ~ eruptions, faithful)$fitted[order(faithful$eruptions)],
lwd = 2,
col = 2))
g2 <- xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
exa) +
layer(panel.lines(exa$x, exa$m,
lwd = 3,
col = 1)) +
layer(panel.lines(loess(y ~ x, exa)$x, loess(y ~ x, exa)$fitted,
lwd = 3,
col = 3)) +
layer(panel.lines(loess(y ~ x, exa,
span = .22)$x,
loess(y ~ x, exa,
span = .22)$fitted,
lwd = 2,
col = 2))
g3 <- xyplot(y ~ x,
type = c("p", "g"),
pch = 16,
ylab = list(rot = 0),
scales = list(y = list(tick.number = 6)),
exb) +
layer(panel.lines(exb$x, exb$m,
lwd = 3,
col = 1)) +
layer(panel.lines(loess(y ~ x, exb)$x, loess(y ~ x, exb)$fitted,
lwd = 3,
col = 3)) +
layer(panel.lines(loess(y ~ x, exb,
span = 1)$x,
loess(y ~ x, exb,
span = 1)$fitted,
lwd = 2,
col = 2))
print(g1,
position = c(0, 0,
1/3, 1),
more = TRUE)
print(g2,
position = c(1/3, 0,
2/3, 1),
more = TRUE)
print(g3,
position = c(2/3, 0,
1, 1))
Nós definimos \(\hat{f}_{\lambda} (x) =\) média dos \(\lambda\) vizinhos mais próximos de \(x\). Nós deixamos a largura da janela variar para acomodar o mesmo número de pontos. Nós precisamos selecionar o número de vizinhos a serem utilizados, validação cruzada pode ser utilizada para este propósito.
Regressão não-paramétrica é mais robusta que regressão paramétrica no sentido de modelos, mas isso não significa que ela é robosta para outliers.
Métodos baseados em médias locais são sensíveis para outliers, então a mediana pode ser útil. Nós deixamos \(N(x, \lambda) = \{i: x_{i}\) é um dos \(\lambda\) vizinhos mais próximos de \(x\}\) então:
\[ \hat{f}_{\lambda} (x) = \text{mediana}\{Y_{i} \text{ tal que } i \in N(x, \lambda) \]
Este método é robusto para outliers, mas produz a ajuste com aparência irregular/rugosa. Pode-se querer suavizar novamente utilizando outro método. Isso é chamada=o de twicing.
Nós achamos que o suavizador loess faz uma boa suavização em todos os propósitos. Ele é robusto para outliers e ainda pode produzir ajustes suaves.
Quando você está confiante que outliers não estão presentes, suavização por splines é mais eficiente que polinômios locais.
Maldição da dimensionalidade:
Você precisa de um número extremamente grande de pontos para cobrir um espaço de alta dimensão para alta densidade.