fit.local.likelihood() can fit a local model in a small
number of local coordinates, called a chart, computed either by
principal component analysis (PCA) or by partial least squares (PLS).
PCA finds the directions in which the predictors vary most. PLS also
uses the response of the training data, so it can keep a direction in
which the predictors vary little but the response changes strongly. That
advantage is not guaranteed. With a noisy binary response the directions
found by PLS can be unstable, and with either method a chart of low
dimension may fail to capture a nonlinear response.
This vignette compares the two methods on simulated binary data for
which the true probabilities are known. It asks two separate questions.
Does unfolding the simplex remove a bend in the geometry? And does a
chart that uses the response improve prediction? All computations use
the exported functions fit.local.likelihood() and
unfold.simplex.chart(). The local simplex charts guide explains
the mathematics and the implementation.
The data lie on the max-normalized simplex: the surface of points
with nonnegative coordinates of which the largest equals one. Distance
on this surface is the length of the shortest path that stays on it. We
place points on two adjacent faces. A latent coordinate t
runs from -0.2 to 0.2 and is mapped to (1, 1+t) on one face
and to (1-t, 1) on the other, and the remaining coordinates
stay below one.
t.line <- c(seq(-.2, -.005, length.out=30), seq(.005, .2, length.out=30))
seam <- cbind(ifelse(t.line < 0, 1, 1-t.line),
ifelse(t.line < 0, 1+t.line, 1), .2)
flat <- unfold.simplex.chart(seam, c(1, .95, .2))
stopifnot(flat$certified)
old.par <- par(mfrow=c(1,2), mar=c(4,4,2,1))
plot(seam[,1:2], asp=1, pch=16, col="#0072B2", xlim=c(.76,1.24),
ylim=c(.76,1.24), xlab="First coordinate", ylab="Second coordinate",
main="Ambient coordinates")
plot(flat$coordinates[,1:2], asp=1, pch=16, col="#009E73",
xlim=c(.76,1.24), ylim=c(.76,1.24), xlab="First coordinate",
ylab="Unfolded second coordinate", main="Unfolded coordinates")A straight latent coordinate appears bent in ambient coordinates. Unfolding rotates the adjacent face into the query face; both axes use the same physical scale.
par(old.par)
variance.one <- function(x) {
values <- svd(scale(x, center=TRUE, scale=FALSE), nu=0, nv=0)$d^2
values[1]/sum(values)
}
knitr::kable(data.frame(Chart=c("Ambient", "Unfolded"),
`Variance retained by one PC (%)`=100*c(variance.one(seam),
variance.one(flat$coordinates)), check.names=FALSE), digits=1)| Chart | Variance retained by one PC (%) |
|---|---|
| Ambient | 80.4 |
| Unfolded | 100.0 |
That the unfolded chart retains more variance is a statement about the geometry. It is not evidence that the outcome is predicted better; the examples below examine prediction separately. The charts with and without unfolding use the same neighbors and the same weights. Unfolding is refused near a corner, when two coordinates are tied for the maximum, or when a neighborhood extends over more than two faces. Such cases are counted and reported; they are not forced into a chart.
Each observation has six coordinates. The seam coordinate
t and a broad coordinate u3 vary
substantially. A narrow coordinate u4 has a much smaller
range; the remaining two coordinates are nuisance features. The table
gives the true log odds. The true probability is
plogis(eta) and the observed response is a Bernoulli draw
with that probability.
| Example | True log odds, eta | Question |
|---|---|---|
| Seam | -1 + 2 t/0.2 | Does a chart retain variation across the bend? |
| High variance | -1 + 2 (u3-0.3)/0.2 | Can PCA capture a signal aligned with broad variation? |
| Low variance | -1 + 2 (u4-0.12)/0.02 | Does supervised PLS recover a narrow signal direction? |
| Constant | -1 | Does adaptation to binary noise hurt prediction? |
| Nonlinear | -1 + 2.5 ((t/0.2)^2 - 1/3) | How do local linear charts handle a symmetric nonlinear signal? |
The narrow coordinate varies only from 0.10 to 0.14, yet in the low-variance example its effect on the log odds is made as large as the effect of the broad coordinate in the high-variance example. This is a deliberately hard case for dimension reduction based on variance. It is not a claim that such signals are common in real data. The predictors are not standardized before the directions are computed, because standardizing would change the geometry and remove part of the contrast under study. The component scores are scaled within the local logistic fit.
scenarios <- c("Seam", "High variance", "Low variance", "Constant", "Nonlinear")
make.example <- function(seed, scenario, n=360L) {
set.seed(seed)
t <- runif(n, -.2, .2)
X <- cbind(ifelse(t < 0, 1, 1-t), ifelse(t < 0, 1+t, 1),
runif(n, .1, .5), runif(n, .10, .14),
runif(n, .02, .06), runif(n, .02, .06))
eta <- switch(scenario,
"Seam"=-1+2*t/.2,
"High variance"=-1+2*(X[,3]-.3)/.2,
"Low variance"=-1+2*(X[,4]-.12)/.02,
"Constant"=rep(-1,n),
"Nonlinear"=-1+2.5*((t/.2)^2-1/3))
probability <- plogis(eta)
list(X=X, y=rbinom(n,1,probability), probability=probability, t=t)
}Each of five independent repetitions per example has 180 training, 60 tuning and 120 test observations. The observations are independent in this simulation. For repeated specimens or matched sets, splitting must respect those groups instead.
We compare three charts: PCA in the original coordinates without unfolding (ambient PCA), PCA after unfolding, and PLS after unfolding. All three use 60 training neighbors, Gaussian weights, a ridge penalty of 1 on the slopes, and pseudo-counts of half a success and half a failure for the intercept. The neighborhood size and the ridge penalty are held fixed so that differences can be attributed to the charts; no claim is made that these values are optimal.
For each chart we report two results: one with a chart of dimension one, and one with the dimension chosen among 1, 2 and 3 as the one with the lowest Brier loss on the tuning observations (the smaller dimension wins a tie). The predictions for the tuning observations are made from the training observations only. The predictions for the test observations come from the same fits; the tuning observations are not added to the training set. Neither the test outcomes nor the true probabilities play any part in choosing a model.
methods <- c("Ambient PCA", "Unfolded PCA", "Unfolded PLS")
fit.chart <- function(X, y, query, method, dimension, degree=1L) {
fit.local.likelihood(X, y, X.eval=query, likelihood.family="bernoulli",
chart.geometry="linf.simplex", unfold.faces=method != "Ambient PCA",
coordinate.method=if (method == "Unfolded PLS") "local.pls" else "local.pca",
support.size=60L, chart.dim=dimension, degree=degree,
kernel="gaussian", lambda.ridge=1, intercept.mass=.5,
max.iter=70L, tol=1e-7, fallback="degree0")
}
train <- 1:180
tune <- 181:240
test <- 241:360Here is a complete fit for the low-variance example. Replacing
"Unfolded PLS" with "Unfolded PCA" changes the
directions while retaining the same geometry and likelihood
settings.
dat <- make.example(271301, "Low variance")
fit <- fit.chart(dat$X[train,], dat$y[train], dat$X[test,],
"Unfolded PLS", dimension=1L)
head(fit$fitted.values)
fit$diagnostics$status.countsThe loop below carries out the comparison. It includes two baselines. The local constant uses the same neighbors and weights as the charts but no directions, and the global constant is the smoothed proportion of successes in the whole training set. Both are useful for reference, especially in the example whose true probability is constant.
The results shown are from all 25 repetitions (five examples, five repetitions each). To keep the vignette quick to build, they are read from a file stored with the package instead of being recomputed. The last section explains how to rerun the loop with the same seeds, sample sizes and settings. The geometry demonstration and the probability slices are computed each time the vignette is built.
rows <- list()
illustrations <- list()
append.result <- function(scenario, repetition, method, mode, dimension,
prediction, truth, response, fallbacks, tuning.fallbacks=NA_integer_) {
stopifnot(all(is.finite(prediction)), all(prediction > 0 & prediction < 1))
data.frame(scenario=scenario, repetition=repetition, method=method,
mode=mode, dimension=dimension,
probability.mse=mean((prediction-truth)^2),
brier=mean((prediction-response)^2),
log.loss=-mean(response*log(prediction)+(1-response)*log1p(-prediction)),
fallbacks=fallbacks, tuning.fallbacks=tuning.fallbacks)
}
for (s in seq_along(scenarios)) for (repetition in 1:5) {
scenario <- scenarios[s]
dat <- make.example(271000+100*s+repetition, scenario)
example.predictions <- list()
for (method in methods) {
fits <- lapply(1:3, function(d) fit.chart(dat$X[train,], dat$y[train],
dat$X[c(tune,test),], method, d))
tuning.loss <- vapply(fits, function(f)
mean((f$fitted.values[1:60]-dat$y[tune])^2), numeric(1))
best <- which.min(tuning.loss)
for (mode in c("One dimension", "Tuned dimension")) {
d <- if (mode == "One dimension") 1L else best
chosen <- fits[[d]]
prediction <- chosen$fitted.values[61:180]
rows[[length(rows)+1L]] <- append.result(scenario, repetition, method,
mode, d, prediction, dat$probability[test], dat$y[test],
sum(chosen$diagnostics$per.eval$fallback[61:180]),
sum(chosen$diagnostics$per.eval$fallback[1:60]))
if (repetition == 1L) example.predictions[[paste(method,mode)]] <- prediction
}
}
local <- fit.chart(dat$X[train,], dat$y[train], dat$X[test,],
"Ambient PCA", dimension=1L, degree=0L)
constants <- list("Local constant"=local$fitted.values,
"Global constant"=rep((sum(dat$y[train])+.5)/(length(train)+1), length(test)))
for (method in names(constants)) {
rows[[length(rows)+1L]] <- append.result(scenario, repetition, method,
"Constant", 0L, constants[[method]], dat$probability[test], dat$y[test], 0L)
if (repetition == 1L) example.predictions[[method]] <- constants[[method]]
}
if (repetition == 1L) illustrations[[scenario]] <- list(data=dat,
predictions=example.predictions)
}
results <- do.call(rbind, rows)
stopifnot(nrow(results) == 200L)Two measures are reported, and lower is better for both. The
probability mean squared error (MSE) compares the
predicted probability with the true probability, which is known here,
and so measures the accuracy of estimation directly. The Brier
loss compares the predicted probability with the observed
binary response, and therefore also contains the variation of the
response itself, which no method can remove. The log loss on the test
observations is stored in results as well. A low MSE, or an
optimizer that converged, does not show that the predicted probabilities
are well calibrated.
The table gives means over the five repetitions. The figures show each repetition as well as the mean, so that a favorable average cannot conceal repetitions in which the order of the methods is reversed. Five repetitions are enough for an illustration. They do not make a simulation study with adequate power, and they do not rank the methods in general.
summary.table <- aggregate(cbind(probability.mse,brier) ~ scenario+method+mode,
results, mean)
summary.table <- summary.table[order(match(summary.table$scenario, scenarios),
match(summary.table$mode,c("One dimension","Tuned dimension","Constant")),
match(summary.table$method,c(methods,"Local constant","Global constant"))),]
knitr::kable(summary.table, digits=4, row.names=FALSE,
col.names=c("Example","Method","Chart","Probability MSE","Brier loss"))| Example | Method | Chart | Probability MSE | Brier loss |
|---|---|---|---|---|
| Seam | Ambient PCA | One dimension | 0.0094 | 0.1753 |
| Seam | Unfolded PCA | One dimension | 0.0082 | 0.1727 |
| Seam | Unfolded PLS | One dimension | 0.0054 | 0.1677 |
| Seam | Ambient PCA | Tuned dimension | 0.0064 | 0.1677 |
| Seam | Unfolded PCA | Tuned dimension | 0.0067 | 0.1679 |
| Seam | Unfolded PLS | Tuned dimension | 0.0059 | 0.1678 |
| Seam | Local constant | Constant | 0.0084 | 0.1738 |
| Seam | Global constant | Constant | 0.0460 | 0.2171 |
| High variance | Ambient PCA | One dimension | 0.0076 | 0.1832 |
| High variance | Unfolded PCA | One dimension | 0.0092 | 0.1848 |
| High variance | Unfolded PLS | One dimension | 0.0054 | 0.1808 |
| High variance | Ambient PCA | Tuned dimension | 0.0062 | 0.1820 |
| High variance | Unfolded PCA | Tuned dimension | 0.0066 | 0.1817 |
| High variance | Unfolded PLS | Tuned dimension | 0.0074 | 0.1826 |
| High variance | Local constant | Constant | 0.0095 | 0.1848 |
| High variance | Global constant | Constant | 0.0465 | 0.2265 |
| Low variance | Ambient PCA | One dimension | 0.0473 | 0.2179 |
| Low variance | Unfolded PCA | One dimension | 0.0472 | 0.2178 |
| Low variance | Unfolded PLS | One dimension | 0.0473 | 0.2173 |
| Low variance | Ambient PCA | Tuned dimension | 0.0456 | 0.2132 |
| Low variance | Unfolded PCA | Tuned dimension | 0.0442 | 0.2107 |
| Low variance | Unfolded PLS | Tuned dimension | 0.0162 | 0.1929 |
| Low variance | Local constant | Constant | 0.0472 | 0.2180 |
| Low variance | Global constant | Constant | 0.0461 | 0.2165 |
| Constant | Ambient PCA | One dimension | 0.0025 | 0.1896 |
| Constant | Unfolded PCA | One dimension | 0.0029 | 0.1889 |
| Constant | Unfolded PLS | One dimension | 0.0037 | 0.1901 |
| Constant | Ambient PCA | Tuned dimension | 0.0034 | 0.1919 |
| Constant | Unfolded PCA | Tuned dimension | 0.0041 | 0.1916 |
| Constant | Unfolded PLS | Tuned dimension | 0.0041 | 0.1897 |
| Constant | Local constant | Constant | 0.0028 | 0.1899 |
| Constant | Global constant | Constant | 0.0005 | 0.1891 |
| Nonlinear | Ambient PCA | One dimension | 0.0180 | 0.2051 |
| Nonlinear | Unfolded PCA | One dimension | 0.0173 | 0.2047 |
| Nonlinear | Unfolded PLS | One dimension | 0.0099 | 0.1967 |
| Nonlinear | Ambient PCA | Tuned dimension | 0.0110 | 0.1924 |
| Nonlinear | Unfolded PCA | Tuned dimension | 0.0129 | 0.1928 |
| Nonlinear | Unfolded PLS | Tuned dimension | 0.0101 | 0.1965 |
| Nonlinear | Local constant | Constant | 0.0165 | 0.2036 |
| Nonlinear | Global constant | Constant | 0.0255 | 0.2095 |
colors <- c("Ambient PCA"="#0072B2", "Unfolded PCA"="#009E73",
"Unfolded PLS"="#D55E00", "Local constant"="#666666", "Global constant"="#AA4499")
labels <- c("PCA", "Unfolded\nPCA", "Unfolded\nPLS", "Local\nconstant", "Global\nconstant")
old.par <- par(mfrow=c(5,2), mar=c(3.2,4,2,1), oma=c(0,0,1,0), cex=.8)
for (scenario in scenarios) for (mode in c("One dimension","Tuned dimension")) {
tab <- results[results$scenario==scenario & results$mode %in% c(mode,"Constant"),]
order.methods <- names(colors)
plot(NA, xlim=c(.7,5.3), ylim=c(0, max(tab$probability.mse)*1.08+1e-6),
xaxt="n", xlab="", ylab="Probability MSE", main=paste(scenario,mode,sep=" — "))
axis(1, at=1:5, labels=labels, cex.axis=.8)
for (j in 1:5) {
values <- tab$probability.mse[tab$method==order.methods[j]]
points(j+seq(-.12,.12,length.out=5), values, pch=16, cex=.7,
col=adjustcolor(colors[j],alpha.f=.6))
points(j, mean(values), pch=18, cex=1.3, col=colors[j])
}
}Held-out probability error for all five repetitions. Small points are independent repetitions; diamonds are their means. Each panel uses its own labelled vertical scale. Lower is better. The constant baselines are identical in the two columns.
The next plot shows predicted against true probabilities for the test observations of the first repetition of each example. The first repetition was designated in advance and was not chosen for its results. Each point is one test observation. On the diagonal, the prediction equals the true probability; the diagonal is not a fitted line. In the constant example all observations have the same true probability, so their vertical spread shows how much each method follows the noise.
old.par <- par(mfrow=c(5,2), mar=c(3,3.3,2,1), cex=.8)
for (scenario in scenarios) for (method in c("Unfolded PCA","Unfolded PLS")) {
item <- illustrations[[scenario]]
prediction <- item$predictions[[paste(method,"One dimension")]]
plot(item$data$probability[test], prediction, xlim=c(0,1), ylim=c(0,1),
xlab="True probability", ylab="Predicted probability", pch=16, cex=.65,
col=adjustcolor(colors[method],alpha.f=.55), main=paste(scenario,method,sep=" — "))
abline(0,1,lty=2,col="grey35")
}Predicted versus known true probabilities for first-repetition test observations. Columns compare unfolded PCA and PLS, each with one dimension. Points near the diagonal recover the true probabilities; all panels share the same axes.
The curves below are fitted to the training observations of the first repetition. In each panel the coordinate that carries the signal varies over its range while the other coordinates are fixed at the centers of their ranges. The curves are the fitted probabilities at those points; they are not averages of the points in the scatterplots above. The black curve is the true probability. All methods use charts of dimension one, and nothing was tuned again for these plots.
slices <- list()
old.par <- par(mfrow=c(3,2), mar=c(4,4,2,1), cex=.85)
for (scenario in scenarios) {
grid <- seq(-1,1,length.out=60)
t <- if (scenario %in% c("Seam","Nonlinear","Constant")) .2*grid else rep(.08,60)
X.slice <- cbind(ifelse(t < 0,1,1-t), ifelse(t < 0,1+t,1), .3,.12,.04,.04)
if (scenario=="High variance") X.slice[,3] <- .3+.2*grid
if (scenario=="Low variance") X.slice[,4] <- .12+.02*grid
eta <- if (scenario=="Constant") rep(-1,60) else if (scenario=="Nonlinear")
-1+2.5*(grid^2-1/3) else -1+2*grid
truth <- plogis(eta)
dat <- illustrations[[scenario]]$data
prediction <- vapply(methods, function(method)
fit.chart(dat$X[train,],dat$y[train],X.slice,method,1L)$fitted.values,
numeric(60))
axis.value <- if (scenario=="High variance") X.slice[,3] else if (scenario=="Low variance")
X.slice[,4] else t
axis.label <- if (scenario=="High variance") "Broad coordinate u3" else if (scenario=="Low variance")
"Narrow coordinate u4" else "Seam coordinate t"
plot(axis.value,truth,type="l",lwd=2,col="black",ylim=c(0,1),
xlab=axis.label,ylab="Probability",main=scenario)
for (j in seq_along(methods)) lines(axis.value,prediction[,j],col=colors[j],lwd=1.5,lty=j)
slices[[scenario]] <- list(X=X.slice,truth=truth,prediction=prediction)
}
plot.new()
legend("center",c("Truth",methods),col=c("black",colors[methods]),
lty=c(1,1,2,3),lwd=c(2,1.5,1.5,1.5),bty="n",cex=.9)Conditional probability slices through the same first-repetition training fits. Nuisance coordinates are fixed at their generating centers. Black is the known truth; colored curves use one-dimensional charts. All probability axes run from zero to one.
tuned <- results[results$mode=="Tuned dimension",]
selection <- as.data.frame(xtabs(~scenario+method+dimension, tuned))
selection <- selection[selection$Freq>0,]
knitr::kable(selection, row.names=FALSE,
col.names=c("Example","Method","Selected dimension","Repetitions"))| Example | Method | Selected dimension | Repetitions |
|---|---|---|---|
| Constant | Ambient PCA | 1 | 3 |
| High variance | Ambient PCA | 1 | 3 |
| Low variance | Ambient PCA | 1 | 2 |
| Nonlinear | Ambient PCA | 1 | 1 |
| Constant | Unfolded PCA | 1 | 3 |
| High variance | Unfolded PCA | 1 | 2 |
| Low variance | Unfolded PCA | 1 | 1 |
| Nonlinear | Unfolded PCA | 1 | 1 |
| Seam | Unfolded PCA | 1 | 1 |
| Constant | Unfolded PLS | 1 | 4 |
| High variance | Unfolded PLS | 1 | 2 |
| Nonlinear | Unfolded PLS | 1 | 3 |
| Seam | Unfolded PLS | 1 | 4 |
| Constant | Ambient PCA | 2 | 1 |
| High variance | Ambient PCA | 2 | 2 |
| Nonlinear | Ambient PCA | 2 | 2 |
| Seam | Ambient PCA | 2 | 4 |
| High variance | Unfolded PCA | 2 | 2 |
| Nonlinear | Unfolded PCA | 2 | 2 |
| Seam | Unfolded PCA | 2 | 3 |
| Constant | Unfolded PLS | 2 | 1 |
| High variance | Unfolded PLS | 2 | 1 |
| Nonlinear | Unfolded PLS | 2 | 2 |
| Seam | Unfolded PLS | 2 | 1 |
| Constant | Ambient PCA | 3 | 1 |
| Low variance | Ambient PCA | 3 | 3 |
| Nonlinear | Ambient PCA | 3 | 2 |
| Seam | Ambient PCA | 3 | 1 |
| Constant | Unfolded PCA | 3 | 2 |
| High variance | Unfolded PCA | 3 | 1 |
| Low variance | Unfolded PCA | 3 | 4 |
| Nonlinear | Unfolded PCA | 3 | 2 |
| Seam | Unfolded PCA | 3 | 1 |
| High variance | Unfolded PLS | 3 | 2 |
| Low variance | Unfolded PLS | 3 | 5 |
knitr::kable(aggregate(cbind(fallbacks,tuning.fallbacks) ~ scenario+method+mode,
results, sum), row.names=FALSE,
col.names=c("Example","Method","Chart","Test fallbacks","Tuning fallbacks"))| Example | Method | Chart | Test fallbacks | Tuning fallbacks |
|---|---|---|---|---|
| Constant | Ambient PCA | One dimension | 0 | 0 |
| High variance | Ambient PCA | One dimension | 0 | 0 |
| Low variance | Ambient PCA | One dimension | 0 | 0 |
| Nonlinear | Ambient PCA | One dimension | 0 | 0 |
| Seam | Ambient PCA | One dimension | 0 | 0 |
| Constant | Unfolded PCA | One dimension | 0 | 0 |
| High variance | Unfolded PCA | One dimension | 0 | 0 |
| Low variance | Unfolded PCA | One dimension | 0 | 0 |
| Nonlinear | Unfolded PCA | One dimension | 0 | 0 |
| Seam | Unfolded PCA | One dimension | 0 | 0 |
| Constant | Unfolded PLS | One dimension | 0 | 0 |
| High variance | Unfolded PLS | One dimension | 0 | 0 |
| Low variance | Unfolded PLS | One dimension | 0 | 0 |
| Nonlinear | Unfolded PLS | One dimension | 0 | 0 |
| Seam | Unfolded PLS | One dimension | 0 | 0 |
| Constant | Ambient PCA | Tuned dimension | 0 | 0 |
| High variance | Ambient PCA | Tuned dimension | 0 | 0 |
| Low variance | Ambient PCA | Tuned dimension | 0 | 0 |
| Nonlinear | Ambient PCA | Tuned dimension | 0 | 0 |
| Seam | Ambient PCA | Tuned dimension | 0 | 0 |
| Constant | Unfolded PCA | Tuned dimension | 0 | 0 |
| High variance | Unfolded PCA | Tuned dimension | 0 | 0 |
| Low variance | Unfolded PCA | Tuned dimension | 0 | 0 |
| Nonlinear | Unfolded PCA | Tuned dimension | 0 | 0 |
| Seam | Unfolded PCA | Tuned dimension | 0 | 0 |
| Constant | Unfolded PLS | Tuned dimension | 0 | 0 |
| High variance | Unfolded PLS | Tuned dimension | 0 | 0 |
| Low variance | Unfolded PLS | Tuned dimension | 0 | 0 |
| Nonlinear | Unfolded PLS | Tuned dimension | 0 | 0 |
| Seam | Unfolded PLS | Tuned dimension | 0 | 0 |
A fallback occurs when a chart cannot be certified or the optimizer
fails; the prediction is then the smoothed local proportion. These
predictions are kept in the scores above and are not removed to make a
method look better. The table counts fallbacks only among the fits that
are reported, namely those with dimension one and those with the
selected dimension. Some fits are counted under both headings, so the
two counts should not be added. results also contains the
selected dimension and the test log loss of each fit.
The low-variance example shows that using the response to choose directions is not sufficient in itself. With one dimension, unfolded PCA and PLS have mean probability MSEs of 0.0472 and 0.0473, respectively. After selecting dimension on the tuning observations, these become 0.0442 and 0.0162. Weighted PLS is based on covariances, so it remains sensitive to the scale of the predictors, to the noise in a binary response, and to the number of directions kept. In this example its advantage for the narrow signal appears only after the dimension has been selected; its first direction alone does not provide it.
In the constant example the global constant has the lowest probability MSE. In the high-variance example PCA with a tuned dimension has a lower mean probability MSE than PLS with a tuned dimension. Tuning the dimension sometimes makes the test results worse than the fit of dimension one, because 60 tuning observations give a noisy criterion for the choice. This is why the vignette shows the baselines, both the fixed and the selected dimension, and every repetition.
Each example was designed to isolate one issue. The seam example separates the correction of a fold in the geometry from the learning of a direction related to the response. The high-variance example suits the criterion of PCA. The low-variance example puts the information about the response in a direction that PCA is likely to discard; a chart of higher dimension lets PCA recover it, at the price of more variable estimates. PLS can help at low dimension, provided its directions are computed from the training data alone.
The constant example is a necessary control, since it contains no direction to be found. The nonlinear example tests whether a nonlinear function is close enough to linear within a neighborhood. Where the signal is symmetric about a point, the local covariance on which PLS relies can be weak even though the response depends strongly on position. None of the methods is expected to be best throughout, and the variation among repetitions matters as much as the averages.
Before relying on such a comparison in a scientific analysis, extend the simulation to realistic sample sizes, class balance, dependence among observations, missing data and geometry. Tune the neighborhood size and the penalties within the training data, assess the calibration of the probabilities on a separate hold-out sample, and examine the results near corners of the simplex and where the data are sparse. Under case-control sampling, the probabilities estimated in the selected sample do not give the prevalence in the population unless the sampling fractions are known.
The stored results were produced by the code shown above. They contain all 25 seeds, all 200 rows of results, and the predictions of the first repetition of each example, and nothing else: no real data and no reference predictions from other software. They also record the package versions and the SHA-256 checksum of this vignette. When the vignette is built, it checks that the stored results were produced from the present version of its source.
To recompute every comparison while rendering, use:
options(geosmooth.recompute.vignettes=TRUE)
rmarkdown::render("vignettes/pca-pls-chart-comparison.Rmd")After changing this vignette, regenerate the stored results by
running Rscript vignettes/precompute-pca-pls.R from the
package source directory. The computation runs serially and needs only
single-threaded BLAS and OpenMP. The package versions used for the
stored results are listed below. ?fit.local.likelihood and
?unfold.simplex.chart describe the fitting options and the
diagnostics.
#> geosmooth dgraphs Rcpp digest
#> "0.2.0" "0.4.1" "1.1.2" "0.6.39"