1818# ' containing the total number of observations in each aggregate unit. For
1919# ' example, the column containing the total number of voters. Required for
2020# ' computing weights unless `x` is an [ei_spec()] object.
21+ # ' @param global If `TRUE`, aggregate the bounds across units to produce bounds
22+ # ' on the global estimands.
2123# ' @param ... Not currently used, but required for extensibility.
2224# '
2325# ' @returns A data frame with bounds. The `.row` column in the output
24- # ' corresponds to the observation index in the input. The `wt` column contains
25- # ' the product of the predictor variable and total for each observation .
26- # ' Taking a weighted average of the bounds against this column will produce
27- # ' global bounds. The `min` and `max` columns contain the minimum and maximum
28- # ' values for each local estimand . It has class `ei_bounds`.
26+ # ' corresponds to the observation index in the input. The `min` and `max`
27+ # ' columns contain the minimum and maximum values for each local estimand .
28+ # ' The `wt` column contains the product of the predictor variable and total
29+ # ' for each observation. Taking a weighted average of the bounds against this
30+ # ' column will produce global bounds . It has class `ei_bounds`.
2931# '
3032# ' @examples
3133# ' data(elec_1968)
3234# '
3335# ' spec = ei_spec(elec_1968, vap_white:vap_other, pres_dem_hum:pres_abs,
3436# ' total = pres_total, covariates = c(state, pop_urban, farm))
3537# '
36- # ' bounds = ei_bounds(spec, bounds = c(0, 1))
37- # ' print( bounds)
38+ # ' ei_bounds(spec, bounds = c(0, 1))
39+ # ' ei_bounds(spec, bounds = c(0, 1), global = TRUE )
3840# '
39- # ' # aggregate min/max
41+ # ' # Infer bounds
42+ # ' ei_bounds(pres_ind_wal ~ vap_white, data = elec_1968, total = pres_total, bounds = NULL)
43+ # '
44+ # ' # manually aggregate min/max
4045# ' # easier with dplyr:
4146# ' # summarize(across(min:max, ~ weighted.mean(.x, wt)), .by=c(predictor, outcome))
42- # ' do.call(rbind, lapply(split(bounds, ~ predictor + outcome), function(b) {
47+ # ' grp_units = split(ei_bounds(spec, bounds = c(0, 1)), ~ predictor + outcome)
48+ # ' do.call(rbind, lapply(grp_units, function(b) {
4349# ' tibble::tibble(
4450# ' predictor = b$predictor[1],
4551# ' outcome = b$outcome[1],
4854# ' )
4955# ' }))
5056# '
51- # ' # Infer bounds
52- # ' ei_bounds(pres_ind_wal ~ vap_white, data = elec_1968, total = pres_total, bounds = NULL)
5357# ' @export
54- ei_bounds <- function (x , ... , total , contrast = NULL , bounds = c(0 , 1 )) {
58+ ei_bounds <- function (x , ... , total , contrast = NULL , bounds = c(0 , 1 ), global = FALSE ) {
5559 UseMethod(" ei_bounds" )
5660}
5761
5862# ' @export
5963# ' @rdname ei_bounds
60- ei_bounds.ei_spec <- function (x , total , contrast = NULL , bounds = c(0 , 1 ), ... ) {
64+ ei_bounds.ei_spec <- function (x , total , contrast = NULL , bounds = c(0 , 1 ), global = FALSE , ... ) {
6165 spec = x
6266 validate_ei_spec(spec )
6367
@@ -75,12 +79,12 @@ ei_bounds.ei_spec <- function(x, total, contrast = NULL, bounds = c(0, 1), ...)
7579 total = as.numeric(eval_tidy(enquo(total ), spec ))
7680 }
7781
78- ei_bounds_impl (x_mat , y_mat , total , contrast , bounds )
82+ ei_bounds_bridge (x_mat , y_mat , total , contrast , bounds , global )
7983}
8084
8185# ' @export
8286# ' @rdname ei_bounds
83- ei_bounds.formula <- function (formula , data , total , contrast = NULL , bounds = c(0 , 1 ), ... ) {
87+ ei_bounds.formula <- function (formula , data , total , contrast = NULL , bounds = c(0 , 1 ), global = FALSE , ... ) {
8488 forms = ei_forms(formula )
8589 form_preds = terms(rlang :: new_formula(lhs = NULL , rhs = forms $ predictors ))
8690 form_out = terms(rlang :: new_formula(forms $ outcome , rhs = NULL ))
@@ -103,13 +107,13 @@ ei_bounds.formula <- function(formula, data, total, contrast = NULL, bounds = c(
103107
104108 total = as.numeric(eval_tidy(enquo(total ), data ))
105109
106- ei_bounds_impl (x , y , total , contrast , processed $ blueprint $ bounds )
110+ ei_bounds_bridge (x , y , total , contrast , processed $ blueprint $ bounds , global )
107111}
108112
109113
110114# ' @export
111115# ' @rdname ei_bounds
112- ei_bounds.data.frame <- function (x , y , total , contrast = NULL , bounds = c(0 , 1 ), ... ) {
116+ ei_bounds.data.frame <- function (x , y , total , contrast = NULL , bounds = c(0 , 1 ), global = FALSE , ... ) {
113117 x_mat = as.matrix(x )
114118 check_preds(x_mat , call = rlang :: new_call(rlang :: sym(" ei_bounds" )))
115119 y_mat = as.matrix(y )
@@ -122,13 +126,13 @@ ei_bounds.data.frame <- function(x, y, total, contrast = NULL, bounds = c(0, 1),
122126
123127 bounds = check_bounds(bounds , y_mat )
124128
125- ei_bounds_impl (x_mat , y_mat , total , contrast , bounds )
129+ ei_bounds_bridge (x_mat , y_mat , total , contrast , bounds , global )
126130}
127131
128132# ' @export
129133# ' @rdname ei_bounds
130- ei_bounds.matrix <- function (x , y , total , contrast = NULL , bounds = c(0 , 1 ), ... ) {
131- ei_bounds.data.frame(x , y , total , contrast , bounds , ... )
134+ ei_bounds.matrix <- function (x , y , total , contrast = NULL , bounds = c(0 , 1 ), global = FALSE , ... ) {
135+ ei_bounds.data.frame(x , y , total , contrast , bounds , global , ... )
132136}
133137
134138# ' @export
@@ -142,37 +146,55 @@ ei_bounds.default <- function(x, ...) {
142146
143147# Implementation --------------------------------------------------------------
144148
145- ei_bounds_impl <- function (x , y , total , contrast , bounds ) {
149+ ei_bounds_bridge <- function (x , y , total , contrast , bounds , global = FALSE ) {
146150 n = nrow(x )
147151 n_x = ncol(x )
148152 n_y = ncol(y )
149153
150- if (! is.null(contrast )) {
151- cli_abort(" {.arg contrast} is not yet implemented for {.fn ei_bounds}." )
152- }
153-
154154 if (identical(bounds , c(- Inf , Inf ))) {
155155 cli_abort(" At least one bound must be provided for {.fn ei_bounds}." , call = parent.frame())
156156 }
157157 if (any(is.na(x ))) cli_abort(" Missing values found in predictors." , call = parent.frame())
158158 if (any(is.na(y ))) cli_abort(" Missing values found in outcome." , call = parent.frame())
159159
160- result = R_bounds_lp (x , y , bounds )
160+ result = ei_bounds_impl (x , y , total , contrast , bounds )
161161
162162 x_nm = colnames(x )
163163 y_nm = colnames(y )
164164
165- tibble :: new_tibble(
166- list (
167- .row = rep(seq_len(n ), n_x * n_y ),
168- predictor = rep(rep(x_nm , each = n ), n_y ),
169- outcome = rep(y_nm , each = n * n_x ),
170- wt = if (is.null(contrast )) rep(c(x * total ), n_y ) else NULL ,
171- min = c(result $ min ),
172- max = c(result $ max )
173- ),
174- class = " ei_bounds"
175- )
165+ if (isTRUE(global )) {
166+ wt = total * (matrix (1 , 1 , n_y ) %x% x )
167+ wt = scale_cols(wt , 1 / colSums(wt ))
168+ tibble :: new_tibble(
169+ list (
170+ predictor = rep(x_nm , n_y ),
171+ outcome = rep(y_nm , each = n_x ),
172+ min = colSums(result $ min * wt ),
173+ max = colSums(result $ max * wt )
174+ ),
175+ class = " ei_bounds"
176+ )
177+ } else {
178+ tibble :: new_tibble(
179+ list (
180+ .row = rep(seq_len(n ), n_x * n_y ),
181+ predictor = rep(rep(x_nm , each = n ), n_y ),
182+ outcome = rep(y_nm , each = n * n_x ),
183+ wt = if (is.null(contrast )) rep(c(x * total ), n_y ) else NULL ,
184+ min = c(result $ min ),
185+ max = c(result $ max )
186+ ),
187+ class = " ei_bounds"
188+ )
189+ }
190+ }
191+
192+ ei_bounds_impl <- function (x , y , total , contrast , bounds ) {
193+ if (! is.null(contrast )) {
194+ cli_abort(" {.arg contrast} is not yet implemented for {.fn ei_bounds}." )
195+ }
196+
197+ R_bounds_lp(x , y , as.double(bounds ))
176198}
177199
178200# ' @describeIn ei_bounds Format bounds as an array with dimensions
0 commit comments