|
104 | 104 | #' hypotheses, this is `NA`. When `look_back = TRUE`, this may be |
105 | 105 | #' earlier than `decision_at` if a hypothesis crossed its boundary at |
106 | 106 | #' a prior analysis but only became testable at a later analysis, |
| 107 | +#' * `last_rejected_at` - Integer vector indicating the latest analysis |
| 108 | +#' at which each hypothesis's boundary was crossed. For non-rejected |
| 109 | +#' hypotheses, this is `NA`. Comparing `first_rejected_at` and |
| 110 | +#' `last_rejected_at` shows whether the rejection is supported by |
| 111 | +#' data at multiple analyses or only at a single analysis, |
107 | 112 | #' * `rejection_sequence` - Character vector giving the order in which |
108 | 113 | #' hypotheses were rejected across all analyses, |
109 | 114 | #' * `graph` - Updated graph after removing all rejected hypotheses. |
@@ -333,6 +338,7 @@ graph_test_shortcut_gsd <- function(graph, |
333 | 338 | rejected = result$rejected, |
334 | 339 | decision_at = result$decision_at, |
335 | 340 | first_rejected_at = result$first_rejected_at, |
| 341 | + last_rejected_at = result$last_rejected_at, |
336 | 342 | rejection_sequence = result$rejection_sequence, |
337 | 343 | graph = if (any(result$rejected)) { |
338 | 344 | graph_update(graph, result$rejected)$updated_graph |
@@ -467,6 +473,7 @@ gsd_test <- function(graph, p, alpha, info_frac, spending_fn, look_back, |
467 | 473 |
|
468 | 474 | # Process analyses sequentially |
469 | 475 | rejected <- structure(rep(FALSE, num_hyps), names = hyp_names) |
| 476 | + last_rejected_at <- structure(rep(NA_integer_, num_hyps), names = hyp_names) |
470 | 477 | decision_at <- structure(rep(NA_integer_, num_hyps), names = hyp_names) |
471 | 478 | first_rejected_at <- structure(rep(NA_integer_, num_hyps), names = hyp_names) |
472 | 479 | adjusted_p <- structure(rep(NA_real_, num_hyps), names = hyp_names) |
@@ -542,13 +549,19 @@ gsd_test <- function(graph, p, alpha, info_frac, spending_fn, look_back, |
542 | 549 | shortcut_k$details$results[[rej_idx]]$hypotheses[hyp_name] |
543 | 550 | allocated_alpha <- w_at_rejection * alpha |
544 | 551 |
|
545 | | - # Look back: earliest analysis where sequential p <= allocated alpha |
| 552 | + # Look back: earliest and latest analysis where boundary is crossed. |
| 553 | + # Check repeated p-values (not sequential) at each analysis against |
| 554 | + # the allocated alpha — a repeated p <= allocated alpha means the |
| 555 | + # boundary at that specific analysis is crossed. |
546 | 556 | j <- which(hyp_names == hyp_name) |
547 | | - earliest <- which(seq_p_matrix[j, 1:k] <= allocated_alpha)[1] |
| 557 | + crossed <- which(rep_p_matrix[j, 1:k] <= allocated_alpha) |
548 | 558 | first_rejected_at[hyp_name] <- |
549 | | - if (!is.na(earliest)) earliest else k |
| 559 | + if (length(crossed) > 0) crossed[1] else k |
| 560 | + last_rejected_at[hyp_name] <- |
| 561 | + if (length(crossed) > 0) crossed[length(crossed)] else k |
550 | 562 | } else { |
551 | 563 | first_rejected_at[hyp_name] <- k |
| 564 | + last_rejected_at[hyp_name] <- k |
552 | 565 | } |
553 | 566 | } |
554 | 567 |
|
@@ -627,6 +640,7 @@ gsd_test <- function(graph, p, alpha, info_frac, spending_fn, look_back, |
627 | 640 | rejected = rejected, |
628 | 641 | decision_at = decision_at, |
629 | 642 | first_rejected_at = first_rejected_at, |
| 643 | + last_rejected_at = last_rejected_at, |
630 | 644 | rejection_sequence = rejection_sequence, |
631 | 645 | test_values = tv_details |
632 | 646 | ) |
|
0 commit comments