diff --git a/DESCRIPTION b/DESCRIPTION
index f4fa1821..4ced1a8f 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
Package: simtrial
Type: Package
Title: Clinical Trial Simulation
-Version: 0.3.0.9
+Version: 0.3.0.10
Authors@R: c(
person("Keaven", "Anderson", email = "keaven_anderson@merck.com", role = c("aut")),
person("Yilong", "Zhang", email = "elong0527@gmail.com", role = c("aut")),
diff --git a/NAMESPACE b/NAMESPACE
index b2aebbdf..c9b345fe 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -12,9 +12,7 @@ export(mb_weight)
export(pvalue_maxcombo)
export(randomize_by_fixed_block)
export(rpwexp)
-export(rpwexpRcpp)
export(rpwexp_enroll)
-export(rpwexpinvRcpp)
export(sim_fixed_n)
export(sim_pw_surv)
export(to_sim_pw_surv)
diff --git a/R/RcppExports.R b/R/RcppExports.R
index 8b15b3eb..4e2df0c4 100644
--- a/R/RcppExports.R
+++ b/R/RcppExports.R
@@ -1,23 +1,25 @@
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
-#' The piecewise exponential distribution in C++
+#' The piecewise exponential distribution using the inverse CDF method
#'
#' @param n Number of observations to be generated.
#' @param fail_rate A data frame containing `duration` and `rate` variables.
#'
-#' @export
-rpwexpRcpp <- function(n, fail_rate) {
- .Call(`_simtrial_rpwexpRcpp`, n, fail_rate)
+#' @noRd
+#'
+rpwexp_inverse_cdf_cpp <- function(n, fail_rate) {
+ .Call(`_simtrial_rpwexp_inverse_cdf_cpp`, n, fail_rate)
}
-#' The piecewise exponential distribution using inverse CDF method in C++
+#' The piecewise exponential distribution using the naive method
#'
#' @param n Number of observations to be generated.
#' @param fail_rate A data frame containing `duration` and `rate` variables.
#'
-#' @export
-rpwexpinvRcpp <- function(n, fail_rate) {
- .Call(`_simtrial_rpwexpinvRcpp`, n, fail_rate)
+#' @noRd
+#'
+rpwexp_naive_cpp <- function(n, fail_rate) {
+ .Call(`_simtrial_rpwexp_naive_cpp`, n, fail_rate)
}
diff --git a/R/rpwexp.R b/R/rpwexp.R
index 960b1843..fd5a5932 100644
--- a/R/rpwexp.R
+++ b/R/rpwexp.R
@@ -70,51 +70,5 @@
rpwexp <- function(
n = 100,
fail_rate = data.frame(duration = c(1, 1), rate = c(10, 20))) {
- n_rate <- nrow(fail_rate)
-
- if (n_rate == 1) {
- # Set failure time to Inf if 0 failure rate
- if (fail_rate$rate == 0) {
- ans <- rep(Inf, n)
- } else {
- # Generate exponential failure time if non-0 failure rate
- ans <- stats::rexp(n, fail_rate$rate)
- }
- } else {
- # Start of first failure rate interval
- start_time <- 0
- # Ends of failure rate interval
- end_time <- cumsum(fail_rate$duration)
- # Initiate vector for failure times
- ans <- rep(0, n)
- # Index for event times not yet reached
- indx <- rep(TRUE, n)
-
- for (i in 1:n_rate) {
- # Number of event times left to generate
- n_event_left <- sum(indx)
-
- # Stop if event is arrived
- if (n_event_left == 0) {
- break
- }
-
- # Set failure time to Inf for interval i if 0 fail rate
- if (fail_rate$rate[i] == 0) {
- ans[indx] <- start_time + rep(Inf, n_event_left)
- } else {
- # Generate exponential failure time for interval i if non-0 failure rate
- ans[indx] <- start_time + stats::rexp(n_event_left, fail_rate$rate[i])
- }
-
- # Skip this for last interval as all remaining times are generated there
- if (i < n_rate) {
- start_time <- end_time[i]
- # Update index of event times not yet reached
- indx <- (ans > end_time[i])
- }
- }
- }
-
- ans
+ rpwexp_inverse_cdf_cpp(n = n, fail_rate = fail_rate)
}
diff --git a/R/rpwexp_naive.R b/R/rpwexp_naive.R
new file mode 100644
index 00000000..64b83d3f
--- /dev/null
+++ b/R/rpwexp_naive.R
@@ -0,0 +1,120 @@
+# Copyright (c) 2023 Merck & Co., Inc., Rahway, NJ, USA and its affiliates.
+# All rights reserved.
+#
+# This file is part of the simtrial program.
+#
+# simtrial is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+#' The piecewise exponential distribution
+#'
+#' The piecewise exponential distribution allows a simple method to specify
+#' a distribution where the hazard rate changes over time.
+#' It is likely to be useful for conditions where failure rates change,
+#' but also for simulations where there may be a delayed treatment effect
+#' or a treatment effect that that is otherwise changing
+#' (for example, decreasing) over time.
+#' `rpwexp_naive()` is to support simulation of both the Lachin and Foulkes (1986)
+#' sample size method for (fixed trial duration) as well as the
+#' Kim and Tsiatis (1990) method (fixed enrollment rates and either
+#' fixed enrollment duration or fixed minimum follow-up);
+#' see [gsDesign::nSurv()].
+#'
+#' Using the `cumulative = TRUE` option, enrollment times that piecewise
+#' constant over time can be generated.
+#'
+#' @param n Number of observations to be generated.
+#' @param fail_rate A data frame containing `duration` and `rate` variables.
+#' `rate` specifies failure rates during the corresponding interval duration
+#' specified in `duration`. The final interval is extended to be infinite
+#' to ensure all observations are generated.
+#'
+#' @noRd
+#'
+#' @examples
+#' # Example 1
+#' # Exponential failure times
+#' x <- rpwexp_naive(
+#' n = 10000,
+#' fail_rate = data.frame(rate = 5, duration = 1)
+#' )
+#' plot(sort(x), (10000:1) / 10001,
+#' log = "y", main = "Exponential simulated survival curve",
+#' xlab = "Time", ylab = "P{Survival}"
+#' )
+#'
+#' # Example 2
+#'
+#' # Get 10k piecewise exponential failure times.
+#' # Failure rates are 1 for time 0 to 0.5, 3 for time 0.5 to 1, and 10 for > 1.
+#' # Intervals specifies duration of each failure rate interval
+#' # with the final interval running to infinity.
+#' x <- rpwexp_naive(
+#' n = 1e4,
+#' fail_rate = data.frame(rate = c(1, 3, 10), duration = c(.5, .5, 1))
+#' )
+#' plot(sort(x), (1e4:1) / 10001,
+#' log = "y", main = "PW Exponential simulated survival curve",
+#' xlab = "Time", ylab = "P{Survival}"
+#' )
+rpwexp_naive <- function(
+ n = 100,
+ fail_rate = data.frame(duration = c(1, 1), rate = c(10, 20))) {
+ n_rate <- nrow(fail_rate)
+
+ if (n_rate == 1) {
+ # Set failure time to Inf if 0 failure rate
+ if (fail_rate$rate == 0) {
+ ans <- rep(Inf, n)
+ } else {
+ # Generate exponential failure time if non-0 failure rate
+ ans <- stats::rexp(n, fail_rate$rate)
+ }
+ } else {
+ # Start of first failure rate interval
+ start_time <- 0
+ # Ends of failure rate interval
+ end_time <- cumsum(fail_rate$duration)
+ # Initiate vector for failure times
+ ans <- rep(0, n)
+ # Index for event times not yet reached
+ indx <- rep(TRUE, n)
+
+ for (i in 1:n_rate) {
+ # Number of event times left to generate
+ n_event_left <- sum(indx)
+
+ # Stop if event is arrived
+ if (n_event_left == 0) {
+ break
+ }
+
+ # Set failure time to Inf for interval i if 0 fail rate
+ if (fail_rate$rate[i] == 0) {
+ ans[indx] <- start_time + rep(Inf, n_event_left)
+ } else {
+ # Generate exponential failure time for interval i if non-0 failure rate
+ ans[indx] <- start_time + stats::rexp(n_event_left, fail_rate$rate[i])
+ }
+
+ # Skip this for last interval as all remaining times are generated there
+ if (i < n_rate) {
+ start_time <- end_time[i]
+ # Update index of event times not yet reached
+ indx <- (ans > end_time[i])
+ }
+ }
+ }
+
+ ans
+}
diff --git a/R/sim_pw_surv.R b/R/sim_pw_surv.R
index 70dde971..0c744b2a 100644
--- a/R/sim_pw_surv.R
+++ b/R/sim_pw_surv.R
@@ -174,11 +174,11 @@ sim_pw_surv <- function(
for (sr in unique_stratum) {
for (tr in unique_treatment) {
indx <- x$stratum == sr & x$treatment == tr
- x$fail_time[indx] <- rpwexpinvRcpp(
+ x$fail_time[indx] <- rpwexp(
n = sum(indx),
fail_rate = fail_rate[fail_rate$stratum == sr & fail_rate$treatment == tr, , drop = FALSE]
)
- x$dropout_time[indx] <- rpwexpinvRcpp(
+ x$dropout_time[indx] <- rpwexp(
n = sum(indx),
fail_rate = dropout_rate[dropout_rate$stratum == sr & dropout_rate$treatment == tr, , drop = FALSE]
)
diff --git a/_pkgdown.yml b/_pkgdown.yml
index 38308c6e..c7e828f1 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -27,8 +27,6 @@ reference:
- title: "Distributions"
contents:
- rpwexp
- - rpwexpRcpp
- - rpwexpinvRcpp
- rpwexp_enroll
- fit_pwexp
diff --git a/man/rpwexpRcpp.Rd b/man/rpwexpRcpp.Rd
deleted file mode 100644
index 0cee78c8..00000000
--- a/man/rpwexpRcpp.Rd
+++ /dev/null
@@ -1,16 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/RcppExports.R
-\name{rpwexpRcpp}
-\alias{rpwexpRcpp}
-\title{The piecewise exponential distribution in C++}
-\usage{
-rpwexpRcpp(n, fail_rate)
-}
-\arguments{
-\item{n}{Number of observations to be generated.}
-
-\item{fail_rate}{A data frame containing \code{duration} and \code{rate} variables.}
-}
-\description{
-The piecewise exponential distribution in C++
-}
diff --git a/man/rpwexpinvRcpp.Rd b/man/rpwexpinvRcpp.Rd
deleted file mode 100644
index 7335fa0b..00000000
--- a/man/rpwexpinvRcpp.Rd
+++ /dev/null
@@ -1,16 +0,0 @@
-% Generated by roxygen2: do not edit by hand
-% Please edit documentation in R/RcppExports.R
-\name{rpwexpinvRcpp}
-\alias{rpwexpinvRcpp}
-\title{The piecewise exponential distribution using inverse CDF method in C++}
-\usage{
-rpwexpinvRcpp(n, fail_rate)
-}
-\arguments{
-\item{n}{Number of observations to be generated.}
-
-\item{fail_rate}{A data frame containing \code{duration} and \code{rate} variables.}
-}
-\description{
-The piecewise exponential distribution using inverse CDF method in C++
-}
diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp
index bfcc80ca..64160634 100644
--- a/src/RcppExports.cpp
+++ b/src/RcppExports.cpp
@@ -10,34 +10,34 @@ Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get();
Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif
-// rpwexpRcpp
-NumericVector rpwexpRcpp(int n, DataFrame fail_rate);
-RcppExport SEXP _simtrial_rpwexpRcpp(SEXP nSEXP, SEXP fail_rateSEXP) {
+// rpwexp_inverse_cdf_cpp
+NumericVector rpwexp_inverse_cdf_cpp(int n, DataFrame fail_rate);
+RcppExport SEXP _simtrial_rpwexp_inverse_cdf_cpp(SEXP nSEXP, SEXP fail_rateSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< int >::type n(nSEXP);
Rcpp::traits::input_parameter< DataFrame >::type fail_rate(fail_rateSEXP);
- rcpp_result_gen = Rcpp::wrap(rpwexpRcpp(n, fail_rate));
+ rcpp_result_gen = Rcpp::wrap(rpwexp_inverse_cdf_cpp(n, fail_rate));
return rcpp_result_gen;
END_RCPP
}
-// rpwexpinvRcpp
-NumericVector rpwexpinvRcpp(int n, DataFrame fail_rate);
-RcppExport SEXP _simtrial_rpwexpinvRcpp(SEXP nSEXP, SEXP fail_rateSEXP) {
+// rpwexp_naive_cpp
+NumericVector rpwexp_naive_cpp(int n, DataFrame fail_rate);
+RcppExport SEXP _simtrial_rpwexp_naive_cpp(SEXP nSEXP, SEXP fail_rateSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< int >::type n(nSEXP);
Rcpp::traits::input_parameter< DataFrame >::type fail_rate(fail_rateSEXP);
- rcpp_result_gen = Rcpp::wrap(rpwexpinvRcpp(n, fail_rate));
+ rcpp_result_gen = Rcpp::wrap(rpwexp_naive_cpp(n, fail_rate));
return rcpp_result_gen;
END_RCPP
}
static const R_CallMethodDef CallEntries[] = {
- {"_simtrial_rpwexpRcpp", (DL_FUNC) &_simtrial_rpwexpRcpp, 2},
- {"_simtrial_rpwexpinvRcpp", (DL_FUNC) &_simtrial_rpwexpinvRcpp, 2},
+ {"_simtrial_rpwexp_inverse_cdf_cpp", (DL_FUNC) &_simtrial_rpwexp_inverse_cdf_cpp, 2},
+ {"_simtrial_rpwexp_naive_cpp", (DL_FUNC) &_simtrial_rpwexp_naive_cpp, 2},
{NULL, NULL, 0}
};
diff --git a/src/rpwexpRcpp.cpp b/src/rpwexpRcpp.cpp
deleted file mode 100644
index 6e752aa5..00000000
--- a/src/rpwexpRcpp.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#include
-#include
-using namespace Rcpp;
-
-//' The piecewise exponential distribution in C++
-//'
-//' @param n Number of observations to be generated.
-//' @param fail_rate A data frame containing `duration` and `rate` variables.
-//'
-//' @export
-// [[Rcpp::export]]
-NumericVector rpwexpRcpp(int n,
- DataFrame fail_rate) {
- NumericVector duration = fail_rate["duration"];
- NumericVector rate = fail_rate["rate"];
- int n_rates = duration.size();
-
- // Initialize failure times to Inf
- NumericVector times(n, R_PosInf);
-
- if (n_rates == 1) {
- if (rate[0] != 0) {
- times = rexp(n, rate[0]); // generate exponential failure time if non-0 failure rate
- }
- } else {
- double starttime = 0; // start of first failure rate interval
- LogicalVector indx(n, 1); // index for event times not yet reached
- int nindx = n; // number of event times left to generate
-
- for (int i = 0; i < n_rates; i++) {
- if (is_false(any(indx))) break; // stop if finished
- if (rate[i] == 0) {
- NumericVector temp(nindx, R_PosInf); // set failure time to Inf for inveral i if 0 fail rate
- times[indx] = temp;
- } else {
- NumericVector temp = rexp(nindx, rate[i]); // generate exponential failure time for interval i if non-0 faiurel rate
- int p = 0;
- for (int j = 0; j < n; j++) {
- if (indx[j]) times[j] = temp[p++] + starttime;
- }
- }
-
- if (i < n_rates) { // skip this for last interval as all remaining times are generated there
- starttime += duration[i]; // update start time for next interval
- for (int j = 0; j < n; j++) {
- if (indx[j] == 1 && times[j] <= starttime) {
- indx[j] = 0; // update index of event times not yet reached
- nindx--; // update number of event times left to generate
- }
- }
- }
-
- }
- }
-
- return times;
-}
diff --git a/src/rpwexpinvRcpp.cpp b/src/rpwexp_inverse_cdf.cpp
similarity index 69%
rename from src/rpwexpinvRcpp.cpp
rename to src/rpwexp_inverse_cdf.cpp
index 68976719..569a9833 100644
--- a/src/rpwexpinvRcpp.cpp
+++ b/src/rpwexp_inverse_cdf.cpp
@@ -3,16 +3,16 @@
#include
using namespace Rcpp;
-//' The piecewise exponential distribution using inverse CDF method in C++
+//' The piecewise exponential distribution using the inverse CDF method
//'
//' @param n Number of observations to be generated.
//' @param fail_rate A data frame containing `duration` and `rate` variables.
//'
-//' @export
+//' @noRd
+//'
// [[Rcpp::export]]
-NumericVector rpwexpinvRcpp(int n,
- DataFrame fail_rate) {
-
+NumericVector rpwexp_inverse_cdf_cpp(int n, DataFrame fail_rate)
+{
NumericVector duration = fail_rate["duration"];
NumericVector rate = fail_rate["rate"];
int n_rates = duration.size();
@@ -23,14 +23,16 @@ NumericVector rpwexpinvRcpp(int n,
// Get number of piecewise rates
NumericVector cumTime(n_rates);
NumericVector cumHaz(n_rates);
- for (int i = 1; i < n_rates; i++) {
- cumTime[i] = cumTime[i-1] + duration[i-1];
- cumHaz[i] = cumHaz[i-1] + duration[i-1] * rate[i-1];
+ for (int i = 1; i < n_rates; i++)
+ {
+ cumTime[i] = cumTime[i - 1] + duration[i - 1];
+ cumHaz[i] = cumHaz[i - 1] + duration[i - 1] * rate[i - 1];
}
NumericVector::iterator pos;
int j;
- for (int i = 0; i < n; i++) {
+ for (int i = 0; i < n; i++)
+ {
pos = std::upper_bound(cumHaz.begin(), cumHaz.end(), times[i]);
j = pos - cumHaz.begin() - 1;
times[i] = cumTime[j] + (times[i] - cumHaz[j]) / rate[j];
diff --git a/src/rpwexp_naive.cpp b/src/rpwexp_naive.cpp
new file mode 100644
index 00000000..de73a841
--- /dev/null
+++ b/src/rpwexp_naive.cpp
@@ -0,0 +1,71 @@
+#include
+#include
+using namespace Rcpp;
+
+//' The piecewise exponential distribution using the naive method
+//'
+//' @param n Number of observations to be generated.
+//' @param fail_rate A data frame containing `duration` and `rate` variables.
+//'
+//' @noRd
+//'
+// [[Rcpp::export]]
+NumericVector rpwexp_naive_cpp(int n, DataFrame fail_rate)
+{
+ NumericVector duration = fail_rate["duration"];
+ NumericVector rate = fail_rate["rate"];
+ int n_rates = duration.size();
+
+ // Initialize failure times to Inf
+ NumericVector times(n, R_PosInf);
+
+ if (n_rates == 1)
+ {
+ if (rate[0] != 0)
+ {
+ times = rexp(n, rate[0]); // Generate exponential failure time if non-zero failure rate
+ }
+ }
+ else
+ {
+ double starttime = 0; // Start of first failure rate interval
+ LogicalVector indx(n, 1); // Index for event times not yet reached
+ int nindx = n; // Number of event times left to generate
+
+ for (int i = 0; i < n_rates; i++)
+ {
+ if (is_false(any(indx)))
+ break; // stop if finished
+ if (rate[i] == 0)
+ {
+ NumericVector temp(nindx, R_PosInf); // Set failure time to Inf for interval i if 0 fail rate
+ times[indx] = temp;
+ }
+ else
+ {
+ NumericVector temp = rexp(nindx, rate[i]); // Generate exponential failure time for interval i if non-0 failure rate
+ int p = 0;
+ for (int j = 0; j < n; j++)
+ {
+ if (indx[j])
+ times[j] = temp[p++] + starttime;
+ }
+ }
+
+ if (i < n_rates)
+ { // Skip this for last interval as all remaining times are generated there
+ starttime += duration[i]; // Update start time for next interval
+ for (int j = 0; j < n; j++)
+ {
+ if (indx[j] == 1 && times[j] <= starttime)
+ {
+ indx[j] = 0; // Update index of event times not yet reached
+ nindx--; // Update number of event times left to generate
+ }
+ }
+ }
+ }
+ }
+
+ return times;
+}
diff --git a/tests/testthat/test-independent_test_rpwexpinvRcpp.R b/tests/testthat/test-independent_test_rpwexp_inverse_cdf_cpp.R
similarity index 62%
rename from tests/testthat/test-independent_test_rpwexpinvRcpp.R
rename to tests/testthat/test-independent_test_rpwexp_inverse_cdf_cpp.R
index 2f1879dd..0bd56680 100644
--- a/tests/testthat/test-independent_test_rpwexpinvRcpp.R
+++ b/tests/testthat/test-independent_test_rpwexp_inverse_cdf_cpp.R
@@ -1,6 +1,6 @@
-test_that("rpwexpinvRcpp handles 0 fail_rate for final period", {
+test_that("rpwexp_inverse_cdf_cpp handles 0 fail_rate for final period", {
# 0 failure rate for last period
- s <- simtrial::rpwexpinvRcpp(
+ s <- simtrial:::rpwexp_inverse_cdf_cpp(
n = 100,
fail_rate = data.frame(duration = c(0, 2), rate = c(5, 0))
)
@@ -8,9 +8,9 @@ test_that("rpwexpinvRcpp handles 0 fail_rate for final period", {
testthat::expect_equal(min(s), Inf)
})
-testthat::test_that("rpwexpinvRcpp handles 0 fail rate properly for one period", {
+testthat::test_that("rpwexp_inverse_cdf_cpp handles 0 fail rate properly for one period", {
# 0 failure rate
- s <- simtrial::rpwexpinvRcpp(
+ s <- simtrial:::rpwexp_inverse_cdf_cpp(
n = 100,
fail_rate = data.frame(duration = c(1), rate = c(0))
)
@@ -19,9 +19,9 @@ testthat::test_that("rpwexpinvRcpp handles 0 fail rate properly for one period",
})
-testthat::test_that("rpwexpinvRcpp handles 0 fail rate properly for multiple periods", {
+testthat::test_that("rpwexp_inverse_cdf_cpp handles 0 fail rate properly for multiple periods", {
# 0 failure rate for 1st period (with duration of 1 time unit)
- s <- simtrial::rpwexpinvRcpp(
+ s <- simtrial:::rpwexp_inverse_cdf_cpp(
n = 100,
fail_rate = data.frame(duration = c(1, 2), rate = c(0, 5))
)