Skip to content

Commit 85db294

Browse files
authored
Merge pull request #305 from stan-dev/use-posterior-gpdfit
use posterior::gpdfit and posterior::qgeneralized_pareto()
2 parents df56db4 + 8d83fc2 commit 85db294

5 files changed

Lines changed: 11 additions & 80 deletions

File tree

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Imports:
3737
checkmate,
3838
matrixStats (>= 0.52),
3939
parallel,
40-
posterior (>= 1.5.0),
40+
posterior (>= 1.7.0),
4141
stats
4242
Suggests:
4343
bayesplot (>= 1.7.0),

R/gpdfit.R

Lines changed: 6 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -29,71 +29,10 @@
2929
#' for the generalized Pareto distribution. *Technometrics* **51**, 316-325.
3030
#'
3131
gpdfit <- function(x, wip = TRUE, min_grid_pts = 30, sort_x = TRUE) {
32-
# See section 4 of Zhang and Stephens (2009)
33-
if (sort_x) {
34-
x <- sort.int(x)
35-
}
36-
N <- length(x)
37-
prior <- 3
38-
M <- min_grid_pts + floor(sqrt(N))
39-
jj <- seq_len(M)
40-
xstar <- x[floor(N / 4 + 0.5)] # first quartile of sample
41-
theta <- 1 / x[N] + (1 - sqrt(M / (jj - 0.5))) / prior / xstar
42-
l_theta <- N * lx(theta, x) # profile log-lik
43-
w_theta <- exp(l_theta - matrixStats::logSumExp(l_theta)) # normalize
44-
theta_hat <- sum(theta * w_theta)
45-
k <- mean.default(log1p(-theta_hat * x))
46-
sigma <- -k / theta_hat
47-
48-
if (wip) {
49-
k <- adjust_k_wip(k, n = N)
50-
}
51-
52-
if (is.na(k)) {
53-
k <- Inf
54-
}
55-
56-
nlist(k, sigma)
57-
}
58-
59-
60-
# internal ----------------------------------------------------------------
61-
62-
lx <- function(a,x) {
63-
a <- -a
64-
k <- vapply(a, FUN = function(a_i) mean(log1p(a_i * x)), FUN.VALUE = numeric(1))
65-
log(a / k) - k - 1
66-
}
67-
68-
#' Adjust k based on weakly informative prior, Gaussian centered on 0.5. This
69-
#' will stabilize estimates for very small Monte Carlo sample sizes and low neff
70-
#' cases.
71-
#'
72-
#' @noRd
73-
#' @param k Scalar khat estimate.
74-
#' @param n Integer number of tail samples used to fit GPD.
75-
#' @return Scalar adjusted khat estimate.
76-
#'
77-
adjust_k_wip <- function(k, n) {
78-
a <- 10
79-
n_plus_a <- n + a
80-
k * n / n_plus_a + a * 0.5 / n_plus_a
81-
}
82-
83-
84-
#' Inverse CDF of generalized Pareto distribution
85-
#' (assuming location parameter is 0)
86-
#'
87-
#' @noRd
88-
#' @param p Vector of probabilities.
89-
#' @param k Scalar shape parameter.
90-
#' @param sigma Scalar scale parameter.
91-
#' @return Vector of quantiles.
92-
#'
93-
qgpd <- function(p, k, sigma) {
94-
if (is.nan(sigma) || sigma <= 0) {
95-
return(rep(NaN, length(p)))
96-
}
97-
98-
sigma * expm1(-k * log1p(-p)) / k
32+
posterior::gpdfit(
33+
x = x,
34+
wip = wip,
35+
min_grid_pts = min_grid_pts,
36+
sort_x = sort_x
37+
)
9938
}

R/psis.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,12 @@ psis_smooth_tail <- function(x, cutoff) {
254254
exp_cutoff <- exp(cutoff)
255255

256256
# save time not sorting since x already sorted
257-
fit <- gpdfit(exp(x) - exp_cutoff, sort_x = FALSE)
257+
fit <- posterior::gpdfit(exp(x) - exp_cutoff, sort_x = FALSE)
258258
k <- fit$k
259259
sigma <- fit$sigma
260260
if (is.finite(k)) {
261261
p <- (seq_len(len) - 0.5) / len
262-
qq <- qgpd(p, k, sigma) + exp_cutoff
262+
qq <- posterior::qgeneralized_pareto(p, 0, sigma, k) + exp_cutoff
263263
tail <- log(qq)
264264
} else {
265265
tail <- x

R/psislw.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ psislw <- function(lw, wcp = 0.2, wtrunc = 3/4,
7272
# body and gPd smoothed tail
7373
tail_ord <- order(x_tail)
7474
exp_cutoff <- exp(cutoff)
75-
fit <- gpdfit(exp(x_tail) - exp_cutoff, wip=FALSE, min_grid_pts = 80)
75+
fit <- posterior::gpdfit(exp(x_tail) - exp_cutoff, wip=FALSE, min_grid_pts = 80)
7676
k <- fit$k
7777
sigma <- fit$sigma
7878
prb <- (seq_len(tail_len) - 0.5) / tail_len
79-
qq <- qgpd(prb, k, sigma) + exp_cutoff
79+
qq <- posterior::qgeneralized_pareto(prb, 0, sigma, k) + exp_cutoff
8080
smoothed_tail <- rep.int(0, tail_len)
8181
smoothed_tail[tail_ord] <- log(qq)
8282
x_new <- x

tests/testthat/test_gpdfit.R

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,3 @@ test_that("gpdfit returns correct result", {
1111
expect_snapshot_value(gpdfit_val_wip_default_grid, style = "serialize")
1212
})
1313

14-
test_that("qgpd returns the correct result ", {
15-
probs <- seq(from = 0, to = 1, by = 0.25)
16-
q1 <- qgpd(probs, k = 1, sigma = 1)
17-
expect_equal(q1, c(0, 1 / 3, 1, 3, Inf))
18-
19-
q2 <- qgpd(probs, k = 1, sigma = 0)
20-
expect_true(all(is.nan(q2)))
21-
})

0 commit comments

Comments
 (0)