Skip to content

Commit e39fc2b

Browse files
2 parents a6b33d7 + 32cbfc8 commit e39fc2b

20 files changed

+231
-42
lines changed

DESCRIPTION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: spmodel
22
Title: Spatial Statistical Modeling and Prediction
3-
Version: 0.8.0
3+
Version: 0.9.0
44
Authors@R: c(
55
person(given = "Michael",
66
family = "Dumelle",

NEWS.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# spmodel 0.9.0
2+
3+
## Major Updates
4+
5+
* Added the `range_constrain` argument to `splm()` and `spglm()` to contrain the range parameter to enhance numerical stability.
6+
17
# spmodel 0.8.0
28

39
## Major Updates

R/cov_estimate_sv.R

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ cov_estimate_sv <- function(data_object, formula, spcov_initial, estmethod,
4343
if (all(spcov_initial_val$is_known)) {
4444
spcov_estimate_val <- use_svloss_known(spcov_initial_val, dist_matrix_list, cov_initial_val$esv, weights)
4545
} else {
46-
spcov_estimate_val <- use_svloss(spcov_initial_val, dist_matrix_list, cov_initial_val$esv, weights, optim_dotlist)
46+
spcov_estimate_val <- use_svloss(spcov_initial_val, dist_matrix_list, cov_initial_val$esv, weights, optim_dotlist,
47+
data_object = data_object)
4748
}
4849
spcov_estimate_val
4950
}

R/cov_initial_search.R

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ cov_initial_search.exponential <- function(spcov_initial_NA, estmethod, data_obj
146146
dist_vector = dist_vector,
147147
residual_vector2 = residual_vector2
148148
)
149+
149150
# find minimum value and record parameters
150151
min_params <- unlist(cov_grid_splits[[which.min(objvals)]])
151152
spcov_params <- min_params[c("de", "ie", "range", "rotate", "scale")]

R/get_data_object.R

+38-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
get_data_object_splm <- function(formula, data, spcov_initial, xcoord, ycoord, estmethod,
2-
anisotropy, random, randcov_initial, partition_factor, local, ...) {
2+
anisotropy, random, randcov_initial, partition_factor, local,
3+
range_constrain, ...) {
34

45

56
# covert sp to sf
@@ -198,6 +199,40 @@ get_data_object_splm <- function(formula, data, spcov_initial, xcoord, ycoord, e
198199
y_range <- range(obdata[[ycoord]])
199200
max_halfdist <- sqrt((max(x_range) - min(x_range))^2 + (max(y_range) - min(y_range))^2) / 2
200201

202+
# if (is.logical(range_constrain)) {
203+
# if (range_constrain) {
204+
# range_constrain_value <- 2 * max_halfdist * 5
205+
# } else {
206+
# range_constrain_value <- Inf
207+
# }
208+
# } else if (is.numeric(range_constrain)) {
209+
# range_constrain <- TRUE
210+
# range_constrain_value <- 0
211+
# } else {
212+
# stop("range_constrain must be logical or numeric.", call. = FALSE)
213+
# }
214+
215+
# range constrain
216+
max_range_scale <- 5
217+
range_constrain_value <- 2 * max_halfdist * max_range_scale
218+
if ("range" %in% names(spcov_initial$is_known)) {
219+
if (spcov_initial$is_known[["range"]] || (spcov_initial$initial[["range"]] > range_constrain_value)) {
220+
range_constrain <- FALSE
221+
}
222+
}
223+
224+
if (inherits(spcov_initial, "none")) {
225+
range_constrain <- FALSE
226+
}
227+
228+
if (is.logical(range_constrain)) {
229+
if (!range_constrain) {
230+
range_constrain_value <- NULL
231+
}
232+
} else {
233+
stop("range_constrain must be logical.", call. = FALSE)
234+
}
235+
201236
# override anisotropy argument if needed
202237
anisotropy <- get_anisotropy_corrected(anisotropy, spcov_initial)
203238

@@ -295,7 +330,8 @@ get_data_object_splm <- function(formula, data, spcov_initial, xcoord, ycoord, e
295330
randcov_list = randcov_list, randcov_names = randcov_names,
296331
sf_column_name = sf_column_name, terms = terms_val, var_adjust = local$var_adjust,
297332
X_list = X_list, xcoord = xcoord, xlevels = xlevels, y_list = y_list, ycoord = ycoord,
298-
ycoord_orig_name = ycoord_orig_name, ycoord_orig_val = ycoord_orig_val, s2 = s2, diagtol = diagtol
333+
ycoord_orig_name = ycoord_orig_name, ycoord_orig_val = ycoord_orig_val, s2 = s2, diagtol = diagtol,
334+
range_constrain = range_constrain, range_constrain_value = range_constrain_value
299335
)
300336
}
301337

R/get_data_object_glm.R

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
get_data_object_spglm <- function(formula, family, data, spcov_initial, xcoord, ycoord, estmethod,
2-
anisotropy, random, randcov_initial, partition_factor, local, ...) {
2+
anisotropy, random, randcov_initial, partition_factor, local,
3+
range_constrain, ...) {
34

45

56

@@ -231,6 +232,27 @@ get_data_object_spglm <- function(formula, family, data, spcov_initial, xcoord,
231232
y_range <- range(obdata[[ycoord]])
232233
max_halfdist <- sqrt((max(x_range) - min(x_range))^2 + (max(y_range) - min(y_range))^2) / 2
233234

235+
# range constrain
236+
max_range_scale <- 5
237+
range_constrain_value <- 2 * max_halfdist * max_range_scale
238+
if ("range" %in% names(spcov_initial$is_known)) {
239+
if (spcov_initial$is_known[["range"]] || (spcov_initial$initial[["range"]] > range_constrain_value)) {
240+
range_constrain <- FALSE
241+
}
242+
}
243+
244+
if (inherits(spcov_initial, "none")) {
245+
range_constrain <- FALSE
246+
}
247+
248+
if (is.logical(range_constrain)) {
249+
if (!range_constrain) {
250+
range_constrain_value <- NULL
251+
}
252+
} else {
253+
stop("range_constrain must be logical.", call. = FALSE)
254+
}
255+
234256
# override anisotropy argument if needed
235257
anisotropy <- get_anisotropy_corrected(anisotropy, spcov_initial)
236258

@@ -334,7 +356,8 @@ get_data_object_spglm <- function(formula, family, data, spcov_initial, xcoord,
334356
randcov_list = randcov_list, randcov_names = randcov_names,
335357
sf_column_name = sf_column_name, size = size, terms = terms_val, var_adjust = local$var_adjust,
336358
X_list = X_list, xcoord = xcoord, xlevels = xlevels, y_list = y_list, ycoord = ycoord,
337-
ycoord_orig_name = ycoord_orig_name, ycoord_orig_val = ycoord_orig_val, s2 = s2, diagtol = diagtol
359+
ycoord_orig_name = ycoord_orig_name, ycoord_orig_val = ycoord_orig_val, s2 = s2, diagtol = diagtol,
360+
range_constrain = range_constrain, range_constrain_value = range_constrain_value
338361
)
339362
}
340363

R/glogclik.R

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
#' @return (Gaussian log) composite likelihood while optimzing
99
#'
1010
#' @noRd
11-
glogclik <- function(par, spcov_orig2optim, residual_vector2, dist_vector) {
11+
glogclik <- function(par, spcov_orig2optim, residual_vector2, dist_vector, data_object) {
1212
# transforming to original scale
13-
spcov_orig_val <- spcov_optim2orig(spcov_orig2optim, par, spcov_profiled = FALSE)
13+
spcov_orig_val <- spcov_optim2orig(spcov_orig2optim, par, spcov_profiled = FALSE,
14+
data_object = data_object)
1415
# making a covariance parameter vector
1516
spcov_params_val <- get_spcov_params(spcov_type = class(spcov_orig2optim), spcov_orig_val = spcov_orig_val)
1617
glogclikloss_val <- get_glogclikloss(spcov_params_val, residual_vector2, dist_vector)

R/gloglik_anis.R

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ gloglik_anis <- function(par, spcov_orig2optim, data_object, estmethod,
2323
randcov_profiled = NULL) {
2424

2525
# transforming to original scale
26-
spcov_orig_val <- spcov_optim2orig(spcov_orig2optim, par, spcov_profiled = spcov_profiled)
26+
spcov_orig_val <- spcov_optim2orig(spcov_orig2optim, par, spcov_profiled = spcov_profiled,
27+
data_object = data_object)
2728

2829
# making a covariance parameter vector
2930
spcov_params_val <- get_spcov_params(spcov_type = class(spcov_orig2optim), spcov_orig_val = spcov_orig_val)

R/laploglik_anis.R

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ laploglik_anis <- function(par, spcov_orig2optim, dispersion_orig2optim, data_ob
99
par <- dispersion_orig_val$new_par
1010

1111
# transforming to original scale
12-
spcov_orig_val <- spcov_optim2orig(spcov_orig2optim, par, spcov_profiled = spcov_profiled)
12+
spcov_orig_val <- spcov_optim2orig(spcov_orig2optim, par, spcov_profiled = spcov_profiled,
13+
data_object = data_object)
1314

1415
# making a covariance parameter vector
1516
spcov_params_val <- get_spcov_params(spcov_type = class(spcov_orig2optim), spcov_orig_val = spcov_orig_val)

R/spcov_optim2orig.R

+24-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ spcov_optim2orig.exponential <- function(spcov_orig2optim, par, spcov_profiled,
2424
ie <- exp(fill_optim_par_val[["ie_log"]])
2525
}
2626

27-
range <- exp(fill_optim_par_val[["range_log"]])
27+
if (data_object$range_constrain) {
28+
range <- expit(fill_optim_par_val[["range_logodds"]]) * data_object$range_constrain_value
29+
} else {
30+
range <- exp(fill_optim_par_val[["range_log"]])
31+
}
32+
2833
rotate <- pi * expit(fill_optim_par_val[["rotate_logodds"]])
2934
scale <- expit(fill_optim_par_val[["scale_logodds"]])
3035

@@ -76,7 +81,12 @@ spcov_optim2orig.matern <- function(spcov_orig2optim, par, spcov_profiled, data_
7681
extra_t <- expit(fill_optim_par_val[["extra_logodds"]])
7782
# fix to be in [1/5, 5]
7883
extra <- extra_t * (5 - 1 / 5) + 1 / 5
79-
range <- exp(fill_optim_par_val[["range_log"]])
84+
# range <- exp(fill_optim_par_val[["range_log"]])
85+
if (data_object$range_constrain) {
86+
range <- expit(fill_optim_par_val[["range_logodds"]]) * data_object$range_constrain_value
87+
} else {
88+
range <- exp(fill_optim_par_val[["range_log"]])
89+
}
8090
rotate <- pi * expit(fill_optim_par_val[["rotate_logodds"]])
8191
scale <- expit(fill_optim_par_val[["scale_logodds"]])
8292

@@ -99,7 +109,12 @@ spcov_optim2orig.cauchy <- function(spcov_orig2optim, par, spcov_profiled, data_
99109

100110

101111
extra <- exp(fill_optim_par_val[["extra_log"]])
102-
range <- exp(fill_optim_par_val[["range_log"]])
112+
# range <- exp(fill_optim_par_val[["range_log"]])
113+
if (data_object$range_constrain) {
114+
range <- expit(fill_optim_par_val[["range_logodds"]]) * data_object$range_constrain_value
115+
} else {
116+
range <- exp(fill_optim_par_val[["range_log"]])
117+
}
103118
rotate <- pi * expit(fill_optim_par_val[["rotate_logodds"]])
104119
scale <- expit(fill_optim_par_val[["scale_logodds"]])
105120

@@ -122,7 +137,12 @@ spcov_optim2orig.pexponential <- function(spcov_orig2optim, par, spcov_profiled,
122137

123138
extra_half <- expit(fill_optim_par_val[["extra_logodds"]])
124139
extra <- 2 * extra_half
125-
range <- exp(fill_optim_par_val[["range_log"]])
140+
# range <- exp(fill_optim_par_val[["range_log"]])
141+
if (data_object$range_constrain) {
142+
range <- expit(fill_optim_par_val[["range_logodds"]]) * data_object$range_constrain_value
143+
} else {
144+
range <- exp(fill_optim_par_val[["range_log"]])
145+
}
126146
rotate <- pi * expit(fill_optim_par_val[["rotate_logodds"]])
127147
scale <- expit(fill_optim_par_val[["scale_logodds"]])
128148

R/spcov_orig2optim.R

+52-12
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,16 @@ spcov_orig2optim.exponential <- function(spcov_initial, spcov_profiled, data_obj
4141

4242
# range changes based on type
4343
range <- spcov_initial$initial[["range"]]
44-
range_log <- log(range)
45-
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
46-
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
44+
if (data_object$range_constrain) {
45+
range_prop <- range / data_object$range_constrain_value
46+
range_logodds <- logit(range_prop)
47+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_logodds = range_logodds)
48+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_logodds = spcov_initial$is_known[["range"]])
49+
} else {
50+
range_log <- log(range)
51+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
52+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
53+
}
4754

4855
# anisotropy parameters
4956
## rotate (between 0 and pi radians)
@@ -124,9 +131,20 @@ spcov_orig2optim.matern <- function(spcov_initial, spcov_profiled, data_object,
124131

125132
# range changes based on type
126133
range <- spcov_initial$initial[["range"]]
127-
range_log <- log(range)
128-
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
129-
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
134+
if (data_object$range_constrain) {
135+
range_prop <- range / data_object$range_constrain_value
136+
range_logodds <- logit(range_prop)
137+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_logodds = range_logodds)
138+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_logodds = spcov_initial$is_known[["range"]])
139+
} else {
140+
range_log <- log(range)
141+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
142+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
143+
}
144+
# range <- spcov_initial$initial[["range"]]
145+
# range_log <- log(range)
146+
# spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
147+
# spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
130148

131149
# # extra p log (for now)
132150
# extra <- spcov_initial$initial[["extra"]]
@@ -191,9 +209,20 @@ spcov_orig2optim.cauchy <- function(spcov_initial, spcov_profiled, data_object,
191209

192210
# range changes based on type
193211
range <- spcov_initial$initial[["range"]]
194-
range_log <- log(range)
195-
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
196-
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
212+
if (data_object$range_constrain) {
213+
range_prop <- range / data_object$range_constrain_value
214+
range_logodds <- logit(range_prop)
215+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_logodds = range_logodds)
216+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_logodds = spcov_initial$is_known[["range"]])
217+
} else {
218+
range_log <- log(range)
219+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
220+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
221+
}
222+
# range <- spcov_initial$initial[["range"]]
223+
# range_log <- log(range)
224+
# spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
225+
# spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
197226

198227
# extra p log
199228
extra <- spcov_initial$initial[["extra"]]
@@ -252,9 +281,20 @@ spcov_orig2optim.pexponential <- function(spcov_initial, spcov_profiled, data_ob
252281

253282
# range changes based on type
254283
range <- spcov_initial$initial[["range"]]
255-
range_log <- log(range)
256-
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
257-
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
284+
if (data_object$range_constrain) {
285+
range_prop <- range / data_object$range_constrain_value
286+
range_logodds <- logit(range_prop)
287+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_logodds = range_logodds)
288+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_logodds = spcov_initial$is_known[["range"]])
289+
} else {
290+
range_log <- log(range)
291+
spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
292+
spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
293+
}
294+
# range <- spcov_initial$initial[["range"]]
295+
# range_log <- log(range)
296+
# spcov_orig2optim_val <- c(spcov_orig2optim_val, range_log = range_log)
297+
# spcov_orig2optim_is_known <- c(spcov_orig2optim_is_known, range_log = spcov_initial$is_known[["range"]])
258298

259299
# extra p logodds (for now)
260300
extra <- spcov_initial$initial[["extra"]]

R/spglm.R

+14-2
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,14 @@
121121
#' If \code{local} is \code{TRUE}, defaults for \code{local} are chosen such
122122
#' that \code{local} is transformed into
123123
#' \code{list(size = 100, method = "kmeans", var_adjust = "theoretical", parallel = FALSE)}.
124+
#' @param range_constrain An optional logical that indicates whether the range
125+
#' should be constrained to enhance numerical stability. If \code{range_constrain = TRUE},
126+
#' the maximum possible range value is 5 times the maximum distance in the domain.
127+
#' If \code{range_constrain = FALSE}, then maximum possible range is unbounded.
128+
#' The default is \code{FALSE}.
129+
#' Note that if \code{range_constrain = TRUE} and the value of \code{range} in \code{spcov_initial}
130+
#' is larger than \code{range_constrain}, then \code{range_constrain} is set to
131+
#' \code{FALSE}.
124132
#' @param ... Other arguments to [esv()] or [stats::optim()].
125133
#'
126134
#' @details The spatial generalized linear model for point-referenced data
@@ -299,7 +307,7 @@
299307
#' McCullagh P. and Nelder, J. A. (1989) \emph{Generalized Linear Models}. London: Chapman and Hall.
300308
spglm <- function(formula, family, data, spcov_type, xcoord, ycoord, spcov_initial,
301309
dispersion_initial, estmethod = "reml", anisotropy = FALSE,
302-
random, randcov_initial, partition_factor, local, ...) {
310+
random, randcov_initial, partition_factor, local, range_constrain, ...) {
303311

304312
# set exponential as default if nothing specified
305313
if (missing(spcov_type) && missing(spcov_initial)) {
@@ -387,6 +395,10 @@ spglm <- function(formula, family, data, spcov_type, xcoord, ycoord, spcov_initi
387395
local <- NULL
388396
}
389397

398+
if (missing(range_constrain)) {
399+
range_constrain <- TRUE
400+
}
401+
390402
# non standard evaluation for x and y coordinates
391403
xcoord <- substitute(xcoord)
392404
ycoord <- substitute(ycoord)
@@ -395,7 +407,7 @@ spglm <- function(formula, family, data, spcov_type, xcoord, ycoord, spcov_initi
395407
data_object <- get_data_object_spglm(
396408
formula, family, data, spcov_initial, xcoord, ycoord,
397409
estmethod, anisotropy, random, randcov_initial,
398-
partition_factor, local, ...
410+
partition_factor, local, range_constrain, ...
399411
)
400412

401413
# parallel cluster if necessary

0 commit comments

Comments
 (0)