Helmut
★★★

Vienna, Austria,
2022-06-12 13:37
(234 d 07:29 ago)

Posting: # 23051
Views: 1,379

## ‘Two-at-a-Time’ approach with >3 treat­ments [Design Issues]

Dear all,

the EMA1–3 recommends for higher-order crossovers the ‘Two-at-a-Time’ approach, i.e., to exclude all but two treatments, leading to Incomplete Block Designs.4 That’s fine, since the ‘All at Once’ approach (an ANOVA of all data) may lead to biased estimates and an inflated Type I Error.5,6

So far I had to deal only with three treatments and planned pairwise comparisons $$\small{\text{A}\;vs\;\text{C}}$$ and $$\small{\text{B}\;vs\;\text{C}}$$. For a six-sequence Williams’ design we get$$\small{\begin{array}{c|ccc} s/p & \text{I} & \text{II} & \text{III} \\\hline 1 & \text{A} & \text{B} & \text{C}\\ 2 & \text{A} & \text{C} & \text{B}\\ 3 & \text{B} & \text{A} & \text{C}\\ 4 & \text{B} & \text{C} & \text{A}\\ 5 & \text{C} & \text{A} & \text{B}\\ 6 & \text{C} & \text{B} & \text{A}\\ \end{array}{\color{Blue}\mapsto} \begin{array}{c|ccc} s/p & \text{I} & \text{II} & \text{III} \\\hline 1 & \text{A} & {\color{Red}\bullet} & \text{C}\\ 2 & \text{A} & \text{C} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & \text{A} & \text{C}\\ 4 & {\color{Red}\bullet} & \text{C} & \text{A}\\ 5 & \text{C} & \text{A} & {\color{Red}\bullet}\\ 6 & \text{C} & {\color{Red}\bullet} & \text{A}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|ccc} s/p & \text{I} & \text{II} & \text{III} \\\hline 1 & {\color{Red}\bullet} & \text{B} & \text{C}\\ 2 & {\color{Red}\bullet} & \text{C} & \text{B}\\ 3 & \text{B} & {\color{Red}\bullet} & \text{C}\\ 4 & \text{B} & \text{C} & {\color{Red}\bullet}\\ 5 & \text{C} & {\color{Red}\bullet} & \text{B}\\ 6 & \text{C} & \text{B} & {\color{Red}\bullet}\\ \end{array}}$$ where $$\small{{\color{Red}\bullet}}$$ denotes an excluded treatment. Both IBDs are balanced. Excellent.

What about four treatments in a Williams’ design with pairwise comparisons $$\small{\text{A}\;vs\;\text{D}}$$, $$\small{\text{B}\;vs\;\text{D}}$$, and $$\small{\text{C}\;vs\;\text{D}}$$? $$\small{\begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & \text{A} & \text{B} & \text{C} & \text{D}\\ 2 & \text{B} & \text{D} & \text{A} & \text{C}\\ 3 & \text{C} & \text{A} & \text{B} & \text{D}\\ 4 & \text{D} & \text{C} & \text{B} & \text{A}\\ \end{array}{\color{Blue}\mapsto} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV} \\\hline 1 & \text{A} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{D}\\ 2 & {\color{Red}\bullet} & \text{D} & \text{A} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & \text{A} & {\color{Red}\bullet} & \text{D}\\ 4 & \text{D} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{A}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & {\color{Red}\bullet} & \text{B} & {\color{Red}\bullet} & \text{D}\\ 2 & \text{B} & \text{D} & {\color{Red}\bullet} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{B} & \text{D}\\ 4 & \text{D} & {\color{Red}\bullet} & \text{B} & {\color{Red}\bullet}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{C} & \text{D}\\ 2 & {\color{Red}\bullet} & \text{D} & {\color{Red}\bullet} & \text{C}\\ 3 & \text{C} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{D}\\ 4 & \text{D} & \text{C} & {\color{Red}\bullet} & {\color{Red}\bullet}\\ \end{array}}$$ Now the IBDs are imbalanced and hence, lacking period effects have to be assumed. Nasty.
Solution: Use one of the 24 arrangements given by Senn.6 Here the first as an example:
$$\small{\begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & \text{A} & \text{B} & \text{C} & \text{D}\\ 2 & \text{B} & \text{A} & \text{D} & \text{C}\\ 3 & \text{C} & \text{D} & \text{A} & \text{B}\\ 4 & \text{D} & \text{C} & \text{B} & \text{A}\\ \end{array}{\color{Blue}\mapsto} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV} \\\hline 1 & \text{A} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{D}\\ 2 & {\color{Red}\bullet} & \text{A} & \text{D} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & \text{D} & \text{A} & {\color{Red}\bullet}\\ 4 & \text{D} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{A}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & {\color{Red}\bullet} & \text{B} & {\color{Red}\bullet} & \text{D}\\ 2 & \text{B} & {\color{Red}\bullet} & \text{D} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & \text{D} & {\color{Red}\bullet} & \text{B}\\ 4 & \text{D} & {\color{Red}\bullet} & \text{B} & {\color{Red}\bullet}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{C} & \text{D}\\ 2 & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{D} & \text{C}\\ 3 & \text{C} & \text{D} & {\color{Red}\bullet} & {\color{Red}\bullet}\\ 4 & \text{D} & \text{C} & {\color{Red}\bullet} & {\color{Red}\bullet}\\ \end{array}}$$ All is good. An -script at the end.
BTW, the common Latin Square ($$\small{\text{ABCD}\,|\,\text{BCDA}\,|\,\text{CDAB}\,|\,\text{DABC}}$$) would do as well.

1. EMA, CHMP. Guideline on the Investigation of Bioequivalence. CPMP/EWP/QWP/1401/98 Rev. 1/Corr **. London. 20 January 2010. Online.
2. Brown D. Presentation at the 3rd EGA Symposium on Bioequivalence. London. June 2010. Slides.
3. EGA. Revised EMA Bioequivalence Guideline. Questions & Answers. Online.
4. Cochran WG, Cox GM. Experimental Designs. New York: Wiley; 2nd ed. 1957. Chapter 9, p. 376–95.
5. Schuirmann DJ. Two at a Time? Or All at Once? International Biometric Society, Eastern North American Region, Spring Meeting. Pittsburgh, PA. March 28–31, 2004. Abstract.
6. D’Angelo P. Testing for Bioequivalence in Higher‐Order Crossover Designs: Two‐at‐a‐Time Principle Versus Pooled ANOVA. 2nd Conference of the Global Bioequivalence Harmonisation Initiative. Rockville, MD. 15–16 Sep­tem­ber, 2016.
7. Senn S. Cross-over Trials in Clinical Research. Chichester: Wiley; 2nd ed. 2002. Table 5.1. p. 163.

library(PowerTOST) library(randomizeBE) # sample size for evaluation by ‘Two at a Time’ # note that the design has to specified as "2x2" n    <- sampleN.TOST(CV = 0.20, theta0 = 0.95, targetpower = 0.8,                      design = "2x2", print = FALSE)[["Sample size"]] # specify the sequences and get the randomization list seqs <- c("ABCD", "BADC", "CDAB", "DCBA") rl   <- RL4(nsubj = n, seqs = seqs)$rl rl <- cbind(rl, IBD.1 = NA, IBD.2 = NA, IBD.3 = NA) # extract pairwise comparisons (A vs D, B vs D, C vs D) and build the IBDs for (j in 1:nrow(rl)) { rl$IBD.1[j] <- gsub("[B, C]", "•", rl$sequence[j]) # keep A and D rl$IBD.2[j] <- gsub("[C, A]", "•", rl$sequence[j]) # keep B and D rl$IBD.3[j] <- gsub("[A, B]", "•", rl$sequence[j]) # keep C and D } rl <- rl[, -2] # remove seqno # check IBDs for balance c.1 <- c.2 <- c.3 <- NA for (j in 1:4) { c.1[j] <- sum(substr(rl$IBD.1, j, j) == "A") == sum(substr(rl$IBD.1, j, j) == "D") c.2[j] <- sum(substr(rl$IBD.2, j, j) == "B") == sum(substr(rl$IBD.2, j, j) == "D") c.3[j] <- sum(substr(rl$IBD.3, j, j) == "C") == sum(substr(rl$IBD.3, j, j) == "D") } chk <- setNames(c(sum(c.1), sum(c.2), sum(c.3)), c("IBD.1", "IBD.2", "IBD.3")) txt <- paste("Specified sequences:", paste(seqs, collapse = ", ")) if (length(unique(chk)) == 1) { txt <- paste(txt, "\nPassed check : All IBDs are balanced.\n") } else { txt <- paste(txt, "\nFailed check : IBDs are not balanced:", "Examine the specified sequences.\n") } print(rl, row.names = FALSE) cat(txt) Gives  subject sequence IBD.1 IBD.2 IBD.3 1 DCBA D••A D•B• DC•• 2 CDAB •DA• •D•B CD•• 3 CDAB •DA• •D•B CD•• 4 BADC •AD• B•D• ••DC 5 BADC •AD• B•D• ••DC 6 ABCD A••D •B•D ••CD 7 DCBA D••A D•B• DC•• 8 ABCD A••D •B•D ••CD 9 CDAB •DA• •D•B CD•• 10 BADC •AD• B•D• ••DC 11 BADC •AD• B•D• ••DC 12 ABCD A••D •B•D ••CD 13 DCBA D••A D•B• DC•• 14 DCBA D••A D•B• DC•• 15 CDAB •DA• •D•B CD•• 16 ABCD A••D •B•D ••CD 17 DCBA D••A D•B• DC•• 18 CDAB •DA• •D•B CD•• 19 ABCD A••D •B•D ••CD 20 BADC •AD• B•D• ••DC Specified sequences: ABCD, BADC, CDAB, DCBA Passed check : All IBDs are balanced. Dif-tor heh smusma 🖖🏼 Довге життя Україна! Helmut Schütz The quality of responses received is directly proportional to the quality of the question asked. 🚮 Science Quotes Shuanghe ★★ Spain, 2022-06-12 21:02 (234 d 00:04 ago) @ Helmut Posting: # 23053 Views: 1,104 ## ‘Two-at-a-Time’ approach with >3 treat­ments Dear Helmut, I vaguelly recall that for 4x4 William design, each row (sequence) and each column (period) should contain A, B, C, and D each exactly once. I think that there's a typo in the 3rd sequence of your first William design. Your 3rd and 4th column do not seem right. So 3rd period would have 2 Bs and 4th had 2 Ds. CABD should be CADB. So It would be ABCD/BDAC/CADB/DCBA. In such case, it would be balanced for pair-wise comparisons. $$\small{\begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & \text{A} & \text{B} & \text{C} & \text{D}\\ 2 & \text{B} & \text{D} & \text{A} & \text{C}\\ 3 & \text{C} & \text{A} & \text{D} & \text{B}\\ 4 & \text{D} & \text{C} & \text{B} & \text{A}\\ \end{array}{\color{Blue}\mapsto} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV} \\\hline 1 & \text{A} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{D}\\ 2 & {\color{Red}\bullet} & \text{D} & \text{A} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & \text{A} & \text{D} & {\color{Red}\bullet}\\ 4 & \text{D} & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{A}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & {\color{Red}\bullet} & \text{B} & {\color{Red}\bullet} & \text{D}\\ 2 & \text{B} & \text{D} & {\color{Red}\bullet} & {\color{Red}\bullet}\\ 3 & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{D} & \text{B}\\ 4 & \text{D} & {\color{Red}\bullet} & \text{B} & {\color{Red}\bullet}\\ \end{array}{\color{Blue}\wedge} \begin{array}{c|cccc} s/p & \text{I} & \text{II} & \text{III} & \text{IV}\\\hline 1 & {\color{Red}\bullet} & {\color{Red}\bullet} & \text{C} & \text{D}\\ 2 & {\color{Red}\bullet} & \text{D} & {\color{Red}\bullet} & \text{C}\\ 3 & \text{C} & {\color{Red}\bullet} & \text{D} & {\color{Red}\bullet}\\ 4 & \text{D} & \text{C} & {\color{Red}\bullet} & {\color{Red}\bullet}\\ \end{array}}$$ All the best, Shuanghe Helmut ★★★ Vienna, Austria, 2022-06-12 22:10 (233 d 22:55 ago) @ Shuanghe Posting: # 23054 Views: 1,002 ## ‘Two-at-a-Time’ approach with >3 treat­ments Hi Shuanghe, long time, no hear! ❝ I vaguelly recall that for 4x4 William design, each row (sequence) and each column (period) should contain A, B, C, and D each exactly once. Absolutely correct. ❝ I think that there's a typo in the 3rd sequence of your first William design. F**k & THX! You are right. ❝ Your 3rd and 4th column do not seem right. So 3rd period would have 2 Bs and 4th had 2 Ds. CABD should be CADB. So It would be ABCD/BDAC/CADB/DCBA. Yep. An updated -script which works for any number of treatments (tested for ≤ 8) at the end. Of course, more than five treatments in a higher-order crossover are not realistic due to the number of periods and likely a Balanced In­com­plete Block Design will be used instead. Not implemented yet. An example for the maximum of treatments I ever came across:  subject seqno sequence IBD.1 IBD.2 IBD.3 IBD.4 IBD.5 1 4 DAFCEB •AF••• ••F••B ••FC•• D•F••• ••F•E• 2 6 FDEABC F••A•• F•••B• F••••C FD•••• F•E••• 3 6 FDEABC F••A•• F•••B• F••••C FD•••• F•E••• 4 3 CBAEDF ••A••F •B•••F C••••F ••••DF •••E•F 5 2 BECFAD •••FA• B••F•• ••CF•• •••F•D •E•F•• 6 5 EFBDCA •F•••A •FB••• •F••C• •F•D•• EF•••• 7 1 ACDBFE A•••F• •••BF• •C••F• ••D•F• ••••FE 8 1 ACDBFE A•••F• •••BF• •C••F• ••D•F• ••••FE 9 2 BECFAD •••FA• B••F•• ••CF•• •••F•D •E•F•• 10 5 EFBDCA •F•••A •FB••• •F••C• •F•D•• EF•••• 11 3 CBAEDF ••A••F •B•••F C••••F ••••DF •••E•F 12 4 DAFCEB •AF••• ••F••B ••FC•• D•F••• ••F•E• 13 2 BECFAD •••FA• B••F•• ••CF•• •••F•D •E•F•• 14 6 FDEABC F••A•• F•••B• F••••C FD•••• F•E••• 15 4 DAFCEB •AF••• ••F••B ••FC•• D•F••• ••F•E• 16 1 ACDBFE A•••F• •••BF• •C••F• ••D•F• ••••FE 17 5 EFBDCA •F•••A •FB••• •F••C• •F•D•• EF•••• 18 4 DAFCEB •AF••• ••F••B ••FC•• D•F••• ••F•E• 19 1 ACDBFE A•••F• •••BF• •C••F• ••D•F• ••••FE 20 3 CBAEDF ••A••F •B•••F C••••F ••••DF •••E•F Sequences: ACDBFE, BECFAD, CBAEDF, DAFCEB, EFBDCA, FDEABC All extracted IBDs are balanced. Sometimes sequences are imbalanced. In this example we have four subjects in sequences DAFCEB and FDEABC and three subjects in each of the other four sequences. However, that’s not relevant because we aim at balance of the IBDs. If you want balance, specify bal <- TRUE. In the example it would require 24 subjects instead of 20. Although there is just one Williams’ design for three treatments, there are six possible for four treatments, twelve for five, 120 for six, and 360 for seven. The function williams() of randomizeBE will arbitrarily select one of them. If you run the script, the chance to get the same sequences like in my example is 1/120. Edit: Don’t use this script. Use the one at the end of this post instead. library(PowerTOST) library(randomizeBE) ntmt <- 6 # number of treatments (at least 3) CV <- 0.20 # assumed CV theta0 <- 0.95 # assumed T/R-ratio target <- 0.80 # target (desired) power bal <- FALSE # TRUE if the study should have balanced sequences make.equal <- function(n, nseq) { return(as.integer(nseq * (n %/% nseq + as.logical(n %% nseq)))) } # sample size for evaluation by the ‘Two at a Time’ approach # note that the design is specified as "2x2" n <- sampleN.TOST(CV = CV, theta0 = theta0, targetpower = target, design = "2x2", print = FALSE)[["Sample size"]] # sequences of a Williams’ design seqs <- williams(ntmt) # alternatively specify your own vector: # treatments must have only one character and the # reference must have the highest lexical order # a Latin Square with four treatments will work # seqs <- c("ABCR", "BCRA", "CRAB", "RABC") # but this one will not # seqs <- c("T1T2T3R", "T2T3RT1", "T3RT1T2", "RT1T2T3") # eventual uprounding if balance is requested if (bal) n <- make.equal(n, length(seqs)) s <- paste0(seqs, collapse = "") # the last treatment in lexical order will act as the reference s <- paste(sort(unlist(strsplit(s, ""))), collapse = "") ref <- substr(s, nchar(s), nchar(s)) # extract treatment codes trts <- unique(c(strsplit(split = "", seqs), recursive = TRUE)) # extract tests in increasing order tests <- sort(trts[!trts == ref]) # get the randomization list rand <- suppressWarnings( # not relevant for TaT RL4(nsubj = n, seqs = seqs)$rl) # generate columns for the IBDs for (j in seq_along(tests)) {   rand[[paste0("IBD.", j)]] <- NA } # build the IBDs for (j in 1:nrow(rand)) {   for (k in seq_along(tests)) {     excl           <- tests[!tests == tests[k]]     excl           <- paste0("[", paste(excl, collapse = ", "), "]")     rand[j, 3 + k] <- gsub(excl, "•", rand$sequence[j]) } } # check IBDs for balance checks <- rep(NA, length(tests)) for (j in seq_along(tests)) { checks[j] <- sum(lengths(regmatches(rand[3 + j], gregexpr(tests[j], rand[3 + j])))) == sum(lengths(regmatches(rand[3 + j], gregexpr(ref, rand[3 + j])))) } txt <- paste("Sequences:", paste(seqs, collapse = ", ")) if (length(unique(checks)) == 1) { # all are TRUE txt <- paste(txt, "\nAll extracted IBDs are balanced.\n") } else { # at least one of the IBDs is imbalanced txt <- paste(txt, "\nExtracted IBDs are imbalanced;", "check the sequences.\n") } print(rand, row.names = FALSE); cat(txt) Dif-tor heh smusma 🖖🏼 Довге життя Україна! Helmut Schütz The quality of responses received is directly proportional to the quality of the question asked. 🚮 Science Quotes Shuanghe ★★ Spain, 2022-07-10 02:05 (206 d 19:01 ago) @ Helmut Posting: # 23123 Views: 809 ## ‘Two-at-a-Time’ approach with >3 treat­ments Hi Helmut, ❝ long time, no hear! Indeed. COVID, change of company and workload, study, ..., Pretty busy these days. Had to work day and night recently to catch everything up, so I had much less time to read the forum than before... But I'll be in Amsterdam this September for the 5th global BE harmonisation conference, so hopefully we'll have time to have a drink together By the way, the Article section is new, am I right? (Don't tell me I missed this section all those years...) I am supprised that no one mentioned it in the forum. I don't have time to read the lengthy ones yet but I really enjoy the reading among the few short ones that I've picked. ❝ All extracted IBDs are balanced. What I understood about balanced IBD here is that, e.g., the number for A vs. F comparison should be same as number of F vs. A. So there should be 10 for each comparison for 20 in total. If that is a correct understanding, it seems that for B, C and D, yes, but for A and E, not... There're 11 AF (FE) but 9 FA (EF). Shouldn't it be 10 and 10 like B/C/D vs F? ❝ If you run the script, the chance to get the same sequences like in my example is 1/120. I tried the code and obviously I cannot reproduce your result as you said, but among the few runs I had, there is a run with 8 EF and 12 FE, 11 FA (BF) and 9 AF(FB)... All the best, Shuanghe Helmut ★★★ Vienna, Austria, 2022-07-10 18:02 (206 d 03:04 ago) @ Shuanghe Posting: # 23126 Views: 879 ## ‘Two-at-a-Time’ approach; ≥3 treat­ments: new 🇷 script Hi Shuanghe, ❝ I'll be in Amsterdam this September for the 5th global BE harmonisation conference, so hopefully we'll have time to have a drink together Great! BioBridges (22/23 September) as well? ❝ By the way, the Article section is new, am I right? (Don't tell me I missed this section all those years...) <1½ years. ❝ I am supprised that no one mentioned it in the forum. Well, I do. Actually I was bored of answering similar questions over and over again. Now I reply only with: , for details see article XYZ.” ❝ I don't have time to read the lengthy ones yet but I really enjoy the reading among the few short ones that I've picked. THX! ❝ What I understood about balanced IBD here is that, e.g., the number for A vs. F comparison should be same as number of F vs. A. So there should be 10 for each comparison for 20 in total. ❝ If that is a correct understanding, … Of course, it is. ❝ … it seems that for B, C and D, yes, but for A and E, not... There're 11 AF (FE) but 9 FA (EF). Shouldn't it be 10 and 10 like B/C/D vs F? THX for discovering such a nasty bug! If debugging is the process of removing bugs, then programming must be the process of putting them in. Edsger W. Dijkstra New (lengthy) script at the end. More flexible (theta0, theta1, theta2, target, do.rate, and a vector of references ref can be specified). Call the function with e.g., make.ibds(ntmt = 6, CV = 0.2):  subject seqno sequence IBD 1 IBD 2 IBD 3 IBD 4 IBD 5 1 5 ECFABD ••FA•• ••F•B• •CF••• ••F••D E•F••• 2 6 FEBCDA F••••A F•B••• F••C•• F•••D• FE•••• 3 5 ECFABD ••FA•• ••F•B• •CF••• ••F••D E•F••• 4 6 FEBCDA F••••A F•B••• F••C•• F•••D• FE•••• 5 3 CAEDFB •A••F• ••••FB C•••F• •••DF• ••E•F• 6 1 ADCBEF A••••F •••B•F ••C••F •D•••F ••••EF 7 2 BFDEAC •F••A• BF•••• •F•••C •FD••• •F•E•• 8 4 DBAFCE ••AF•• •B•F•• •••FC• D••F•• •••F•E 9 3 CAEDFB •A••F• ••••FB C•••F• •••DF• ••E•F• 10 2 BFDEAC •F••A• BF•••• •F•••C •FD••• •F•E•• 11 1 ADCBEF A••••F •••B•F ••C••F •D•••F ••••EF 12 4 DBAFCE ••AF•• •B•F•• •••FC• D••F•• •••F•E 13 4 DBAFCE ••AF•• •B•F•• •••FC• D••F•• •••F•E 14 3 CAEDFB •A••F• ••••FB C•••F• •••DF• ••E•F• 15 1 ADCBEF A••••F •••B•F ••C••F •D•••F ••••EF 16 2 BFDEAC •F••A• BF•••• •F•••C •FD••• •F•E•• 17 5 ECFABD ••FA•• ••F•B• •CF••• ••F••D E•F••• 18 2 BFDEAC •F••A• BF•••• •F•••C •FD••• •F•E•• 19 6 FEBCDA F••••A F•B••• F••C•• F•••D• FE•••• 20 3 CAEDFB •A••F• ••••FB C•••F• •••DF• ••E•F• Reference : F Tests : A, B, C, D, E Sequences : ADCBEF, BFDEAC, CAEDFB, DBAFCE, ECFABD, FEBCDA Subjects per sequence : 3 | 4 | 4 | 3 | 3 | 3 (imbalanced) Estimated sample size : 20 Achieved power : 0.8347 Randomized : 2022-07-13 22:55:28 CEST Seed : 820697 Runtime : 30 ms (1 call for balance of IBDs) Since we are working in a regulated environment, perhaps you will be asked by an inspector how you performed the randomization. You need to provide the sample size, all sequences and the seed to reproduce it. Try: library(randomizeBE) RL4(n = 20, seqs = c("ADCBEF", "BFDEAC", "CAEDFB", "DBAFCE", "ECFABD", "FEBCDA"), seed = 820697) Note that in this case you get at the end: Warning message: Unbalanced design! # of subj. in sequences ADCBEF BFDEAC CAEDFB DBAFCE ECFABD FEBCDA: 3 4 4 3 3 3 Is that important? Although we are interested in balanced IBDs – which they are – they are not balanced for period effects. Let’s check how often the treatments are administered in each period:  p1 p2 p3 p4 p5 p6 A 3 4 3 3 4 3 B 4 3 3 3 3 4 C 4 3 3 3 3 4 D 3 3 4 4 3 3 E 3 3 4 4 3 3 F 3 4 3 3 4 3 If you want period-balance (which is a desirable property), use bal = TRUE in the call: tmp <- make.ibds(ntmt = 6, CV = 0.2, print = FALSE, bal = TRUE) cat(tmp$txt, "\n") Reference                : F Tests                    : A, B, C, D, E Sequences                : AEBDFC, BAFECD, CFDBEA, DCEFAB, EDACBF, FBCADE Subjects per sequence    : 4 | 4 | 4 | 4 | 4 | 4 (balanced) Estimated sample size    : 20 Achieved power           : 0.8347 Adjustment to obtain period-balance  Adjusted sample size    : 24  Achieved power          : 0.8960 Randomized               : 2022-07-15 11:13:50 CEST Seed                     : 9503537 Runtime                  : 20 ms (1 call for balance of IBDs)

There is no free lunch; we need 24 subjects instead of 20.

If you are interested in the performance:

calls <- et <- integer(1000) for (j in seq_along(calls)) {   tmp      <- make.ibds(ntmt = 6, CV = 0.2, print = FALSE)   calls[j] <- as.integer(tmp$calls) et[j] <- as.integer(tmp$et) } probs <- c(0, 0.025, 0.25, 0.5, 0.75, 0.975, 1) perf  <- matrix(data = c(quantile(calls, probs = probs),                          round(quantile(et, probs = probs))),                          nrow = 2, ncol = length(probs), byrow = TRUE,                 dimnames = list(c("calls", "runtimes (ms)"),                                 paste0(100 * probs, "%"))) print(perf)

On my aged machine:

              0% 2.5% 25% 50% 75% 97.5% 100% calls          1    1   3   7  14    35   59 runtimes (ms)  0   10  30  70 140   360  610

More arguments (NTID, T/R-ratio 0.975, power ≥90%, adjusting for anticipated dro­pout-rate 5%, runtime suppressed):

 make.ibds(ntmt = 4, CV = 0.1, theta0 = 0.975, theta1 = 0.9, target = 0.9,           do.rate = 0.05, sep = "–", details = FALSE)  subject seqno sequence IBD 1 IBD 2 IBD 3        1     2     BDAC  –DA–  BD––  –D–C        2     3     CADB  –AD–  ––DB  C–D–        3     1     ABCD  A––D  –B–D  ––CD        4     4     DCBA  D––A  D–B–  DC––        5     3     CADB  –AD–  ––DB  C–D–        6     4     DCBA  D––A  D–B–  DC––        7     1     ABCD  A––D  –B–D  ––CD        8     2     BDAC  –DA–  BD––  –D–C        9     1     ABCD  A––D  –B–D  ––CD       10     4     DCBA  D––A  D–B–  DC––       11     3     CADB  –AD–  ––DB  C–D–       12     3     CADB  –AD–  ––DB  C–D–       13     4     DCBA  D––A  D–B–  DC––       14     1     ABCD  A––D  –B–D  ––CD       15     2     BDAC  –DA–  BD––  –D–C       16     2     BDAC  –DA–  BD––  –D–C       17     2     BDAC  –DA–  BD––  –D–C       18     2     BDAC  –DA–  BD––  –D–C       19     4     DCBA  D––A  D–B–  DC––       20     1     ABCD  A––D  –B–D  ––CD       21     4     DCBA  D––A  D–B–  DC––       22     1     ABCD  A––D  –B–D  ––CD       23     3     CADB  –AD–  ––DB  C–D–       24     3     CADB  –AD–  ––DB  C–D–       25     3     CADB  –AD–  ––DB  C–D–       26     1     ABCD  A––D  –B–D  ––CD       27     2     BDAC  –DA–  BD––  –D–C       28     1     ABCD  A––D  –B–D  ––CD       29     4     DCBA  D––A  D–B–  DC––       30     4     DCBA  D––A  D–B–  DC––       31     3     CADB  –AD–  ––DB  C–D–       32     2     BDAC  –DA–  BD––  –D–C Reference                : D Tests                    : A, B, C Sequences                : ABCD, BDAC, CADB, DCBA Subjects per sequence    : 8 | 8 | 8 | 8 (balanced) Estimated sample size    : 30 Achieved power           : 0.9167  Anticipated dropout-rate: 5%  Adjusted sample size    : 32  Achieved power          : 0.9318 Randomized               : 2022-07-13 23:02:13 CEST Seed                     : 874461

You can also assign the stuff to a list-variable for checking. No counting any more.

tmp <- make.ibds(ntmt = 6, CV = 0.2) tmp[2:6] $n [1] 20$sequences [1] "ACFDEB" "BEDFCA" "CDABFE" "DBCEAF" "EFBADC" "FAECBD" $n.seq [1] 4 4 3 3 3 3$info [1] "imbalanced" $ibds seq n seq n IBD 1 AF 10 FA 10 IBD 2 BF 10 FB 10 IBD 3 CF 10 FC 10 IBD 4 DF 10 FD 10 IBD 5 EF 10 FE 10 Comparison of one test with two references: make.ibds(ntmt = 3, CV = 0.15, ref = c("B", "C"), sep = "–") subject seqno sequence IBD 1 IBD 2 1 3 BAC BA– –AC 2 3 BAC BA– –AC 3 1 ABC AB– A–C 4 6 CBA –BA C–A 5 1 ABC AB– A–C 6 2 ACB A–B AC– 7 6 CBA –BA C–A 8 5 CAB –AB CA– 9 4 BCA B–A –CA 10 4 BCA B–A –CA 11 5 CAB –AB CA– 12 2 ACB A–B AC– References : B, C Test : A Sequences : ABC, ACB, BAC, BCA, CAB, CBA Subjects per sequence : 2 | 2 | 2 | 2 | 2 | 2 (balanced) Estimated sample size : 12 Achieved power : 0.8305 Randomized : 2022-07-13 23:04:24 CEST Seed : 9664508 Runtime : 20 ms (1 call for balance of IBDs) If all pairwise comparisons are required (i.e., additionally between the tests), the number rises quickly: $$N_\text{comp}=\frac{n!}{2\,(n-2)!}$$ For six treatments we have already 15 pairwise comparisons. make.ibds <- function(ntmt = 3, CV, alpha = 0.05, theta0 = 0.95, theta1, theta2, target = 0.80, ref, do.rate = 0, bal = FALSE, sep = "•", print = TRUE, details = TRUE) { ################################################################## # Estimates the sample size of a Higher-Order (Williams’) design # # to be evaluated by the ‘Two at a Time’ approach, i.e., with # # extracted Incomplete Block Designs. All IBDs will be balanced. # # Note: Balanced IBDs cannot be extracted from Latin Squares. # # Optionally the estimated sample size can be adjusted for an # # anticipated dropout-rate and/or uprounded to obtain balanced # # sequences. # # -------------------------------------------------------------- # # TODO: All pairwise comparisons, i.e., additionally between the # # tests. # ################################################################## if (ntmt < 3) stop("ntmt must be at least 3.") if (missing(CV)) stop("CV must be given.") if (missing(theta1) & missing(theta2)) theta1 <- 0.80 if (missing(theta1)) theta1 <- 1/theta2 if (missing(theta2)) theta2 <- 1/theta1 if (theta0 < theta1 | theta0 > theta2) stop("theta0 must lie within BE limits.") if (target <= 0.5) stop("target ", target, " does not make sense.") if (alpha > 0.5) stop("alpha ", alpha, " does not make sense.") make.equal <- function(n, ns) { return(as.integer(ns * (n %/% ns + as.logical(n %% ns)))) } nadj <- function(n, do.rate, ns) { return(as.integer(make.equal(n / (1 - do.rate), ns))) } suppressMessages(require(PowerTOST)) suppressMessages(require(randomizeBE)) st <- proc.time()[[3]] # Sample size for evaluation by ‘Two at a Time’ approach. # Note that we are using the default design = "2x2". smpl <- sampleN.TOST(alpha = alpha, CV = CV, theta0 = theta0, theta1 = theta1, theta2 = theta2, targetpower = target, print = FALSE) n.orig <- smpl[["Sample size"]] p.orig <- smpl[["Achieved power"]] # Get sequences of a Williams’ design. seqs <- williams(ntmt) ns <- 2 # because 2x2! # Adjust for dropout-rate. n <- nadj(n.orig, do.rate, ns) # Eventual uprounding if balance is requested. if (bal) n <- make.equal(n, length(seqs)) pwr <- power.TOST(alpha = alpha, CV = CV, theta0 = theta0, theta1 = theta1, theta2 = theta2, n = n) # Extract treatment codes. trts <- sort( unique( unlist( strsplit(Reduce(function(x, y) paste0(x, y), seqs), "")))) if (missing(ref)) { # The last treatment in lexical order is the reference. s <- Reduce(function(x, y) paste0(x, y), trts) ref <- substr(s, nchar(s), nchar(s)) } if (sum(ref %in% trts) == 0) stop("ref \'", ref, "\'not in trts \'", trts, "\'") # Extract test(s). tests <- trts[!trts %in% ref] calls <- 0 repeat { # Call randomizations until all IBDs are balanced. # Get a randomization list. calls <- calls + 1 tmp <- suppressWarnings( # Not relevant for TaT. RL4(nsubj = n, seqs = seqs, randctrl = FALSE)) rand <- tmp$rl     # Generate columns for the IBDs.     n.ibd <-  length(tests) * length(ref)     for (j in 1:n.ibd) {       rand[[paste0("IBD.", j)]] <- NA     }     # Build the IBDs.     for (j in 1:nrow(rand)) {       c <- 3       for (k in seq_along(ref)) {         for (m in seq_along(tests)) {           c          <- c  + 1           excl       <- tests[!tests == tests[m]]           excl       <- c(excl, ref[!ref == ref[k]])           excl       <- paste0("[", paste(excl, collapse = ", "), "]")           rand[j, c] <- gsub(excl, sep, rand$sequence[j]) } } } # Check the IBDs for balance. checks <- NA ibd.seq <- as.data.frame( matrix(data = NA, nrow = n.ibd, ncol = 4, byrow = TRUE, dimnames = list(names(rand)[4:ncol(rand)], paste0(rep(c("seq.", "n."), 2), rep(1:2, each = 2))))) for (j in 1:n.ibd) { ibd <- gsub("[^[:alnum:], ]", "", rand[3 + j]) ibd <- gsub("c", "", ibd) ibd <- gsub(" ", "", unlist(strsplit(ibd, ","))) ibd.seq[j, c(1, 3)] <- sort(unique(ibd)) ibd.seq[j, c(2, 4)] <- c(length(ibd[ibd == sort(unique(ibd))[1]]), length(ibd[ibd == sort(unique(ibd))[2]])) checks[j] <- ibd.seq[j, 2] == ibd.seq[j, 4] } if (sum(checks) == length(trts) - 1) break # Success! } ifelse (do.rate == 0, f <- "\n%-22s: ", f <- "\n%-25s: ") # Cosmetics. if (bal) f <- "\n%-25s: " ifelse (length(ref) == 1, txt <- paste0(sprintf(f, "Reference"), ref), txt <- paste0(sprintf(f, "References"), paste(ref, collapse = ", "))) ifelse (length(tests) == 1, txt <- paste0(txt, sprintf(f, "Test"), tests), txt <- paste0(txt, sprintf(f, "Tests"), paste(tests, collapse = ", "))) txt <- paste0(txt, sprintf(f, "Sequences"), paste(seqs, collapse = ", ")) txt <- paste0(txt, sprintf(f, "Subjects per sequence"), paste(tmp$ninseqs, collapse = " | "))   ifelse (length(unique(tmp$ninseqs)) == 1, info <- "balanced", info <- "imbalanced") txt <- paste0(txt, " (", info, ")") txt <- paste0(txt, sprintf(f, "Estimated sample size"), n.orig, sprintf(f, "Achieved power"), sprintf("%.4f", p.orig)) if (!n == n.orig) { if (bal) { txt <- paste0(txt, "\nAdjustment to obtain period-balance") } if (do.rate > 0) { txt <- paste0(txt, sprintf("\n %-24s: ", "Anticipated dropout-rate"), sprintf("%.3g%%", 100 * do.rate)) } txt <- paste0(txt, sprintf("\n %-24s: ", "Adjusted sample size"), n, sprintf("\n %-24s: ", "Achieved power"), sprintf("%.4f", pwr)) } txt <- paste0(txt, sprintf(f, "Randomized"), format(tmp$date, usetz = TRUE),                    sprintf(f, "Seed"), tmp$seed) ibd <- names(rand)[4:ncol(rand)] names(rand)[4:ncol(rand)] <- gsub("\\.", " ", ibd) row.names(ibd.seq) <- names(rand)[4:ncol(rand)] names(ibd.seq) <- rep(c("seq", "n"), 2) et <- signif(1000 * (proc.time()[[3]] - st), 3) if (details) { # Only if requested. ifelse (et < 10, rt <- "<10 ms", rt <- paste(et, "ms")) txt <- paste0(txt, sprintf(f, "Runtime"), rt, " (", calls, " call") if (calls > 1) txt <- paste0(txt, "s") txt <- paste(txt, "for balance of IBDs)") } if (print) { # To the console. print(rand, row.names = FALSE) txt <- paste0(txt, "\n") cat(txt) } # Results as a list. return(invisible(list(rand = rand, n = n, sequences = seqs, n.seq = tmp$ninseqs, info = info,                         ibds = ibd.seq, seed = tmp\$seed,                         calls = calls, et = et,                         txt = substr(txt, 2, nchar(txt))))) }

Dif-tor heh smusma 🖖🏼 Довге життя Україна!
Helmut Schütz

The quality of responses received is directly proportional to the quality of the question asked. 🚮
Science Quotes
Shuanghe
★★

Spain,
2022-07-13 17:35
(203 d 03:30 ago)

@ Helmut
Posting: # 23142
Views: 677

## ‘Two-at-a-Time’ approach; ≥3 treat­ments: new 🇷 script

Hi Helmut,

BioBridges (22/23 September) as well?

Sure! It would be very nice to see all the familiar faces. Actually I searched the forum several time before but couldn't find any information on BioBridges this year. You used to have a post called eventlist each year and BioBridges is usually on it but it seems this year is an exception.

THX for discovering such a nasty bug!

If debugging is the process of removing bugs,

then programming must be the process of putting them in.     Edsger W. Dijkstra

Damn, looking at my own code, I must be a bug farmer then ...

make.ibds <- function(ntmt = 3, CV, theta0 = 0.95, theta1, theta2,

                      target = 0.80, do.rate = 0, bal = FALSE,

                      sep = "•", print = TRUE, details = TRUE) {... }

Thanks. I'll save the code to read it later.

All the best,
Shuanghe
Helmut
★★★

Vienna, Austria,
2022-07-13 18:46
(203 d 02:20 ago)

@ Shuanghe
Posting: # 23143
Views: 731

## You’re a NE🇷D!

Hi Shuanghe,

❝ […] I searched the forum several time before but couldn't find any information on BioBridges this year. You used to have a post called eventlist each year and BioBridges is usually on it but it seems this year is an exception.

Sorry, too busy with crazy stuff.

❝ Damn, looking at my own code, I must be a bug farmer then ...

Welcome to the club!

❝ I'll save the code to read it later.

Reading code? Proves that you are real NE?D!
Don’t get obstipation of too much Spaghetti viennese (i.e., my coding style).

I have stopped reading Stephen King novels.
Richard A. O’Keefe

PS: I edited the script. Use the new one. See also there: Scroll down and click .

Dif-tor heh smusma 🖖🏼 Довге життя Україна!
Helmut Schütz

The quality of responses received is directly proportional to the quality of the question asked. 🚮
Science Quotes