-
Notifications
You must be signed in to change notification settings - Fork 97
/
Copy pathmetaMDSiter.R
193 lines (193 loc) · 7.67 KB
/
metaMDSiter.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
`metaMDSiter` <-
function (dist, k = 2, try = 20, trymax = 20, trace = 1, plot = FALSE,
previous.best, engine = "monoMDS", maxit = 200,
parallel = getOption("mc.cores"), ...)
{
engine <- match.arg(engine, c("monoMDS", "isoMDS"))
EPS <- 0.05
if (engine == "monoMDS")
EPS <- EPS/100 # monoMDS stress (0,1), isoMDS (0,100)
RESLIM <- 0.01
RMSELIM <- 0.005
converged <- 0
## set tracing for engines
isotrace <- max(0, trace - 1)
monotrace <- engine == "monoMDS" && trace > 1
## explain monoMDS convergence codes (sol$icause)
monomsg <- c("no. of iterations >= maxit",
"stress < smin",
"stress ratio > sratmax",
"scale factor of the gradient < sfgrmin")
## monoMDS trace >= 2
monostop <- function(mod) {
if (mod$maxits == 0)
return(NULL)
lab <- monomsg[mod$icause]
cat(" ", mod$iters, "iterations: ", lab, "\n")
}
## collect monoMDS convergence code for trace
if (trace && engine == "monoMDS")
stopcoz <- numeric(4)
## Previous best or initial configuration
if (!missing(previous.best) && !is.null(previous.best)) {
## check if previous.best is from metaMDS or isoMDS
if (inherits(previous.best, c("metaMDS", "monoMDS")) ||
is.list(previous.best) &&
all(c("points", "stress") %in% names(previous.best))) {
## Previous best may come from another 'engine' or
## 'model': extract its 'points' and use as an initial
## configuration with 'maxit = 0' to evaluate the stress
## in current case, or take a matrix as configuration.
init <- previous.best$points
bestry <- max(0, previous.best$bestry)
trybase <- max(0, previous.best$tries)
converged <- max(0, previous.best$converged)
nc <- NCOL(init)
if (nc > k)
init <- init[, 1:k, drop = FALSE]
else if (nc < k)
for (i in 1:(k-nc))
init <- cbind(init, runif(NROW(init), -0.1, 0.1))
if (trace)
cat(sprintf("Starting from %d-dimensional configuration\n",
nc))
} else {
init <- as.matrix(previous.best)
bestry <- 0
trybase <- 0
}
## evaluate stress
s0 <- switch(engine,
"monoMDS" = monoMDS(dist, y = init, k = k, maxit = 0, ...),
"isoMDS" = isoMDS(dist, y = init, k = k, maxit = 0))
## Check whether model changed
if (is.list(previous.best) && !is.null(previous.best$stress) &&
!isTRUE(all.equal(previous.best$stress, s0$stress))) {
if (trace) cat("Stress differs from 'previous.best': reset tries\n")
if (inherits(previous.best, "metaMDS"))
previous.best$tries <- 0
}
} else {
## no previous.best: start with cmdscale
s0 <- switch(engine,
"monoMDS" = monoMDS(dist, y = cmdscale(dist, k = k), k = k,
maxit = maxit, ...),
"isoMDS" = isoMDS(dist, k = k, trace = isotrace,
maxit = maxit))
bestry <- 0
trybase <- 0
}
if (trace)
cat("Run 0 stress", s0$stress, "\n")
if (monotrace)
monostop(s0)
tries <- 0
## Prepare for parallel processing
if (is.null(parallel))
parallel <- 1
hasClus <- inherits(parallel, "cluster")
isParal <- hasClus || parallel > 1
isMulticore <- .Platform$OS.type == "unix" && !hasClus
if (isParal && !isMulticore && !hasClus) {
parallel <- makeCluster(parallel)
clusterEvalQ(parallel, library(vegan))
}
## get the number of clusters
if (inherits(parallel, "cluster"))
nclus <- length(parallel)
else
nclus <- parallel
## proper iterations
while(tries < try || tries < trymax && converged == 0) {
init <- replicate(nclus, initMDS(dist, k = k))
if (nclus > 1) isotrace <- FALSE
if (isParal) {
if (isMulticore) {
stry <-
mclapply(1:nclus, function(i)
switch(engine,
"monoMDS" = monoMDS(dist, init[,,i], k = k,
maxit = maxit, ...),
"isoMDS" = isoMDS(dist, init[,,i], k = k,
maxit = maxit, tol = 1e-07,
trace = isotrace)),
mc.cores = parallel)
} else {
stry <-
parLapply(parallel, 1:nclus, function(i)
switch(engine,
"monoMDS" = monoMDS(dist, init[,,i], k = k,
maxit = maxit, ...),
"isoMDS" = isoMDS(dist, init[,,i], k = k,
maxit = maxit, tol = 1e-07, trace = isotrace)))
}
} else {
stry <- list(switch(engine,
"monoMDS" = monoMDS(dist, init[,,1], k = k,
maxit = maxit, ...),
"isoMDS" = isoMDS(dist, init[,,1], k = k,
maxit = maxit, tol = 1e-07, trace = isotrace)))
}
## analyse results of 'nclus' tries
for (i in 1:nclus) {
tries <- tries + 1
if (trace)
cat("Run", tries, "stress", stry[[i]]$stress, "\n")
if (trace && engine == "monoMDS")
stopcoz[stry[[i]]$icause] <- stopcoz[stry[[i]]$icause] + 1L
if (monotrace)
monostop(stry[[i]])
if ((s0$stress - stry[[i]]$stress) > -EPS) {
pro <- procrustes(s0, stry[[i]], symmetric = TRUE)
if (plot && k > 1)
plot(pro)
if (stry[[i]]$stress < s0$stress) {
s0 <- stry[[i]]
## New best solution has not converged unless
## proved later
converged <- 0
bestry <- tries + trybase
if (trace)
cat("... New best solution\n")
}
summ <- summary(pro)
if (trace)
cat("... Procrustes: rmse", summ$rmse, " max resid",
max(summ$resid), "\n")
if (summ$rmse < RMSELIM && max(summ$resid) < RESLIM) {
if (trace)
cat("... Similar to previous best\n")
converged <- converged + 1
}
}
flush.console()
}
}
if (trace) {
if (converged > 0)
cat("*** Best solution repeated", converged, "times\n")
else if (engine == "monoMDS") {
cat(sprintf(
"*** Best solution was not repeated -- %s stopping criteria:\n",
engine))
for (i in seq_along(stopcoz))
if (stopcoz[i] > 0)
cat(sprintf("%6d: %s\n", stopcoz[i], monomsg[i]))
}
}
## stop socket cluster
if (isParal && !isMulticore && !hasClus)
stopCluster(parallel)
if (!missing(previous.best) && inherits(previous.best, "metaMDS")) {
tries <- tries + previous.best$tries
}
out <- s0
out$ndim = k
out$data <- attr(dist, "commname")
out$distance <- attr(dist, "method")
out$converged <- converged
out$tries <- tries
out$bestry <- bestry
out$engine <- engine
out
}