Spaces:
Running
Running
Update app.R
Browse files
app.R
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
#
|
2 |
# ============================================================
|
3 |
# app.R | Shiny App for Rerandomization with fastrerandomize
|
4 |
# ============================================================
|
@@ -25,248 +25,6 @@ library(parallel) # For detecting CPU cores
|
|
25 |
# install.packages("devtools")
|
26 |
# devtools::install_github("cjerzak/fastrerandomize-software/fastrerandomize")
|
27 |
|
28 |
-
# ---------------------------------------------------------
|
29 |
-
# HELPER FUNCTIONS (BASE R)
|
30 |
-
# ---------------------------------------------------------
|
31 |
-
|
32 |
-
# 1) Compute Hotelling's T^2 in base R
|
33 |
-
baseR_hotellingT2 <- function(X, W) {
|
34 |
-
# For a single assignment W:
|
35 |
-
# T^2 = (n0 * n1 / (n0 + n1)) * (xbar1 - xbar0)^T * S_inv * (xbar1 - xbar0)
|
36 |
-
n <- length(W)
|
37 |
-
n1 <- sum(W)
|
38 |
-
n0 <- n - n1
|
39 |
-
if (n1 == 0 || n0 == 0) return(NA_real_) # invalid scenario
|
40 |
-
xbar_treat <- colMeans(X[W == 1, , drop = FALSE])
|
41 |
-
xbar_control <- colMeans(X[W == 0, , drop = FALSE])
|
42 |
-
diff_vec <- (xbar_treat - xbar_control)
|
43 |
-
|
44 |
-
# covariance (pooled) – we just use cov(X)
|
45 |
-
S <- cov(X)
|
46 |
-
Sinv <- tryCatch(solve(S), error = function(e) NULL)
|
47 |
-
if (is.null(Sinv)) {
|
48 |
-
# fallback: diagonal approximation if solve fails
|
49 |
-
Sinv <- diag(1 / diag(S), ncol(S))
|
50 |
-
}
|
51 |
-
|
52 |
-
out <- (n0 * n1 / (n0 + n1)) * c(t(diff_vec) %*% Sinv %*% diff_vec)
|
53 |
-
out
|
54 |
-
}
|
55 |
-
|
56 |
-
# 2) Generate randomizations in base R, filtering by acceptance probability
|
57 |
-
# using T^2 and keep the best (lowest) fraction.
|
58 |
-
baseR_generate_randomizations <- function(n_units, n_treated, X, accept_prob, random_type,
|
59 |
-
max_draws, batch_size) {
|
60 |
-
|
61 |
-
# For safety, check if exact enumerations will explode:
|
62 |
-
if (random_type == "exact") {
|
63 |
-
n_comb_total <- choose(n_units, n_treated)
|
64 |
-
if (n_comb_total > 1e6) {
|
65 |
-
warning(
|
66 |
-
sprintf("Exact randomization is requested, but that is %s combinations.
|
67 |
-
This may be infeasible in terms of memory/time.
|
68 |
-
Consider Monte Carlo instead.", format(n_comb_total, big.mark=",")),
|
69 |
-
immediate. = TRUE
|
70 |
-
)
|
71 |
-
}
|
72 |
-
}
|
73 |
-
|
74 |
-
if (random_type == "exact") {
|
75 |
-
# -------------- EXACT RANDOMIZATIONS --------------
|
76 |
-
cidx <- combn(n_units, n_treated)
|
77 |
-
# Build assignment matrix
|
78 |
-
n_comb <- ncol(cidx)
|
79 |
-
assignment_mat <- matrix(0, nrow = n_comb, ncol = n_units)
|
80 |
-
for (i in seq_len(n_comb)) {
|
81 |
-
assignment_mat[i, cidx[, i]] <- 1
|
82 |
-
}
|
83 |
-
# Compute T^2 for each row
|
84 |
-
T2vals <- apply(assignment_mat, 1, function(w) baseR_hotellingT2(X, w))
|
85 |
-
# Drop any NA (in pathological cases)
|
86 |
-
keep_idx <- which(!is.na(T2vals))
|
87 |
-
assignment_mat <- assignment_mat[keep_idx, , drop = FALSE]
|
88 |
-
T2vals <- T2vals[keep_idx]
|
89 |
-
|
90 |
-
# acceptance threshold
|
91 |
-
cutoff <- quantile(T2vals, probs = accept_prob)
|
92 |
-
keep_final <- (T2vals < cutoff)
|
93 |
-
assignment_mat_accepted <- assignment_mat[keep_final, , drop = FALSE]
|
94 |
-
T2vals_accepted <- T2vals[keep_final]
|
95 |
-
|
96 |
-
} else {
|
97 |
-
# -------------- MONTE CARLO RANDOMIZATIONS --------------
|
98 |
-
# We'll sample max_draws permutations
|
99 |
-
base_assign <- c(rep(1, n_treated), rep(0, n_units - n_treated))
|
100 |
-
|
101 |
-
# We'll store T^2's in chunks to reduce memory overhead
|
102 |
-
batch_count <- ceiling(max_draws / batch_size)
|
103 |
-
all_assign <- list()
|
104 |
-
all_T2 <- numeric(0)
|
105 |
-
|
106 |
-
cur_draw <- 0
|
107 |
-
for (b in seq_len(batch_count)) {
|
108 |
-
ndraws_here <- min(batch_size, max_draws - cur_draw)
|
109 |
-
cur_draw <- cur_draw + ndraws_here
|
110 |
-
|
111 |
-
# sample permutations
|
112 |
-
perms <- matrix(nrow = ndraws_here, ncol = n_units)
|
113 |
-
for (j in seq_len(ndraws_here)) {
|
114 |
-
perms[j, ] <- sample(base_assign)
|
115 |
-
}
|
116 |
-
# T^2 for each
|
117 |
-
T2vals_batch <- apply(perms, 1, function(w) baseR_hotellingT2(X, w))
|
118 |
-
|
119 |
-
# collect
|
120 |
-
all_assign[[b]] <- perms
|
121 |
-
all_T2 <- c(all_T2, T2vals_batch)
|
122 |
-
}
|
123 |
-
assignment_mat <- do.call(rbind, all_assign)
|
124 |
-
|
125 |
-
# remove any NA
|
126 |
-
keep_idx <- which(!is.na(all_T2))
|
127 |
-
assignment_mat <- assignment_mat[keep_idx, , drop = FALSE]
|
128 |
-
all_T2 <- all_T2[keep_idx]
|
129 |
-
|
130 |
-
# acceptance threshold
|
131 |
-
cutoff <- quantile(all_T2, probs = accept_prob)
|
132 |
-
keep_final <- (all_T2 < cutoff)
|
133 |
-
assignment_mat_accepted <- assignment_mat[keep_final, , drop = FALSE]
|
134 |
-
T2vals_accepted <- all_T2[keep_final]
|
135 |
-
}
|
136 |
-
|
137 |
-
list(randomizations = assignment_mat_accepted, balance = T2vals_accepted)
|
138 |
-
}
|
139 |
-
|
140 |
-
# Helper: compute difference in means quickly
|
141 |
-
diff_in_means <- function(Y, W) {
|
142 |
-
mean(Y[W == 1]) - mean(Y[W == 0])
|
143 |
-
}
|
144 |
-
|
145 |
-
# Helper: for a given tau, relabel outcomes and compute the difference in means for a single permutation
|
146 |
-
compute_diff_at_tau_for_oneW <- function(Wprime, obsY, obsW, tau) {
|
147 |
-
# Y0_under_null = obsY - obsW * tau
|
148 |
-
Y0 <- obsY - obsW * tau
|
149 |
-
# Y1_under_null = Y0 + tau
|
150 |
-
# But in practice, for assignment Wprime, the observed outcome is:
|
151 |
-
# Y'(i) = Y0(i) if Wprime(i) = 0, or Y0(i) + tau if Wprime(i)=1
|
152 |
-
Yprime <- Y0
|
153 |
-
Yprime[Wprime == 1] <- Y0[Wprime == 1] + tau
|
154 |
-
diff_in_means(Yprime, Wprime)
|
155 |
-
}
|
156 |
-
|
157 |
-
# 3a) For base R randomization test: difference in means + optional p-value
|
158 |
-
# *without* fiducial interval
|
159 |
-
# (We will incorporate the FI logic below.)
|
160 |
-
baseR_randomization_test <- function(obsW, obsY, allW, findFI = FALSE, alpha = 0.05) {
|
161 |
-
# Observed diff in means
|
162 |
-
tau_obs <- diff_in_means(obsY, obsW)
|
163 |
-
|
164 |
-
# for each candidate assignment, compute diff in means on obsY
|
165 |
-
diffs <- apply(allW, 1, function(w) diff_in_means(obsY, w))
|
166 |
-
|
167 |
-
# p-value = fraction whose absolute diff >= observed
|
168 |
-
pval <- mean(abs(diffs) >= abs(tau_obs))
|
169 |
-
|
170 |
-
# optionally compute a fiducial interval
|
171 |
-
FI <- NULL
|
172 |
-
if (findFI) {
|
173 |
-
FI <- baseR_find_fiducial_interval(obsW, obsY, allW, tau_obs, alpha = alpha)
|
174 |
-
}
|
175 |
-
|
176 |
-
list(p_value = pval, tau_obs = tau_obs, FI = FI)
|
177 |
-
}
|
178 |
-
|
179 |
-
# 3b) The fiducial interval logic for base R, mirroring the approach in fastrerandomize:
|
180 |
-
# 1) Attempt to find a wide lower and upper bracket via random updates
|
181 |
-
# 2) Then a grid search in [lowerBound-1, upperBound*2] for which tau are accepted.
|
182 |
-
baseR_find_fiducial_interval <- function(obsW, obsY, allW, tau_obs, alpha = 0.05, c_initial = 2,
|
183 |
-
n_search_attempts = 500) {
|
184 |
-
|
185 |
-
# random bracket approach
|
186 |
-
lowerBound_est <- tau_obs - 3*tau_obs
|
187 |
-
upperBound_est <- tau_obs + 3*tau_obs
|
188 |
-
|
189 |
-
z_alpha <- qnorm(1 - alpha)
|
190 |
-
k <- 2 / (z_alpha * (2 * pi)^(-1/2) * exp(-z_alpha^2 / 2))
|
191 |
-
|
192 |
-
# For each iteration, pick one random assignment from allW
|
193 |
-
# then see how the implied difference changes, and update the bracket
|
194 |
-
n_allW <- nrow(allW)
|
195 |
-
for (step_t in seq_len(n_search_attempts)) {
|
196 |
-
# pick random assignment
|
197 |
-
idx <- sample.int(n_allW, 1)
|
198 |
-
Wprime <- allW[idx, ]
|
199 |
-
|
200 |
-
# ~~~~~ update lowerBound ~~~~~
|
201 |
-
# Y0 = obsY - obsW * lowerBound_est
|
202 |
-
# Y'(Wprime) = ...
|
203 |
-
lowerY0 <- obsY - obsW * lowerBound_est
|
204 |
-
Yprime_lower <- lowerY0
|
205 |
-
Yprime_lower[Wprime == 1] <- lowerY0[Wprime == 1] + lowerBound_est
|
206 |
-
|
207 |
-
tau_at_step_lower <- diff_in_means(Yprime_lower, Wprime)
|
208 |
-
|
209 |
-
c_step <- c_initial
|
210 |
-
# difference from obs
|
211 |
-
delta <- tau_obs - tau_at_step_lower
|
212 |
-
|
213 |
-
if (tau_at_step_lower < tau_obs) {
|
214 |
-
# move lowerBound up
|
215 |
-
lowerBound_est <- lowerBound_est + k * delta * (alpha/2) / step_t
|
216 |
-
} else {
|
217 |
-
# move it down
|
218 |
-
lowerBound_est <- lowerBound_est - k * (-delta) * (1 - alpha/2) / step_t
|
219 |
-
}
|
220 |
-
|
221 |
-
# ~~~~~ update upperBound ~~~~~
|
222 |
-
upperY0 <- obsY - obsW * upperBound_est
|
223 |
-
Yprime_upper <- upperY0
|
224 |
-
Yprime_upper[Wprime == 1] <- upperY0[Wprime == 1] + upperBound_est
|
225 |
-
|
226 |
-
tau_at_step_upper <- diff_in_means(Yprime_upper, Wprime)
|
227 |
-
delta2 <- tau_at_step_upper - tau_obs
|
228 |
-
|
229 |
-
if (tau_at_step_upper > tau_obs) {
|
230 |
-
# move upperBound down
|
231 |
-
upperBound_est <- upperBound_est - k * delta2 * (alpha/2) / step_t
|
232 |
-
} else {
|
233 |
-
# move it up
|
234 |
-
upperBound_est <- upperBound_est + k * (-delta2) * (1 - alpha/2) / step_t
|
235 |
-
}
|
236 |
-
}
|
237 |
-
|
238 |
-
# Now we do a grid search from (lowerBound_est - 1) to (upperBound_est * 2)
|
239 |
-
# in e.g. 100 steps, seeing which tau is "accepted".
|
240 |
-
# We'll define "accepted" if the min of:
|
241 |
-
# fraction(tau_obs >= distribution_of(tau_pseudo))
|
242 |
-
# fraction(tau_obs <= distribution_of(tau_pseudo))
|
243 |
-
# is > alpha, i.e. do not reject
|
244 |
-
grid_lower <- lowerBound_est - 1
|
245 |
-
grid_upper <- upperBound_est * 2
|
246 |
-
tau_seq <- seq(grid_lower, grid_upper, length.out = 100)
|
247 |
-
|
248 |
-
accepted <- logical(length(tau_seq))
|
249 |
-
for (i in seq_along(tau_seq)) {
|
250 |
-
tau_pseudo <- tau_seq[i]
|
251 |
-
# for each row in allW, compute the diff in means if the true effect = tau_pseudo
|
252 |
-
# distribution_of(tau_pseudo)
|
253 |
-
diffs_pseudo <- apply(allW, 1, function(wp) compute_diff_at_tau_for_oneW(wp, obsY, obsW, tau_pseudo))
|
254 |
-
# Then see how often diffs_pseudo >= tau_obs (or <= tau_obs)
|
255 |
-
frac_ge <- mean(diffs_pseudo >= tau_obs)
|
256 |
-
frac_le <- mean(diffs_pseudo <= tau_obs)
|
257 |
-
# min(...) is the typical "two-sided" approach
|
258 |
-
accepted[i] <- (min(frac_ge, frac_le) > alpha / 2) # or 0.05 if we want 5% test
|
259 |
-
}
|
260 |
-
|
261 |
-
if (!any(accepted)) {
|
262 |
-
# no values accepted => degenerate?
|
263 |
-
# We'll return the bracket we found, or NA.
|
264 |
-
return(c(NA, NA))
|
265 |
-
}
|
266 |
-
|
267 |
-
c(min(tau_seq[accepted]), max(tau_seq[accepted]))
|
268 |
-
}
|
269 |
-
|
270 |
# ---------------------------------------------------------
|
271 |
# UI Section
|
272 |
# ---------------------------------------------------------
|
@@ -481,7 +239,7 @@ ui <- dashboardPage(
|
|
481 |
numericInput("max_draws", "Max Draws (MC)", value = 1e5, min = 1e3),
|
482 |
numericInput("batch_size", "Batch Size (MC)", value = 1e3, min = 1e2)
|
483 |
),
|
484 |
-
actionButton("generate_btn", "Generate
|
485 |
),
|
486 |
|
487 |
box(width = 8, title = "Summary of Accepted Randomizations",
|
@@ -516,32 +274,49 @@ ui <- dashboardPage(
|
|
516 |
tabName = "randtest",
|
517 |
|
518 |
fluidRow(
|
519 |
-
box(
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
),
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
|
|
|
|
|
|
543 |
),
|
544 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
545 |
box(width = 8, title = "Test Results", status = "info", solidHeader = TRUE,
|
546 |
|
547 |
# First row: p-value and observed effect (fastrerandomize)
|
@@ -671,7 +446,7 @@ server <- function(input, output, session) {
|
|
671 |
# =========== 2) base R generation timing ===========
|
672 |
t0_base <- Sys.time()
|
673 |
out_base <- tryCatch({
|
674 |
-
|
675 |
n_units = nrow(X_data()),
|
676 |
n_treated = input$n_treated,
|
677 |
X = X_data(),
|
@@ -709,7 +484,7 @@ server <- function(input, output, session) {
|
|
709 |
if (is.null(rr) || is.null(rr$balance)) {
|
710 |
valueBox("---", "Min Balance Measure", icon = icon("question"), color = "orange")
|
711 |
} else {
|
712 |
-
minBal <- round(min(rr$balance),
|
713 |
valueBox(minBal, "Min Balance Measure", icon = icon("thumbs-up"), color = "blue")
|
714 |
}
|
715 |
})
|
@@ -742,8 +517,9 @@ server <- function(input, output, session) {
|
|
742 |
df <- data.frame(balance = rr$balance)
|
743 |
ggplot(df, aes(x = balance)) +
|
744 |
geom_histogram(binwidth = diff(range(df$balance))/30, fill = "darkblue", alpha = 0.7) +
|
745 |
-
labs(title = "Distribution of Balance
|
746 |
-
|
|
|
747 |
y = "Frequency") +
|
748 |
theme_minimal(base_size = 14)
|
749 |
})
|
@@ -804,6 +580,24 @@ server <- function(input, output, session) {
|
|
804 |
}
|
805 |
})
|
806 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
807 |
# The randomization test result:
|
808 |
RandTestResult <- reactiveVal(NULL)
|
809 |
RandTestResult_base <- reactiveVal(NULL)
|
@@ -857,7 +651,7 @@ server <- function(input, output, session) {
|
|
857 |
|
858 |
t0_testbase <- Sys.time()
|
859 |
outTestBase <- tryCatch({
|
860 |
-
|
861 |
obsW = obsW,
|
862 |
obsY = obsY,
|
863 |
allW = rr_base$randomizations,
|
@@ -889,9 +683,9 @@ server <- function(input, output, session) {
|
|
889 |
output$tauobs_box <- renderValueBox({
|
890 |
rt <- RandTestResult()
|
891 |
if (is.null(rt)) {
|
892 |
-
valueBox("---", "Observed Effect
|
893 |
} else {
|
894 |
-
valueBox(round(rt$tau_obs, 4), "Observed Effect
|
895 |
}
|
896 |
})
|
897 |
|
@@ -917,19 +711,14 @@ server <- function(input, output, session) {
|
|
917 |
})
|
918 |
|
919 |
# If we have a fiducial interval from fastrerandomize, display it
|
920 |
-
output$fi_text <- renderUI({
|
921 |
-
|
922 |
-
|
923 |
-
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
-
tagList(
|
929 |
-
strong("Fiducial Interval (fastrerandomize, 95%):"),
|
930 |
-
p(sprintf("[%.4f, %.4f]", fi_lower, fi_upper))
|
931 |
-
)
|
932 |
-
})
|
933 |
|
934 |
# If we have a fiducial interval from base R, display it
|
935 |
output$fi_text_baseR <- renderUI({
|
@@ -941,7 +730,7 @@ server <- function(input, output, session) {
|
|
941 |
fi_upper <- round(rt$FI[2], 4)
|
942 |
|
943 |
tagList(
|
944 |
-
strong("Fiducial Interval (
|
945 |
p(sprintf("[%.4f, %.4f]", fi_lower, fi_upper))
|
946 |
)
|
947 |
})
|
@@ -973,3 +762,4 @@ server <- function(input, output, session) {
|
|
973 |
# Run the Application
|
974 |
# ---------------------------------------------------------
|
975 |
shinyApp(ui = ui, server = server)
|
|
|
|
1 |
+
# install.packages("~/Documents/fastrerandomize-software/fastrerandomize",repos = NULL, type = "source",force = F)
|
2 |
# ============================================================
|
3 |
# app.R | Shiny App for Rerandomization with fastrerandomize
|
4 |
# ============================================================
|
|
|
25 |
# install.packages("devtools")
|
26 |
# devtools::install_github("cjerzak/fastrerandomize-software/fastrerandomize")
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
# ---------------------------------------------------------
|
29 |
# UI Section
|
30 |
# ---------------------------------------------------------
|
|
|
239 |
numericInput("max_draws", "Max Draws (MC)", value = 1e5, min = 1e3),
|
240 |
numericInput("batch_size", "Batch Size (MC)", value = 1e3, min = 1e2)
|
241 |
),
|
242 |
+
actionButton("generate_btn", "Generate")
|
243 |
),
|
244 |
|
245 |
box(width = 8, title = "Summary of Accepted Randomizations",
|
|
|
274 |
tabName = "randtest",
|
275 |
|
276 |
fluidRow(
|
277 |
+
box(
|
278 |
+
width = 4, title = "Randomization Test Setup",
|
279 |
+
status = "primary", solidHeader = TRUE,
|
280 |
+
|
281 |
+
# (Existing UI elements for Y already in your code)
|
282 |
+
radioButtons("outcome_source", "Outcome Data (Y):",
|
283 |
+
choices = c("Simulate Y" = "simulate",
|
284 |
+
"Upload CSV" = "uploadY"),
|
285 |
+
selected = "simulate"),
|
286 |
+
|
287 |
+
conditionalPanel(
|
288 |
+
condition = "input.outcome_source == 'simulate'",
|
289 |
+
numericInput("true_tau", "True Effect (simulate)", 1, step = 0.5),
|
290 |
+
numericInput("noise_sd", "Noise SD for Y", 0.5, step = 0.1),
|
291 |
+
actionButton("simulateY_btn", "Simulate Y")
|
292 |
+
),
|
293 |
+
|
294 |
+
conditionalPanel(
|
295 |
+
condition = "input.outcome_source == 'uploadY'",
|
296 |
+
fileInput("file_outcomes", "Choose CSV File with outcome vector Y",
|
297 |
+
accept = c(".csv")),
|
298 |
+
helpText("Single column with length = #units.")
|
299 |
+
),
|
300 |
+
|
301 |
+
br(),
|
302 |
+
actionButton("run_randtest_btn", "Run Test"),
|
303 |
+
checkboxInput("findFI", "Compute Fiducial Interval?", value = TRUE)
|
304 |
),
|
305 |
|
306 |
+
box(
|
307 |
+
width = 8, title = "Preview of Outcomes (Y)",
|
308 |
+
status = "info", solidHeader = TRUE,
|
309 |
+
DTOutput("outcomes_table")
|
310 |
+
)
|
311 |
+
),
|
312 |
+
|
313 |
+
fluidRow(
|
314 |
+
box(
|
315 |
+
width = 4, title = NULL, status = NULL,
|
316 |
+
background = NULL, solidHeader = FALSE, collapsible = FALSE,
|
317 |
+
tags$p("Note: Relative speedups greatest for large number of accepted randomizations.",
|
318 |
+
style = "color:#555; font-size:90%; margin:0;")
|
319 |
+
),
|
320 |
box(width = 8, title = "Test Results", status = "info", solidHeader = TRUE,
|
321 |
|
322 |
# First row: p-value and observed effect (fastrerandomize)
|
|
|
446 |
# =========== 2) base R generation timing ===========
|
447 |
t0_base <- Sys.time()
|
448 |
out_base <- tryCatch({
|
449 |
+
generate_randomizations_R(
|
450 |
n_units = nrow(X_data()),
|
451 |
n_treated = input$n_treated,
|
452 |
X = X_data(),
|
|
|
484 |
if (is.null(rr) || is.null(rr$balance)) {
|
485 |
valueBox("---", "Min Balance Measure", icon = icon("question"), color = "orange")
|
486 |
} else {
|
487 |
+
minBal <- round(min(rr$balance), 3)
|
488 |
valueBox(minBal, "Min Balance Measure", icon = icon("thumbs-up"), color = "blue")
|
489 |
}
|
490 |
})
|
|
|
517 |
df <- data.frame(balance = rr$balance)
|
518 |
ggplot(df, aes(x = balance)) +
|
519 |
geom_histogram(binwidth = diff(range(df$balance))/30, fill = "darkblue", alpha = 0.7) +
|
520 |
+
labs(title = "Distribution of Balance Statistic",
|
521 |
+
subtitle = "Among Accepted Randomizations",
|
522 |
+
x = "Balance (i.e., T^2)",
|
523 |
y = "Frequency") +
|
524 |
theme_minimal(base_size = 14)
|
525 |
})
|
|
|
580 |
}
|
581 |
})
|
582 |
|
583 |
+
# Render a preview of Y
|
584 |
+
output$outcomes_table <- renderDT({
|
585 |
+
req(Y_data()) # Make sure Y_data is not NULL
|
586 |
+
|
587 |
+
# Convert to data frame for DT
|
588 |
+
dfy <- data.frame(Y = Y_data())
|
589 |
+
|
590 |
+
# Optionally round numeric data
|
591 |
+
dfy[] <- lapply(dfy, function(col) {
|
592 |
+
if (is.numeric(col)) signif(col, 3) else col
|
593 |
+
})
|
594 |
+
|
595 |
+
datatable(
|
596 |
+
dfy,
|
597 |
+
options = list(scrollX = TRUE, pageLength = 5)
|
598 |
+
)
|
599 |
+
})
|
600 |
+
|
601 |
# The randomization test result:
|
602 |
RandTestResult <- reactiveVal(NULL)
|
603 |
RandTestResult_base <- reactiveVal(NULL)
|
|
|
651 |
|
652 |
t0_testbase <- Sys.time()
|
653 |
outTestBase <- tryCatch({
|
654 |
+
randomization_test_R(
|
655 |
obsW = obsW,
|
656 |
obsY = obsY,
|
657 |
allW = rr_base$randomizations,
|
|
|
683 |
output$tauobs_box <- renderValueBox({
|
684 |
rt <- RandTestResult()
|
685 |
if (is.null(rt)) {
|
686 |
+
valueBox("---", "Observed Effect", icon = icon("question"), color = "maroon")
|
687 |
} else {
|
688 |
+
valueBox(round(rt$tau_obs, 4), "Observed Effect", icon = icon("bullseye"), color = "maroon")
|
689 |
}
|
690 |
})
|
691 |
|
|
|
711 |
})
|
712 |
|
713 |
# If we have a fiducial interval from fastrerandomize, display it
|
714 |
+
#output$fi_text <- renderUI({
|
715 |
+
# rt <- RandTestResult()
|
716 |
+
# if (is.null(rt) || is.null(rt$FI)) {
|
717 |
+
# return(NULL)
|
718 |
+
# }
|
719 |
+
# fi_lower <- round(rt$FI[1], 4)
|
720 |
+
# fi_upper <- round(rt$FI[2], 4)
|
721 |
+
#})
|
|
|
|
|
|
|
|
|
|
|
722 |
|
723 |
# If we have a fiducial interval from base R, display it
|
724 |
output$fi_text_baseR <- renderUI({
|
|
|
730 |
fi_upper <- round(rt$FI[2], 4)
|
731 |
|
732 |
tagList(
|
733 |
+
strong("Fiducial Interval (95%):"),
|
734 |
p(sprintf("[%.4f, %.4f]", fi_lower, fi_upper))
|
735 |
)
|
736 |
})
|
|
|
762 |
# Run the Application
|
763 |
# ---------------------------------------------------------
|
764 |
shinyApp(ui = ui, server = server)
|
765 |
+
|