1 Intro

There was a recent blog post on plotting estimated values \((\hat{y})\) versus actual values \((y)\) here.

The premise is to plot the residuals as a function of model prediction, and comparing them to the line \(y = 0\), using a smoothing curve through the residuals. The idea is that for a well-fit model, the smoothing curve should approximately lie on the line \(y = 0\). This is true not only for linear models, but for any model that captures most of the explainable variance, and for which the unexplainable variance (the noise) is IID and zero mean.

If the residuals aren’t zero mean independently of the model’s predictions, then either you are missing some explanatory variables, or your model does not have the correct structure, or an appropriate inductive bias.

The following examples demonstrate NNS.reg() residuals to have 0 mean as an artifact of the quadrant partitioning method. We compare NNS to a basic linear regression and a nonlinear kernel regression.

2 Install NNS (>=0.4.6) from GitHub

#require(devtools); install_github('OVVO-Financial/NNS', ref = "NNS-Beta-Version")

library(NNS)
library(ggplot2)
library(np)
options(np.messages = FALSE)

3 Generate Data and Run Regressions

set.seed(34524)
N = 100
x1 = runif(N)
x2 = runif(N)
noise = 0.25*rnorm(N)
y = x1 + x2 + noise
qf = data.frame(x1=x1, x2=x2, y=y)
model = lm(y~x1+x2, data=qf)

qf$pred = predict(model, newdata=qf)
qf$residual = with(qf, y-pred)



nns_model = NNS.reg(qf[, 1:2], qf$y, residual.plot = FALSE, dist = "L2")$Fitted.xy

nns_cross_val = NNS.stack(qf[, 1:2], qf$y, IVs.test = qf[, 1:2], method = 1, dist = "L2")$stack
nns_cv_resid = nns_cross_val - qf$y
nns_cv = cbind.data.frame(y.hat = nns_cross_val, residuals = nns_cv_resid, y = qf$y)


bw = npregbw(xdat=qf[,1:2],ydat=qf$y)
np_model = npreg(bws = bw, residuals = TRUE)

np = data.frame(cbind(mean=np_model$mean, resid=np_model$resid))

# standard residual plot
ggplot(qf, aes(x=pred, y=residual)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("Standard residual plot",
          subtitle = "linear model and process")

ggplot(nns_model, aes(x=y.hat, y=residuals)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("Standard residual plot",
          subtitle = "NNS model and process")

ggplot(nns_cv, aes(x=y.hat, y=residuals)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("Standard residual plot",
          subtitle = "NNS CV model and process")

ggplot(np, aes(x=mean, y=resid)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("Standard residual plot",
          subtitle = "np model and process")

ggplot(qf, aes(x=pred, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("Standard prediction plot")

ggplot(nns_model, aes(x=y.hat, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("NNS Standard prediction plot")

ggplot(nns_cv, aes(x=y.hat, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("NNS CV Standard prediction plot")

ggplot(np, aes(x=mean, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("np Standard prediction plot")

x3 = runif(N)
y = x1 + x2 + 2*x3^2 + 0.25*noise
qf = data.frame(x1=x1, x2=x2, x3=x3, y=y)

# Fit a linear regression model
model2 = lm(y~x1+x2+x3, data=qf)
# summary(model2)

qf$pred = predict(model2, newdata=qf)
qf$residual = with(qf, y-pred)



nns_model2 = NNS.reg(qf[,1:3], qf$y, residual.plot = FALSE, dist = "L2")$Fitted.xy

nns_cross_val_2 = NNS.stack(qf[, 1:3], qf$y, IVs.test = qf[, 1:3], method = 1, dist = "L2")$stack
nns_cv_resid_2 = nns_cross_val_2 - qf$y
nns_cv_2 = cbind.data.frame(y.hat = nns_cross_val_2, residuals = nns_cv_resid_2, y = qf$y)


bw = npregbw(xdat=qf[,1:3],ydat=qf$y)
np_model2 = npreg(bws = bw, residuals = TRUE)

np2 = data.frame(cbind(mean=np_model2$mean, resid=np_model2$resid))



ggplot(qf, aes(x=pred, y=residual)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("Standard residual plot",
          subtitle = "linear model, quadratic process")

ggplot(nns_model2, aes(x=y.hat, y=residuals)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("NNS Standard residual plot",
          subtitle = "NNS model and process")

ggplot(nns_cv_2, aes(x=y.hat, y=residuals)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("Standard residual plot",
          subtitle = "NNS CV model and process")

ggplot(np2, aes(x=mean, y=resid)) + 
  geom_point(alpha=0.5) + geom_hline(yintercept=0, color="red") +
  geom_smooth(se=FALSE) + 
  ggtitle("np Standard residual plot",
          subtitle = "np model and process")

ggplot(qf, aes(x=pred, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("Standard prediction plot")

ggplot(nns_model2, aes(x=y.hat, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("NNS Standard prediction plot")

ggplot(nns_cv_2, aes(x=y.hat, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("NNS CV Standard prediction plot")

ggplot(np2, aes(x=mean, y=y)) + 
  geom_point(alpha=0.5) + geom_abline(color="red") + 
  geom_smooth(se=FALSE) + 
  ggtitle("np Standard prediction plot")

4 Comments

NNS.reg is not a black-box ML solution, rather, NNS provides a theoretically sound solution to dynamically partitioning regressors, and finding conditional outputs from those partitions.

NNS is not a one-trick pony, as it has been demonstrated to excel in time-series forecasting, nonlinear continuous regressions, and provide solutions for econometric applications. See the following examples:

I look forward to further discussions and collaboration with those equally as passionate about these issues, and open to embracing alternative solutions. If you found this presentation interesting or useful, please feel free to reach out via e-mail:

Thanks for your interest!