Summarize group sequential designs in gt tables
Yujie Zhao
Source:vignettes/articles/story-summarize-designs.Rmd
story-summarize-designs.Rmd
Overview
This vignette introduces publication quality table production for group sequential designs in the gsDesign2 package. It also demonstrates designs for an example scenario using multiple design approaches. We divide the document into 3 parts:
- Design specification and derivation
- Printing design summary tables
- Details of output from design functions
- Details on table output options
The reader can decide which of these sections is of interest to them.
The function used to generate bounds tables is
gsDesign2::summary()
. Users can use
gsDesign2::as_gt()
to format the above table using the
gt package.
In this vignette, we introduce a general approach to bound summaries by examples using different design approaches for a time-to-event outcome:
- the average hazard ratio (AHR) method extended from Mukhopadhyay et al. (2020) using
gsDesign2::gs_design_ahr()
; - the weighted logrank (WLR) method of Yung and
Liu (2020) using
gsDesign2::gs_design_wlr()
;
Design specification and derivation
Design parameters
The design parameters we use across the different designs derived are:
# enrollment/failure rates
enroll_rate <- define_enroll_rate(
duration = 12,
rate = 30
)
fail_rate <- define_fail_rate(
duration = c(4, 100),
fail_rate = log(2) / 12,
hr = c(1, .6),
dropout_rate = .001
)
# Information fraction
info_frac <- (1:3) / 3
# Analysis times in months; first 2 will be ignored as info_frac will not be achieved
analysis_time <- c(.01, .02, 36)
# Experimental / Control randomization ratio
ratio <- 1
# 1-sided Type I error
alpha <- 0.025
# Type II error (1 - power)
beta <- 0.1
# Upper bound
upper <- gsDesign2::gs_spending_bound # alpha-spending bound
upar <- list(sf = gsDesign::sfLDOF, total_spend = 0.025, param = NULL, timing = NULL)
# Lower bound
lower <- gsDesign2::gs_spending_bound # beta-spending bound
lpar <- list(sf = gsDesign::sfHSD, total_spend = 0.1, param = 0, timing = NULL)
# Fleming-Harrington (FH) weight functions for weighted logrank (WLR)
wgt00 <- function(x, arm0, arm1) { # Equal weighting for logrank
gsDesign2::wlr_weight_fh(x, arm0, arm1, rho = 0, gamma = 0)
}
wgt05 <- function(x, arm0, arm1) { # Early downweighting with FH(0,.5)
gsDesign2::wlr_weight_fh(x, arm0, arm1, rho = 0, gamma = .5)
}
# Both of above tests for MaxCombo: logrank and FH(0,.5)
fh_test <- rbind(
# Include logrank for all 3 analyses
data.frame(rho = 0, gamma = 0, tau = -1, test = 1, Analysis = 1:3, analysis_time = c(12, 24, 36)),
# Only include FH(0,.5) for analyses 2 and 3
data.frame(rho = c(0, 0.5), gamma = 0.5, tau = -1, test = 2:3, Analysis = 3, analysis_time = 36)
)
Deriving designs
AHR design derivation
Using the design parameters above, the AHR design is derived as follows:
By using the design parameters above, one can generate an AHR model
by gs_design_ahr
as
x_design_ahr <- gs_design_ahr(
enroll_rate = enroll_rate,
fail_rate = fail_rate,
info_frac = info_frac,
analysis_time = analysis_time,
ratio = ratio,
alpha = alpha,
beta = beta,
upper = upper,
upar = upar,
lower = lower,
lpar = lpar
)
x_power_ahr <- gs_power_ahr(
enroll_rate = x_design_ahr$enroll_rate,
fail_rate = x_design_ahr$fail_rate,
event = c(100, 200, 300),
analysis_time = NULL,
upper = upper,
upar = upar,
lower = lower,
lpar = lpar
)
WLR design derivation
x_design_wlr <- gs_design_wlr(
enroll_rate = enroll_rate,
fail_rate = fail_rate,
weight = wgt05,
info_frac = NULL,
analysis_time = sort(unique(x_design_ahr$analysis$time)),
ratio = ratio,
alpha = alpha,
beta = beta,
upper = upper,
upar = upar,
lower = lower,
lpar = lpar
) |> to_integer()
x_power_wlr <- gs_power_wlr(
enroll_rate = x_design_wlr$enroll_rate,
fail_rate = x_design_wlr$fail_rate,
weight = wgt05,
event = c(50, 100, 150),
analysis_time = NULL,
upper = upper,
upar = upar,
lower = lower,
lpar = lpar
)
Default summary table production
Instead of outputting 4 detailed tables (a table of enrollment rates,
a table of failure rates, a table of analysis summary, a table of bounds
summary), users can get a com pensive summary table by calling
summary(x)
, where x
is the object returned
either by gs_design_ahr
or gs_design_wlr
. The
summary()
function produces an overall summary table for
bounds for publication in a protocol.
For example, the default output of summary()
for the AHR
method is
Bound | Z | ~HR at bound | Nominal p | Alternate hypothesis | Null hypothesis |
---|---|---|---|---|---|
Analysis: 1 Time: 11.7 N: 479.6 Event: 121.5 AHR: 0.85 Information fraction: 0.33 | |||||
Futility | −0.9400 | 1.1856 | 0.8259 | 0.0338 | 0.1741 |
Efficacy | 3.7100 | 0.5101 | 0.0001 | 0.0027 | 0.0001 |
Analysis: 2 Time: 20.3 N: 493.1 Event: 243 AHR: 0.74 Information fraction: 0.66 | |||||
Futility | 0.6300 | 0.9228 | 0.2656 | 0.0666 | 0.7368 |
Efficacy | 2.5100 | 0.7246 | 0.0060 | 0.4135 | 0.0060 |
Analysis: 3 Time: 36 N: 493.1 Event: 364.6 AHR: 0.69 Information fraction: 1 | |||||
Futility | 1.9900 | 0.8118 | 0.0233 | 0.1006 | 0.9753 |
Efficacy | 1.9900 | 0.8116 | 0.0231 | 0.9000 | 0.0245 |
Please note the summary()
can also be applied to
objected returned by gs_power_ahr()
. For example,
Bound | Z | ~HR at bound | Nominal p | Alternate hypothesis | Null hypothesis |
---|---|---|---|---|---|
Analysis: 1 Time: 10.4 N: 428.7 Event: 100 AHR: 0.87 Information fraction: 0.33 | |||||
Futility | −1.1200 | 1.2537 | 0.8691 | 0.0341 | 0.1309 |
Efficacy | 3.7100 | 0.4735 | 0.0001 | 0.0015 | 0.0001 |
Analysis: 2 Time: 16.7 N: 493.1 Event: 200 AHR: 0.78 Information fraction: 0.67 | |||||
Futility | 0.1000 | 0.9863 | 0.4616 | 0.0671 | 0.5450 |
Efficacy | 2.5100 | 0.6984 | 0.0060 | 0.2262 | 0.0060 |
Analysis: 3 Time: 26.2 N: 493.1 Event: 300 AHR: 0.71 Information fraction: 1 | |||||
Futility | 1.3900 | 0.8503 | 0.0822 | 0.1006 | 0.9188 |
Efficacy | 1.9900 | 0.7926 | 0.0231 | 0.8029 | 0.0249 |
And the default output of summary()
for the WLR method
is
Bound | Z | ~wHR at bound | Nominal p | Alternate hypothesis | Null hypothesis |
---|---|---|---|---|---|
Analysis: 1 Time: 11.6 N: 360.3 Event: 91 AHR: 0.79 Information fraction: 0.14 | |||||
Futility | −1.1900 | 1.2832 | 0.8828 | 0.0140 | 0.1172 |
Efficacy | 3.7300 | 0.4580 | 0.0001 | 0.0034 | 0.0001 |
Analysis: 2 Time: 20.2 N: 372 Event: 183 AHR: 0.69 Information fraction: 0.46 | |||||
Futility | 0.5700 | 0.9196 | 0.2853 | 0.0462 | 0.7198 |
Efficacy | 2.5200 | 0.6894 | 0.0059 | 0.4368 | 0.0060 |
Analysis: 3 Time: 36 N: 372 Event: 275 AHR: 0.64 Information fraction: 1 | |||||
Futility | 1.9900 | 0.7871 | 0.0236 | 0.1001 | 0.9756 |
Efficacy | 2.0200 | 0.7836 | 0.0216 | 0.8957 | 0.0228 |
Note that summary()
can also be applied to summarize an
object returned by gs_power_wlr()
.
Bound | Z | ~wHR at bound | Nominal p | Alternate hypothesis | Null hypothesis |
---|---|---|---|---|---|
Analysis: 1 Time: 8.3 N: 255.9 Event: 50 AHR: 0.86 Information fraction: 0.13 | |||||
Futility | −1.8100 | 1.6700 | 0.9651 | 0.0134 | 0.0349 |
Efficacy | 6.0600 | 0.1799 | 0.0000 | 0.0000 | 0.0000 |
Analysis: 2 Time: 12.3 N: 372 Event: 100 AHR: 0.78 Information fraction: 0.51 | |||||
Futility | −0.5900 | 1.1248 | 0.7218 | 0.0512 | 0.2877 |
Efficacy | 2.9400 | 0.5559 | 0.0017 | 0.0362 | 0.0017 |
Analysis: 3 Time: 16.6 N: 372 Event: 150 AHR: 0.72 Information fraction: 1 | |||||
Futility | 0.4100 | 0.9358 | 0.3422 | 0.1001 | 0.6745 |
Efficacy | 1.9700 | 0.7250 | 0.0245 | 0.4561 | 0.0249 |
Detailed summary table formatting
Here we demonstrate options for formatting analysis rows, bound rows as well as other table parameters such as titles, labels and footnotes.
Customize the variables to be summarized for each analysis
In the above default table summary table generated by
summary(x)
, the variables used to summarize each analysis
includes analysis
, time
, n
(sample
size), event
, AHR
, and info_frac
(information fraction). But users can customize these variables chosen
using analysis_vars = ...
and the corresponding decimals
displayed using the argument analysis_decimals = ...
. For
example
summary(
x_design_ahr,
analysis_vars = c("n", "event"),
analysis_decimals = c(1, 1)
) %>%
gt::gt() %>%
gt::fmt_number(columns = c(3:6), decimals = 4)
Bound | Z | ~HR at bound | Nominal p | Alternate hypothesis | Null hypothesis |
---|---|---|---|---|---|
Analysis: 1 N: 479.6 Event: 121.5 | |||||
Futility | −0.9400 | 1.1856 | 0.8259 | 0.0338 | 0.1741 |
Efficacy | 3.7100 | 0.5101 | 0.0001 | 0.0027 | 0.0001 |
Analysis: 2 N: 493.1 Event: 243 | |||||
Futility | 0.6300 | 0.9228 | 0.2656 | 0.0666 | 0.7368 |
Efficacy | 2.5100 | 0.7246 | 0.0060 | 0.4135 | 0.0060 |
Analysis: 3 N: 493.1 Event: 364.6 | |||||
Futility | 1.9900 | 0.8118 | 0.0233 | 0.1006 | 0.9753 |
Efficacy | 1.9900 | 0.8116 | 0.0231 | 0.9000 | 0.0245 |
Please note that there is no need to input "Analysis"
into analysis_vars = ...
as it will always appear.
Customize the bound names
Users can also customize the bound names. In the default output
generated by summary(x)
, the bound name is
c("Efficacy", "Futility")
, which can be changed into
c("A is better", "B is better")
for a 2-sided design by
using the argument bound_names = ...
. For example,
summary(
x_design_ahr,
bound_names = c("A is better", "B is better")
) %>%
mutate_if(is.numeric, round, digits = 4) %>%
gt::gt() %>%
gt::fmt_number(columns = c(3:6), decimals = 4)
#> `mutate_if()` ignored the following grouping variables:
#> • Column `Analysis`
Bound | Z | ~HR at bound | Nominal p | Alternate hypothesis | Null hypothesis |
---|---|---|---|---|---|
Analysis: 1 Time: 11.7 N: 479.6 Event: 121.5 AHR: 0.85 Information fraction: 0.33 | |||||
B is better | −0.9400 | 1.1856 | 0.8259 | 0.0338 | 0.1741 |
A is better | 3.7100 | 0.5101 | 0.0001 | 0.0027 | 0.0001 |
Analysis: 2 Time: 20.3 N: 493.1 Event: 243 AHR: 0.74 Information fraction: 0.66 | |||||
B is better | 0.6300 | 0.9228 | 0.2656 | 0.0666 | 0.7368 |
A is better | 2.5100 | 0.7246 | 0.0060 | 0.4135 | 0.0060 |
Analysis: 3 Time: 36 N: 493.1 Event: 364.6 AHR: 0.69 Information fraction: 1 | |||||
B is better | 1.9900 | 0.8118 | 0.0233 | 0.1006 | 0.9753 |
A is better | 1.9900 | 0.8116 | 0.0231 | 0.9000 | 0.0245 |
Customize into a gt table and add title/subtitle/footnotes/spanners
Users can also use as_gt()
to get the the above R table
into a gt table. Furthermore, they can edit the
title/subtitle/spanner/footnotes of the gt table by using the arguments
in summary
.
summary(x_design_ahr) %>%
as_gt(
title = "Summary of the Crossing Probability",
subtitle = "by Using gs_design_ahr",
colname_spanner = "Cumulative boundary crossing probability",
colname_spannersub = c("Alternate hypothesis", "Null hypothesis"),
footnote = list(
content = c(
"approximate hazard ratio to cross bound.",
"gs_design_ahr is a function in gsDesign2.",
"AHR is average hazard ratio; info_frac is information fraction."
),
location = c("~HR at bound", NA, NA),
attr = c("colname", "subtitle", "analysis")
)
)
Summary of the Crossing Probability | |||||
by Using gs_design_ahr1 | |||||
Bound | Z | Nominal p | ~HR at bound2 | Cumulative boundary crossing probability | |
---|---|---|---|---|---|
Alternate hypothesis | Null hypothesis | ||||
Analysis: 1 Time: 11.7 N: 479.6 Event: 121.5 AHR: 0.85 Information fraction: 0.333 | |||||
Futility | -0.94 | 0.8259 | 1.1856 | 0.0338 | 0.1741 |
Efficacy | 3.71 | 0.0001 | 0.5101 | 0.0027 | 0.0001 |
Analysis: 2 Time: 20.3 N: 493.1 Event: 243 AHR: 0.74 Information fraction: 0.663 | |||||
Futility | 0.63 | 0.2656 | 0.9228 | 0.0666 | 0.7368 |
Efficacy | 2.51 | 0.0060 | 0.7246 | 0.4135 | 0.0060 |
Analysis: 3 Time: 36 N: 493.1 Event: 364.6 AHR: 0.69 Information fraction: 13 | |||||
Futility | 1.99 | 0.0233 | 0.8118 | 0.1006 | 0.9753 |
Efficacy | 1.99 | 0.0231 | 0.8116 | 0.9000 | 4 0.0245 |
1 gs_design_ahr is a function in gsDesign2. | |||||
2 approximate hazard ratio to cross bound. | |||||
3 AHR is average hazard ratio; info_frac is information fraction. | |||||
4 Cumulative alpha for final analysis (0.0245) is less than the full alpha (0.025) when the futility bound is non-binding. The smaller value subtracts the probability of crossing a futility bound before crossing an efficacy bound at a later analysis (0.025 - 0.0005 = 0.0245) under the null hypothesis. |
The above objective can also be realized by using functions in the R
package gt
for custom design of table layout. We note that
as_gt()
always produces a gt
object and, thus,
can be further customized with gt package formatting
functions. In the future, we to support rich text format using a
function as_rtf()
in a fashion similar to
as_gt()
.
Customize the variables to display
Users can select the variables to be displayed in the summary table
by using the argument display_colunm = ...
.
Bound summary for AHR design | |||
AHR approximations of ~HR at bound | |||
Bound | Z | Cumulative boundary crossing probability | |
---|---|---|---|
Alternate hypothesis | Null hypothesis | ||
Analysis: 1 Time: 11.7 N: 479.6 Event: 121.5 AHR: 0.85 Information fraction: 0.33 | |||
Futility | -0.94 | 0.0338 | 0.1741 |
Efficacy | 3.71 | 0.0027 | 0.0001 |
Analysis: 2 Time: 20.3 N: 493.1 Event: 243 AHR: 0.74 Information fraction: 0.66 | |||
Futility | 0.63 | 0.0666 | 0.7368 |
Efficacy | 2.51 | 0.4135 | 0.0060 |
Analysis: 3 Time: 36 N: 493.1 Event: 364.6 AHR: 0.69 Information fraction: 1 | |||
Futility | 1.99 | 0.1006 | 0.9753 |
Efficacy | 1.99 | 0.9000 | 1 0.0245 |
1 Cumulative alpha for final analysis (0.0245) is less than the full alpha (0.025) when the futility bound is non-binding. The smaller value subtracts the probability of crossing a futility bound before crossing an efficacy bound at a later analysis (0.025 - 0.0005 = 0.0245) under the null hypothesis. |
Customize whether to show infinity bound or not
Users have options to either show the infinity bounds or not by
taking advantage of display_inf_bound = ...
.
Bound summary for AHR design | |||||
AHR approximations of ~HR at bound | |||||
Bound | Z | Nominal p1 | ~HR at bound2 | Cumulative boundary crossing probability | |
---|---|---|---|---|---|
Alternate hypothesis | Null hypothesis | ||||
Analysis: 1 Time: 11.7 N: 479.6 Event: 121.5 AHR: 0.85 Information fraction: 0.33 | |||||
Futility | -0.94 | 0.8259 | 1.1856 | 0.0338 | 0.1741 |
Efficacy | 3.71 | 0.0001 | 0.5101 | 0.0027 | 0.0001 |
Analysis: 2 Time: 20.3 N: 493.1 Event: 243 AHR: 0.74 Information fraction: 0.66 | |||||
Futility | 0.63 | 0.2656 | 0.9228 | 0.0666 | 0.7368 |
Efficacy | 2.51 | 0.0060 | 0.7246 | 0.4135 | 0.0060 |
Analysis: 3 Time: 36 N: 493.1 Event: 364.6 AHR: 0.69 Information fraction: 1 | |||||
Futility | 1.99 | 0.0233 | 0.8118 | 0.1006 | 0.9753 |
Efficacy | 1.99 | 0.0231 | 0.8116 | 0.9000 | 3 0.0245 |
1 One-sided p-value for experimental vs control treatment. Value < 0.5 favors experimental, > 0.5 favors control. | |||||
2 Approximate hazard ratio to cross bound. | |||||
3 Cumulative alpha for final analysis (0.0245) is less than the full alpha (0.025) when the futility bound is non-binding. The smaller value subtracts the probability of crossing a futility bound before crossing an efficacy bound at a later analysis (0.025 - 0.0005 = 0.0245) under the null hypothesis. |
Details of output from design/power functions
There are four components in the objects returned by either
gs_design_ahr()
/gs_design_wlr()
or
gs_power_ahr()
/gs_power_wlr()
: 1. failure
rates: a table summarizing failure rate and dropout rate. 1. enrollment
rates: a table summarizing the enrollment rate. 1. bounds: a table
summarize the bound of each analysis. 1. analysis: a table summarize the
each analysis, with each one row for one analysis one hypothesis.
Failure rates
The failure rates of different gsDesign object can be obtained by
using x$fail_rate
, where x
is the object
returned either by gs_design_ahr
or
gs_design_wlr
. For example, the failure rates of the AHR
design derivation can be returned by calling
x_design_ahr$fail_rate %>%
gt::gt() %>%
gt::fmt_number(columns = 3:5, decimals = 4)
stratum | duration | fail_rate | dropout_rate | hr |
---|---|---|---|---|
All | 4 | 0.0578 | 0.0010 | 1.0000 |
All | 100 | 0.0578 | 0.0010 | 0.6000 |
Please note that both x_design_ahr
and
x_wlr
returns the same failure rates, which is the same as
that inputted as fail_rate
. To verify, let’s take a look at
the failure rate of the WLR design derivation, which are shown as
below.
x_design_wlr$fail_rate %>%
gt::gt() %>%
gt::fmt_number(columns = 3:5, decimals = 4)
stratum | duration | fail_rate | dropout_rate | hr |
---|---|---|---|---|
All | 4 | 0.0578 | 0.0010 | 1.0000 |
All | 100 | 0.0578 | 0.0010 | 0.6000 |
Enrollment
The enrollment rate of a gs design derivation can be collected by
using x$fail_rate
, where x
is the object
returned either by gs_design_ahr
or
gs_design_wlr
. For example, the enrollment rates of the
AHR/WLR design derivation is
x_design_ahr$enroll_rate %>%
gt::gt() %>%
gt::fmt_number(columns = 3, decimals = 4)
stratum | duration | rate |
---|---|---|
All | 12 | 41.0885 |
x_design_wlr$enroll_rate %>%
gt::gt() %>%
gt::fmt_number(columns = 3, decimals = 4)
stratum | duration | rate |
---|---|---|
All | 12 | 31.0000 |
It can be seen that, although the design derivation is different, the enrollment rate table share the same table structure, same enrollment period durations for each rate. Yet, the enrollment rates differ between designs only by a multiplicative constant.
Analysis
The analysis summary table has the structure of one row per analysis
per hypothesis. And columns can vary with different defaults for each
design option. This type of tables are useful for understanding
commonalities in how designs are summarized for different models. To get
analysis summary table, users can call x$analysis
, where
x
is the object returned either by
gs_design_ahr
or gs_design_wlr
. For example,
the analysis summary of the AHR/WLR design derivation is
x_design_ahr$analysis %>%
gt::gt() %>%
gt::fmt_number(columns = 2:8, decimals = 4)
analysis | time | n | event | ahr | theta | info | info0 | info_frac |
---|---|---|---|---|---|---|---|---|
1 | 11.6713 | 479.5569 | 121.5178 | 0.8487 | 0.1641 | 29.8909 | 30.3795 | 0.3322753 |
2 | 20.2531 | 493.0625 | 243.0357 | 0.7427 | 0.2974 | 59.4218 | 60.7589 | 0.6605497 |
3 | 36.0000 | 493.0625 | 364.5535 | 0.6917 | 0.3686 | 89.9581 | 91.1384 | 1.0000000 |
x_design_wlr$analysis %>%
gt::gt() %>%
gt::fmt_number(columns = 2:8, decimals = 4)
analysis | time | n | event | ahr | theta | info | info0 | info_frac | info_frac0 |
---|---|---|---|---|---|---|---|---|---|
1 | 11.6211 | 360.2556 | 91.0000 | 0.7926 | 0.5447 | 3.4558 | 3.4759 | 0.1384101 | 0.1317923 |
2 | 20.2095 | 372.0000 | 182.9999 | 0.6898 | 0.6949 | 11.5089 | 11.7455 | 0.4609515 | 0.4453419 |
3 | 35.9888 | 372.0000 | 275.0000 | 0.6443 | 0.6819 | 24.9676 | 26.3742 | 1.0000000 | 1.0000000 |
Bounds
The analysis summary table has the structure of One row per analysis
per bound per hypothesis. Columns can vary with different defaults for
each design option. To get a bonus summary table, users can call
x$analysis
, where x
is the object returned
either by gs_design_ahr
or gs_design_wlr
. For
example, the bounds summary of the AHR/WLR design derivation is
x_design_ahr$bound %>%
gt::gt() %>%
gt::fmt_number(columns = c(3, 5:7), decimals = 4)
analysis | bound | probability | probability0 | z | ~hr at bound | nominal p |
---|---|---|---|---|---|---|
1 | upper | 0.0027 | 0.0001035057 | 3.7103 | 0.5101 | 0.0001 |
1 | lower | 0.0338 | 0.1740609319 | −0.9382 | 1.1856 | 0.8259 |
2 | upper | 0.4135 | 0.0060482351 | 2.5114 | 0.7246 | 0.0060 |
2 | lower | 0.0666 | 0.7368187135 | 0.6262 | 0.9228 | 0.2656 |
3 | upper | 0.9000 | 0.0245385477 | 1.9931 | 0.8116 | 0.0231 |
3 | lower | 0.1006 | 0.9753350071 | 1.9905 | 0.8118 | 0.0233 |
x_design_wlr$bounds %>%
gt::gt() %>%
gt::fmt_number(columns = c(3, 5:7), decimals = 4)
analysis | bound | probability | probability0 | z | ~hr at bound | nominal p |
---|---|---|---|---|---|---|
1 | upper | 0.0034 | 9.762582e-05 | 3.7251 | 0.4580 | 0.0001 |
1 | lower | 0.0140 | 1.171678e-01 | −1.1893 | 1.2832 | 0.8828 |
2 | upper | 0.4368 | 5.998566e-03 | 2.5158 | 0.6894 | 0.0059 |
2 | lower | 0.0462 | 7.198316e-01 | 0.5672 | 0.9196 | 0.2853 |
3 | upper | 0.8957 | 2.283770e-02 | 2.0223 | 0.7836 | 0.0216 |
3 | lower | 0.1001 | 9.756012e-01 | 1.9851 | 0.7871 | 0.0236 |