-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathweighted_quantities.R
144 lines (119 loc) · 3.58 KB
/
weighted_quantities.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
##' Weighted quantities
##'
##' Weighted version of common quantities of interest.
##'
##' @param x Numeric vector to calculate quantity from.
##' @param weights Vector of weights corresponding to values in x.
##' @param probs Vector of probabilities for quantiles.
##' @param type Character vector specifying type of quantiles (either
##' "7" for Type 7 (default) or "hd" for Harrell-Davis)
##' @param ... Currently unused.
##' @return Named vector of calculated quantity.
##' @name weighted_quantities
##' @keywords internal
NULL
median_weighted <- function(x, weights, ...) {
weighted_median <- matrixStats::weightedMedian(
x = x,
w = weights
)
return(c(median = weighted_median))
}
mad_weighted <- function(x, weights, ...) {
weighted_mad <- matrixStats::weightedMad(
x = x,
w = weights
)
return(c(mad = weighted_mad))
}
var_weighted <- function(x, weights, ...) {
if (is.null(weights)) {
var <- var(x)
} else {
var <- as.numeric(stats::cov.wt(cbind(as.numeric(x)), weights)$cov)
}
return(c(var = var))
}
sd_weighted <- function(x, weights, ...) {
if (is.null(weights)) {
sd <- sd(x)
} else {
sd <- as.numeric(sqrt(var_weighted(x, weights)))
}
return(c(sd = sd))
}
mean_weighted <- function(x, weights, ...) {
weighted_mean <- matrixStats::weightedMean(
x = x,
w = weights
)
return(c(mean = weighted_mean))
}
##' Weighted summary measures
##'
##' Returns weighted versions of
##' `posterior::default_summary_measures()` to be used with
##' `posterior::summarise_draws()`.
##' @param x draws object to extract weights from
##' @return Vector of formulas for use with `posterior::summarise_draws()`
##' @keywords internal
weighted_summary_measures <- function(x) {
funcs <- c(
stats::as.formula(paste0("~mean_weighted(.x, weights(", x, "))")),
stats::as.formula(paste0("~median_weighted(.x, weights(", x, "))")),
stats::as.formula(paste0("~sd_weighted(.x, weights(", x, "))")),
stats::as.formula(paste0("~mad_weighted(.x, weights(", x, "))")),
stats::as.formula(paste0("~quantile_weighted(.x, weights(", x, "))"))
)
return(funcs)
}
## Following is adapted from Andrey Akinshin (2023) "Weighted quantile estimators" arXiv:2304.07265 [stat.ME]
quantile_weighted <- function(x, weights, probs = c(0.05, 0.95),
type = "7", ...) {
if (type == "7") {
# Weighted Type 7 quantile estimator
cdf_fun <- function(n, p) {
return(function(cdf_probs) {
h <- p * (n - 1) + 1
u <- pmax((h - 1) / n, pmin(h / n, cdf_probs))
u * n - h + 1
})
}
} else if (type == "hd") {
# Weighted Harrell-Davis quantile estimator
cdf_fun <- function(n, p) {
return(function(cdf_probs) {
stats::pbeta(cdf_probs, (n + 1) * p, (n + 1) * (1 - p))
})
}
}
quants <- .quantile_weighted(
x = x,
weights = weights,
probs = probs,
cdf_fun = cdf_fun
)
names(quants) <- paste0("q", probs * 100)
return(quants)
}
.quantile_weighted <- function(x, probs, cdf_fun, weights) {
# Weighted generic quantile estimator
n <- length(x)
if (is.null(weights))
weights <- rep(1 / n, n)
nw <- sum(weights)^2 / sum(weights^2) # Kish's effective sample size
idx <- order(x)
x <- x[idx]
weights <- weights[idx]
weights <- weights / sum(weights)
cdf_probs <- cumsum(c(0, weights))
sapply(probs, function(p) {
cdf <- cdf_fun(nw, p)
q <- cdf(cdf_probs)
w <- utils::tail(q, -1) - utils::head(q, -1)
sum(w * x)
})
}
quantile2_weighted <- quantile_weighted
# always use quantile2 internally
quantile <- posterior::quantile2