diff --git a/DESCRIPTION b/DESCRIPTION index a29c1a21..afe1656d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: rpact Title: Confirmatory Adaptive Clinical Trial Design and Analysis -Version: 4.0.1.9259 -Date: 2024-09-19 +Version: 4.0.1.9260 +Date: 2024-09-20 Authors@R: c( person( given = "Gernot", diff --git a/NEWS.md b/NEWS.md index 068fcbd7..761d71cd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -12,7 +12,7 @@ * Usage of pipe-operators improved * Analysis progress messages are only displayed when R is used interactively * Manual use of `kable()` for rpact result objects marked as deprecated, as the formatting and display will be handled automatically by rpact -* Minor summary improvements +* The order of all summary entries has been revised and optimized * Minimum version of suggested package `ggplot2` changed from 2.2.0 to 3.2.0 * Issues [#41](https://github.com/rpact-com/rpact/issues/41), [#44](https://github.com/rpact-com/rpact/issues/44), [#46](https://github.com/rpact-com/rpact/issues/46), and [#47](https://github.com/rpact-com/rpact/issues/47) fixed * When analyzing with a two-sided test, an issue with the calculation of the conditional rejection probability was fixed diff --git a/R/class_summary.R b/R/class_summary.R index f5e2e7b5..72341553 100644 --- a/R/class_summary.R +++ b/R/class_summary.R @@ -13,8 +13,8 @@ ## | ## | Contact us for information about our services: info@rpact.com ## | -## | File version: $Revision: 8240 $ -## | Last changed: $Date: 2024-09-19 16:58:11 +0200 (Do, 19 Sep 2024) $ +## | File version: $Revision: 8243 $ +## | Last changed: $Date: 2024-09-20 07:33:34 +0200 (Fr, 20 Sep 2024) $ ## | Last changed by: $Author: pahlke $ ## | @@ -444,7 +444,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", formatRepeatedPValues = FALSE, validateParameterType = TRUE, lastStage = NA_integer_, - roundDigitsAsInformation = FALSE) { + roundDigitsAsInformation = FALSE, + showNA = FALSE) { if (!is.null(parameterName) && length(parameterName) == 1 && inherits(parameterSet, "ParameterSet") && parameterSet$.getParameterType(parameterName) == C_PARAM_NOT_APPLICABLE) { @@ -584,7 +585,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", cumsumEnabled = cumsumEnabled, smoothedZeroFormat = smoothedZeroFormat, formatRepeatedPValues = formatRepeatedPValues, - roundDigitsAsInformation = roundDigitsAsInformation + roundDigitsAsInformation = roundDigitsAsInformation, + showNA = showNA ) if (parameterName1 %in% c("piControl", "overallPiControl", "overallPooledStDevs")) { @@ -602,7 +604,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", cumsumEnabled = cumsumEnabled, smoothedZeroFormat = smoothedZeroFormat, formatRepeatedPValues = formatRepeatedPValues, - roundDigitsAsInformation = roundDigitsAsInformation + roundDigitsAsInformation = roundDigitsAsInformation, + showNA = showNA ) valuesToShow2 <- self$.getInnerValues(valuesToShow2, transpose = transpose) } @@ -777,23 +780,31 @@ SummaryFactory <- R6::R6Class("SummaryFactory", for (variantIndex in 1:numberOfVariants) { colValues <- self$.getColumnValues(parameterName, values, variantIndex, transposed) - colValues <- .getSummaryValuesFormatted(parameterSet, parameterName1, - colValues, + colValues <- .getSummaryValuesFormatted( + parameterSet, + parameterName1, + values = colValues, roundDigits = roundDigits, ceilingEnabled = ceilingEnabled, cumsumEnabled = cumsumEnabled, smoothedZeroFormat = smoothedZeroFormat, formatRepeatedPValues = formatRepeatedPValues, - roundDigitsAsInformation = roundDigitsAsInformation + roundDigitsAsInformation = roundDigitsAsInformation, + showNA = showNA ) colValues2 <- NA_real_ if (!all(is.na(values2))) { colValues2 <- self$.getColumnValues(parameterName, values2, variantIndex, transposed) - colValues2 <- .getSummaryValuesFormatted(parameterSet, parameterName2, colValues2, - roundDigits = roundDigits, ceilingEnabled = ceilingEnabled, + colValues2 <- .getSummaryValuesFormatted( + parameterSet, + parameterName2, + values = colValues2, + roundDigits = roundDigits, + ceilingEnabled = ceilingEnabled, cumsumEnabled = cumsumEnabled, smoothedZeroFormat = smoothedZeroFormat, formatRepeatedPValues = formatRepeatedPValues, - roundDigitsAsInformation = roundDigitsAsInformation + roundDigitsAsInformation = roundDigitsAsInformation, + showNA = showNA ) } colValues <- self$.getFormattedParameterValue(valuesToShow = colValues, valuesToShow2 = colValues2) @@ -923,7 +934,14 @@ SummaryFactory <- R6::R6Class("SummaryFactory", ) ) -.formatSummaryValues <- function(values, digits, smoothedZeroFormat = FALSE, formatRepeatedPValues = FALSE) { +.formatSummaryValues <- function( + values, + ..., + digits, + smoothedZeroFormat = FALSE, + formatRepeatedPValues = FALSE, + showNA = FALSE) { + if (is.na(digits)) { digits <- 3 } @@ -974,7 +992,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", } } - formattedValue[is.na(formattedValue) | trimws(formattedValue) == "NA"] <- getOption("rpact.summary.na", "") + formattedValue[is.na(formattedValue) | trimws(formattedValue) == "NA"] <- + ifelse(showNA, "n/a", getOption("rpact.summary.na", "")) return(formattedValue) } @@ -989,7 +1008,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", cumsumEnabled = FALSE, smoothedZeroFormat = FALSE, formatRepeatedPValues = FALSE, - roundDigitsAsInformation = FALSE) { + roundDigitsAsInformation = FALSE, + showNA = FALSE) { if (!is.numeric(values)) { return(values) @@ -1048,7 +1068,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", values <- .formatSummaryValues(values, digits = roundDigits, smoothedZeroFormat = smoothedZeroFormat, - formatRepeatedPValues = formatRepeatedPValues + formatRepeatedPValues = formatRepeatedPValues, + showNA = showNA ) } }, @@ -3234,7 +3255,7 @@ SummaryFactory <- R6::R6Class("SummaryFactory", ) } - return(invisible(summaryFactory)) + return(summaryFactory) } #' @@ -3576,7 +3597,8 @@ SummaryFactory <- R6::R6Class("SummaryFactory", parameterName = parameterNameSubjects, parameterCaption = subjectsCaption, roundDigits = digitSettings$digitsSampleSize, - validateParameterType = !countDataEnabled + validateParameterType = !countDataEnabled, + showNA = TRUE ) } diff --git a/R/f_simulation_base_count_data.R b/R/f_simulation_base_count_data.R index 4983c7d6..e8f78a7c 100644 --- a/R/f_simulation_base_count_data.R +++ b/R/f_simulation_base_count_data.R @@ -13,8 +13,8 @@ ## | ## | Contact us for information about our services: info@rpact.com ## | -## | File version: $Revision: 8241 $ -## | Last changed: $Date: 2024-09-19 17:11:12 +0200 (Do, 19 Sep 2024) $ +## | File version: $Revision: 8243 $ +## | Last changed: $Date: 2024-09-20 07:33:34 +0200 (Fr, 20 Sep 2024) $ ## | Last changed by: $Author: pahlke $ ## | @@ -126,21 +126,22 @@ #' \enumerate{ #' \item \code{iterationNumber}: The number of the simulation iteration. #' \item \code{stageNumber}: The stage. -#' \item \code{lambda1}: The assumed or derived event rate in the treatment group (if available). -#' \item \code{lambda2}: The assumed or derived event rate in the control group (if available). -#' \item \code{numberOfSubjects}: The number of subjects under consideration when the -#' (interim) analysis takes place. +#' \item \code{lambda1}: The assumed or derived event rate in the treatment group. +#' \item \code{lambda2}: The assumed or derived event rate in the control group. +#' \item \code{accrualTime}: The assumed accrualTime. +#' \item \code{followUpTime}: The assumed followUpTime. +#' \item \code{overdispersion}: The assumed overdispersion. +#' \item \code{fixedFollowUp}: The assumed fixedFollowUp. +#' \item \code{numberOfSubjects}: The number of subjects under consideration when the (interim) analysis takes place. #' \item \code{rejectPerStage}: 1 if null hypothesis can be rejected, 0 otherwise. #' \item \code{futilityPerStage}: 1 if study should be stopped for futility, 0 otherwise. #' \item \code{testStatistic}: The test statistic that is used for the test decision -#' \item \code{overallLambda1}: The cumulative rate in treatment group 1. -#' \item \code{overallLambda2}: The cumulative rate in treatment group 2. -#' \item \code{sampleSizesPerStage1}: The stage-wise sample size in treatment group 1. -#' \item \code{sampleSizesPerStage2}: The stage-wise sample size in treatment group 2. +#' \item \code{estimatedLambda1}: The estimated rate in treatment group 1. +#' \item \code{estimatedLambda2}: The estimated rate in treatment group 2. +#' \item \code{estimatedOverdispersion}: The estimated overdispersion. +#' \item \code{infoAnalysis}: The Fisher information at interim stage. #' \item \code{trialStop}: \code{TRUE} if study should be stopped for efficacy or futility or final stage, \code{FALSE} otherwise. -#' \item \code{conditionalPowerAchieved}: The conditional power for the subsequent stage of the trial for -#' selected sample size and effect. The effect is either estimated from the data or can be -#' user defined with \code{lambda1H1} and \code{lambda2H1}. +#' \item \code{conditionalPowerAchieved}: Not yet available #' } #' #' @template return_object_simulation_results @@ -312,6 +313,9 @@ getSimulationCounts <- function(design = NULL, dataTestStatistic <- rep(NA_real_, len) dataTrialStop <- rep(NA, len) dataConditionalPower <- rep(NA_real_, len) + dataSampleSizes <- rep(NA_real_, len) + dataReject <- rep(0, len) + dataFutility <- rep(0, len) index <- 1 messageShown <- FALSE @@ -411,6 +415,7 @@ getSimulationCounts <- function(design = NULL, zValue <- (2 * directionUpper - 1) * (log(nb[1]) - log(nb[2]) - log(thetaH0)) * sqrt(infoAnalysis) if (!is.na(zValue) && zValue > .getCriticalValues(design, 1)) { reject[1] <- reject[1] + 1 + dataReject[index] <- 1 } iterations[1, iCase] <- iterations[1, iCase] + 1 nTotal <- nTotal + n1 + n2 @@ -423,7 +428,7 @@ getSimulationCounts <- function(design = NULL, dataLambda1[index] <- lambda1[iCase] dataLambda2[index] <- lambda2 dataOverdispersion[index] <- overdispersion - dataFixedFollowUp[index] <- !is.na(fixedExposureTime) + dataFixedFollowUp[index] <- fixedExposureTime dataNegativeBinomialEstimate1[index] <- nb[1] dataNegativeBinomialEstimate2[index] <- nb[2] dataNegativeBinomialOverdispersion[index] <- nb[3] @@ -431,6 +436,7 @@ getSimulationCounts <- function(design = NULL, dataTestStatistic[index] <- zValue dataTrialStop[index] <- TRUE dataConditionalPower[index] <- NA_real_ + dataSampleSizes[index] <- n1 + n2 index <- index + 1 } else { @@ -507,7 +513,7 @@ getSimulationCounts <- function(design = NULL, dataLambda1[index] <- lambda1[iCase] dataLambda2[index] <- lambda2 dataOverdispersion[index] <- overdispersion - dataFixedFollowUp[index] <- !is.na(fixedExposureTime) + dataFixedFollowUp[index] <- fixedExposureTime dataNegativeBinomialEstimate1[index] <- nb[1] dataNegativeBinomialEstimate2[index] <- nb[2] dataNegativeBinomialOverdispersion[index] <- nb[3] @@ -515,17 +521,20 @@ getSimulationCounts <- function(design = NULL, dataTestStatistic[index] <- zValue dataConditionalPower[index] <- conditionalPowerAchieved dataTrialStop[index] <- FALSE + dataSampleSizes[index] <- length(timeUnderObservation1) + length(timeUnderObservation2) if (!is.na(zValue)) { if (zValue > .getCriticalValues(design, k)) { reject[k] <- reject[k] + 1 dataTrialStop[index] <- TRUE + dataReject[index] <- 1 index <- index + 1 break } if (zValue < design$futilityBounds[k] && k < kMax) { futility[k] <- futility[k] + 1 dataTrialStop[index] <- TRUE + dataFutility[index] <- 1 index <- index + 1 break } @@ -584,6 +593,7 @@ getSimulationCounts <- function(design = NULL, simulationResults$iterations <- iterations simulationResults$.setParameterType("iterations", C_PARAM_GENERATED) + sampleSizePerStage[is.nan(sampleSizePerStage)] <- NA_integer_ simulationResults$numberOfSubjects <- sampleSizePerStage simulationResults$.setParameterType( "numberOfSubjects", @@ -615,20 +625,22 @@ getSimulationCounts <- function(design = NULL, } data <- data.frame( - caseNumber = dataCase, iterationNumber = dataIterationNumber, stageNumber = dataStageNumber, - accrualTime = dataAccrualTime, - followUpTime = dataFollowUpTime, lambda1 = dataLambda1, lambda2 = dataLambda2, overdispersion = dataOverdispersion, + accrualTime = dataAccrualTime, + followUpTime = dataFollowUpTime, fixedFollowUp = dataFixedFollowUp, - negativeBinomialEstimate1 = dataNegativeBinomialEstimate1, - negativeBinomialEstimate2 = dataNegativeBinomialEstimate2, - nbOverdispersion = dataNegativeBinomialOverdispersion, - infoAnalysis = dataInfoAnalysis, + numberOfSubjects = dataSampleSizes, + rejectPerStage = dataReject, + futilityPerStage = dataFutility, testStatistic = dataTestStatistic, + estimatedLambda1 = dataNegativeBinomialEstimate1, + estimatedLambda2 = dataNegativeBinomialEstimate2, + estimatedOverdispersion = dataNegativeBinomialOverdispersion, + infoAnalysis = dataInfoAnalysis, trialStop = dataTrialStop, conditionalPowerAchieved = dataConditionalPower ) diff --git a/man-roxygen/examples_get_simulation_counts.R b/man-roxygen/examples_get_simulation_counts.R index c9bf7870..1ff7883b 100644 --- a/man-roxygen/examples_get_simulation_counts.R +++ b/man-roxygen/examples_get_simulation_counts.R @@ -18,19 +18,19 @@ #' # equidistant time points (lambda1, lambda2, and overdispersion as specified, #' # no futility stopping): #' dOF <- getDesignGroupSequential( -#' kMax = 3, -#' alpha = 0.025, -#' beta = 0.2, +#' kMax = 3, +#' alpha = 0.025, +#' beta = 0.2, #' typeOfDesign = "asOF") -#' +#' #' getSimulationCounts(design = dOF, -#' lambda1 = seq(0.04, 0.12, 0.02), -#' lambda2 = 0.12, -#' directionUpper = FALSE, -#' overdispersion = 5, +#' lambda1 = seq(0.04, 0.12, 0.02), +#' lambda2 = 0.12, +#' directionUpper = FALSE, +#' overdispersion = 5, #' plannedCalendarTime = (1:3)/3*4, -#' maxNumberOfSubjects = 264, -#' followUpTime = 2.75, +#' maxNumberOfSubjects = 264, +#' followUpTime = 2.75, #' accrualTime = 1.25, #' maxNumberOfIterations = 1000) #' } diff --git a/man/getSimulationCounts.Rd b/man/getSimulationCounts.Rd index bf1213ee..d6e31dd5 100644 --- a/man/getSimulationCounts.Rd +++ b/man/getSimulationCounts.Rd @@ -132,21 +132,22 @@ object as \code{\link[base]{data.frame}}. The data frame contains the following \enumerate{ \item \code{iterationNumber}: The number of the simulation iteration. \item \code{stageNumber}: The stage. -\item \code{lambda1}: The assumed or derived event rate in the treatment group (if available). -\item \code{lambda2}: The assumed or derived event rate in the control group (if available). -\item \code{numberOfSubjects}: The number of subjects under consideration when the -(interim) analysis takes place. +\item \code{lambda1}: The assumed or derived event rate in the treatment group. +\item \code{lambda2}: The assumed or derived event rate in the control group. +\item \code{accrualTime}: The assumed accrualTime. +\item \code{followUpTime}: The assumed followUpTime. +\item \code{overdispersion}: The assumed overdispersion. +\item \code{fixedFollowUp}: The assumed fixedFollowUp. +\item \code{numberOfSubjects}: The number of subjects under consideration when the (interim) analysis takes place. \item \code{rejectPerStage}: 1 if null hypothesis can be rejected, 0 otherwise. \item \code{futilityPerStage}: 1 if study should be stopped for futility, 0 otherwise. \item \code{testStatistic}: The test statistic that is used for the test decision -\item \code{overallLambda1}: The cumulative rate in treatment group 1. -\item \code{overallLambda2}: The cumulative rate in treatment group 2. -\item \code{sampleSizesPerStage1}: The stage-wise sample size in treatment group 1. -\item \code{sampleSizesPerStage2}: The stage-wise sample size in treatment group 2. +\item \code{estimatedLambda1}: The estimated rate in treatment group 1. +\item \code{estimatedLambda2}: The estimated rate in treatment group 2. +\item \code{estimatedOverdispersion}: The estimated overdispersion. +\item \code{infoAnalysis}: The Fisher information at interim stage. \item \code{trialStop}: \code{TRUE} if study should be stopped for efficacy or futility or final stage, \code{FALSE} otherwise. -\item \code{conditionalPowerAchieved}: The conditional power for the subsequent stage of the trial for -selected sample size and effect. The effect is either estimated from the data or can be -user defined with \code{lambda1H1} and \code{lambda2H1}. +\item \code{conditionalPowerAchieved}: Not yet available } } @@ -181,19 +182,19 @@ getSimulationCounts( # equidistant time points (lambda1, lambda2, and overdispersion as specified, # no futility stopping): dOF <- getDesignGroupSequential( - kMax = 3, - alpha = 0.025, - beta = 0.2, + kMax = 3, + alpha = 0.025, + beta = 0.2, typeOfDesign = "asOF") getSimulationCounts(design = dOF, - lambda1 = seq(0.04, 0.12, 0.02), - lambda2 = 0.12, - directionUpper = FALSE, - overdispersion = 5, + lambda1 = seq(0.04, 0.12, 0.02), + lambda2 = 0.12, + directionUpper = FALSE, + overdispersion = 5, plannedCalendarTime = (1:3)/3*4, - maxNumberOfSubjects = 264, - followUpTime = 2.75, + maxNumberOfSubjects = 264, + followUpTime = 2.75, accrualTime = 1.25, maxNumberOfIterations = 1000) }