Skip to content

Commit f6d1606

Browse files
committed
docs: mention sim_discrete_event() throughout package documentation
feat: more tests for sim_discrete_event()
1 parent 5f8e0cc commit f6d1606

16 files changed

Lines changed: 191 additions & 50 deletions

R/input_checks.r

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,15 @@ check_inputs_sim_discrete_time <- function(n_sim, dag, t0_sort_dag,
623623
stopifnot("'save_states' must be either 'last', 'all' or 'at_t'." =
624624
is.element(save_states, c("last", "all", "at_t")))
625625
}
626+
627+
# no next_time nodes allowed
628+
types <- vapply(tx_nodes, FUN=function(x){x$type_str},
629+
FUN.VALUE=character(1))
630+
if ("next_time" %in% types) {
631+
stop("Nodes of type='next_time' are not allowed in sim_discrete_time()",
632+
". Use sim_discrete_event() instead or re-define the DAG.",
633+
call.=FALSE)
634+
}
626635
}
627636

628637
## checking the inputs of the node_time_to_event function
@@ -885,7 +894,7 @@ check_inputs_sim_discrete_event <- function(dag, n_sim, t0_sort_dag,
885894
names_args <- names(t0_transform_args)
886895
if (!all(names_args %in% names_fun)) {
887896
stop("The following arguments are in 't0_transform_args' but are",
888-
" not define in 't0_transform_fun': ",
897+
" not defined in 't0_transform_fun': ",
889898
paste0(names_args[!names_args %in% names_fun], collapse=","),
890899
call.=FALSE)
891900
}

R/sim_discrete_event.r

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#' @importFrom data.table copy
55
#' @importFrom data.table melt.data.table
66
#' @importFrom data.table merge.data.table
7+
#' @importFrom data.table as.data.table
78
#' @importFrom data.table setkey
89
#' @importFrom data.table rbindlist
910
#' @importFrom data.table fifelse
@@ -78,7 +79,7 @@ sim_discrete_event <- function(dag, n_sim=NULL, t0_sort_dag=FALSE,
7879
check_inputs=check_inputs)
7980
data[, .id := seq(1, n_sim)]
8081
} else {
81-
data <- data.table::setDT(t0_data)
82+
data <- as.data.table(t0_data)
8283
n_sim <- nrow(data)
8384
data[, .id := seq(1, n_sim)]
8485
}
@@ -209,7 +210,6 @@ sim_discrete_event <- function(dag, n_sim=NULL, t0_sort_dag=FALSE,
209210
out[[length(out) + 1]] <- data[!duplicated(data$.id), cnames, with=FALSE]
210211

211212
# remove rows that no longer need to be updated
212-
213213
data <- data[!(is.infinite(.event_duration) & .is_new_event==TRUE) &
214214
!(is.infinite(.immunity_duration) & .is_new_change==TRUE) &
215215
.time < max_t]
@@ -349,7 +349,7 @@ timecuts <- function(n, rate, l, cuts) {
349349
}
350350

351351
## this function efficiently either sets the covariate values to TRUE if
352-
## a new event occured or sets them back to FALSE if needed
352+
## a new event occurred or sets them back to FALSE if needed
353353
set_cols_to_value <- function(data, .value, type, var_names, allow_ties) {
354354

355355
.is_new_event <- .kind <- .id <- .is_new_change <- .time <-

R/sim_n_datasets.r

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@ sim_n_datasets <- function(dag, n_sim, n_repeats, n_cores=1,
1313
data_format_args=data_format_args,
1414
progressbar=progressbar)
1515

16+
# select which function should be used
17+
if (length(dag$tx_nodes) > 0) {
18+
types <- vapply(dag$tx_nodes, FUN=function(x){x$type_str},
19+
FUN.VALUE=character(1))
20+
if ("next_time" %in% types) {
21+
sim <- "DES"
22+
} else {
23+
sim <- "DTS"
24+
}
25+
} else {
26+
sim <- "DAG"
27+
}
28+
29+
if (sim=="DES" & data_format %in% c("long", "wide")) {
30+
warning("Only the 'start_stop' format is supported with discrete-event",
31+
" simulations. No data transformation was carried out.",
32+
call.=FALSE)
33+
}
34+
1635
# without parallel processing
1736
if (n_cores==1) {
1837

@@ -21,7 +40,7 @@ sim_n_datasets <- function(dag, n_sim, n_repeats, n_cores=1,
2140
for (i in seq_len(n_repeats)) {
2241
out[[i]] <- generate_one_dataset(dag=dag, data_format=data_format,
2342
data_format_args=data_format_args,
24-
n_sim=n_sim, ...)
43+
n_sim=n_sim, sim=sim, ...)
2544
}
2645

2746
# with parallel processing
@@ -69,7 +88,7 @@ sim_n_datasets <- function(dag, n_sim, n_repeats, n_cores=1,
6988

7089
generate_one_dataset(dag=dag, data_format=data_format,
7190
data_format_args=data_format_args,
72-
n_sim=n_sim, ...)
91+
n_sim=n_sim, sim=sim, ...)
7392
}
7493
on.exit(close(pb))
7594
on.exit(parallel::stopCluster(cl))
@@ -81,23 +100,28 @@ sim_n_datasets <- function(dag, n_sim, n_repeats, n_cores=1,
81100
## generate one dataset using either the sim_from_dag() or the
82101
## sim_discrete_time() function and optionally format it
83102
generate_one_dataset <- function(dag, data_format, data_format_args, n_sim,
84-
...) {
103+
sim, ...) {
85104

86105
# simulate the dataset
87-
if (length(dag$tx_nodes) > 0) {
106+
if (sim=="DTS") {
88107
dat <- sim_discrete_time(dag=dag, n_sim=n_sim, ...)
108+
} else if (sim=="DES") {
109+
dat <- sim_discrete_event(dag=dag, n_sim=n_sim, ...)
89110
} else {
90111
dat <- sim_from_dag(dag=dag, n_sim=n_sim, ...)
91112
}
92113

93114
# transform it, if specified
94115
if (data_format %in% c("start_stop", "long", "wide") &&
95-
length(dag$tx_nodes) > 0) {
116+
sim=="DTS") {
96117

97118
data_format_args$to <- data_format
98119
data_format_args$sim <- dat
99120
dat <- do.call("sim2data", args=data_format_args)
100121

122+
} else if (data_format %in% c("start_stop", "long", "wide") &&
123+
sim=="DES") {
124+
# do nothing
101125
} else if (data_format != "raw") {
102126
data_format_args$data <- dat
103127
dat <- do.call(data_format, args=data_format_args)

TODOs.txt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@ sim_discrete_event:
88

99
General:
1010
- write Vignette
11-
- update documentation throughout the package
12-
- add this function to sim_n_datasets()
1311

1412
missing features:
1513
- allow formula
1614
- allow network dependencies
1715
- change how .event_count works, to allow the amount of previous events to influence other events
18-
- remove_not_at_risk argument
16+
- add remove_not_at_risk argument
1917
- allow internal other distr_fun (cox, aft models, ...)
2018

2119
####### New Features: #######

man/node.Rd

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
Create a node object for a \code{DAG}
77
}
88
\description{
9-
These functions should be used in conjunction with the \code{\link{empty_dag}} function to create \code{DAG} objects, which can then be used to simulate data using the \code{\link{sim_from_dag}} function or the \code{\link{sim_discrete_time}} function.
9+
These functions should be used in conjunction with the \code{\link{empty_dag}} function to create \code{DAG} objects, which can then be used to simulate data using the \code{\link{sim_from_dag}}, \code{\link{sim_discrete_time}} or \code{\link{sim_discrete_event}} functions.
1010
}
1111
\usage{
1212
node(name, type, parents=NULL, formula=NULL, ...)
@@ -32,28 +32,29 @@ Further named arguments needed to specify the node. Those can be parameters of d
3232
}
3333
\details{
3434

35-
To generate data using the \code{\link{sim_from_dag}} function or the \code{\link{sim_discrete_time}} function, it is required to create a \code{DAG} object first. This object needs to contain information about the causal structure of the data (e.g. which variable causes which variable) and the specific structural equations for each variable (information about causal coefficients, type of distribution etc.). In this package, the \code{node} and/or \code{node_td} function is used in conjunction with the \code{\link{empty_dag}} function to create this object.
35+
To generate data using the \code{\link{sim_from_dag}}, \code{\link{sim_discrete_time}} or \code{\link{sim_discrete_event}} functions, it is required to create a \code{DAG} object first. This object needs to contain information about the causal structure of the data (e.g. which variable causes which variable) and the specific structural equations for each variable (information about causal coefficients, type of distribution etc.). In this package, the \code{node} and/or \code{node_td} functions are used in conjunction with the \code{\link{empty_dag}} function to create this object.
3636

3737
This works by first initializing an empty \code{DAG} using the \code{\link{empty_dag}} function and then adding multiple calls to the \code{node} and/or \code{node_td} functions to it using a simple \code{+}, where each call to \code{node} and/or \code{node_td} adds information about a single node that should be generated. Multiple examples are given below.
3838

3939
In each call to \code{node} or \code{node_td} the user needs to indicate what the node should be called (\code{name}), which function should be used to generate the node (\code{type}), whether the node has any parents and if so which (\code{parents}) and any additional arguments needed to actually call the data-generating function of this node later passed to the three-dot syntax (\code{...}).
4040

4141
\strong{\emph{\code{node} vs. \code{node_td}}}:
4242

43-
By calling \code{node} you are indicating that this node is a time-fixed variable which should only be generated once. By using \code{node_td} you are indicating that it is a time-dependent node, which will be updated at each step in time when using a discrete-time simulation.
43+
By calling \code{node} you are indicating that this node is a time-fixed variable which should only be generated once. By using \code{node_td} you are indicating that it is a time-dependent node, which will be updated at each step in time when using a discrete-time simulation, or at event changes in discrete-event simulations.
4444

45-
\code{node_td} should only be used if you are planning to perform a discrete-time simulation with the \code{\link{sim_discrete_time}} function. \code{DAG} objects including time-dependent nodes may not be used in the \code{\link{sim_from_dag}} function.
45+
\code{node_td} should only be used if you are planning to perform a discrete-time or discrete-event simulation with the \code{\link{sim_discrete_time}} or \code{\link{sim_discrete_event}} functions. \code{DAG} objects including time-dependent nodes may not be used in the \code{\link{sim_from_dag}} function.
4646

4747
\strong{\emph{Implemented Root Node Types}}:
4848

4949
Any function can be used to generate root nodes. The only requirement is that the function has at least one named argument called \code{n} which controls the length of the resulting vector. For example, the user could specify a node of type \code{"rnorm"} to create a normally distributed node with no parents. The argument \code{n} will be set internally, but any additional arguments can be specified using the \code{...} syntax. In the \code{type="rnorm"} example, the user could set the mean and standard deviation using \code{node(name="example", type="rnorm", mean=10, sd=5)}.
5050

51-
For convenience, this package additionally includes four custom root-node functions:
51+
For convenience, this package additionally includes five custom root-node functions:
5252

5353
\itemize{
5454
\item{"\link[=rbernoulli]{rbernoulli}": Draws randomly from a bernoulli distribution.}
5555
\item{"\link[=rcategorical]{rcategorical}": Draws randomly from any discrete probability density function.}
5656
\item{"\link[=rsample]{rsample}": Draws random samples from a given vector.}
57+
\item{"\link[=rtexp]{rtexp}": Draws random values from a left-truncated exponential distribution.}
5758
\item{"\link[=rconstant]{rconstant}": Used to set a variable to a constant value.}
5859
}
5960

@@ -89,6 +90,7 @@ Currently, the following node types are implemented directly for convenience to
8990
\itemize{
9091
\item{"\link[=node_time_to_event]{time_to_event}": A node based on repeatedly checking whether an event occurs at each point in time.}
9192
\item{"\link[=node_competing_events]{competing_events}": A node based on repeatedly checking whether one of multiple mutually exclusive events occurs at each point in time.}
93+
\item{"\link[=node_next_time]{next_time}": A node that draws the time of the next event in discrete-event simulation.}
9294
}
9395

9496
However, the user may also use any of the child node types in a \code{node_td} call directly. For custom time-dependent node types, please consult the associated vignette.
@@ -105,7 +107,7 @@ If the data generated by a child node is categorical (such as when using \code{n
105107
106108
The name DAG (directed \strong{acyclic} graph) implies that cycles are not allowed. This means that if you start from any node and only follow the arrows in the direction they are pointing, there should be no way to get back to your original node. This is necessary both theoretically and for practical reasons if we are dealing with static DAGs created using the \code{node} function. If the user attempts to generate data from a static cyclic graph using the \code{\link{sim_from_dag}} function, an error will be produced.
107109
108-
However, in the realm of discrete-time simulations, cyclic causal structures are perfectly reasonable. A variable \eqn{A} at \eqn{t = 1} may influence a variable \eqn{B} at \eqn{t = 2}, which in turn may influence variable \eqn{A} at \eqn{t = 3} again. Therefore, when using the \code{node_td} function to simulate time-dependent data using the \code{\link{sim_discrete_time}} function, cyclic structures are allowed to be present and no error will be produced.
110+
However, in the realm of discrete-time or discrete-event simulations, cyclic causal structures are perfectly reasonable. A variable \eqn{A} at \eqn{t = 1} may influence a variable \eqn{B} at \eqn{t = 2}, which in turn may influence variable \eqn{A} at \eqn{t = 3} again. Therefore, when using the \code{node_td} function to simulate time-dependent data using the \code{\link{sim_discrete_time}} or \code{\link{sim_discrete_event}} function, cyclic structures are allowed to be present and no error will be produced.
109111
110112
}
111113
\note{

man/simDAG.Rd

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ This package aims to give a comprehensive framework to simulate static and longi
1212

1313
\strong{\emph{What features are included in this package?}}
1414

15-
This package includes two main simulation functions: the \code{\link{sim_from_dag}} function, which can be used to simulate data from a previously defined causal DAG and node information and the \code{\link{sim_discrete_time}} function, which implements a framework to conduct discrete-time simulations. The former is very easy to use, but cannot deal with time-varying variable easily. The latter is a little more difficult to use (usually requiring the user to write some functions himself), but allows the simulation of arbitrarily complex longitudinal data.
15+
This package includes three main simulation functions: the \code{\link{sim_from_dag}} function, which can be used to simulate data from a previously defined causal DAG and node information, the \code{\link{sim_discrete_time}} function, which implements a framework to conduct discrete-time simulations and the \code{\link{sim_discrete_event}} function for discrete-event simulations. The former is very easy to use, but cannot deal with time-varying variable easily. The latter two are a little more difficult to use (usually requiring the user to write some functions himself), but allow the simulation of arbitrarily complex longitudinal data in discrete and continuous time.
1616

17-
Through a collection of implemented node types, this package allows the user to generate data with a mix of binary, categorical, count and time-to-event data. The \code{\link{sim_discrete_time}} function additionally enables the user to generate time-to-event data with, if desired, a mix of competing events, recurrent events, time-varying variables that influence each other and any types of censoring.
17+
Through a collection of implemented node types, this package allows the user to generate data with a mix of binary, categorical, count and time-to-event data. The \code{\link{sim_discrete_time}} and \code{\link{sim_discrete_event}} functions additionally enable the user to generate time-to-event data with, if desired, a mix of competing events, recurrent events, time-varying variables that influence each other and any types of censoring.
1818

1919
The package also includes a few functions to transform resulting data into multiple formats, to augment existing DAGs, to plot DAGs and to plot a flow-chart of the data generation process.
2020

@@ -26,15 +26,15 @@ Users should start by defining a \code{DAG} object using the \code{\link{empty_d
2626

2727
\strong{\emph{When should I use \code{sim_from_dag} and when \code{sim_discrete_time}?}}
2828

29-
If you want to simulate data that is easily described using a standard DAG without time-varying variables, you should use the \code{\link{sim_from_dag}} function. If the DAG includes time-varying variables, but you only want to consider a few points in time and can easily describe the relations between those manually, you can still use the \code{\link{sim_from_dag}} function. If you want more complex data with time-varying variables, particularly with time-to-event outcomes, you should consider using the \code{\link{sim_discrete_time}} function.
29+
If you want to simulate data that is easily described using a standard DAG without time-varying variables, you should use the \code{\link{sim_from_dag}} function. If the DAG includes time-varying variables, but you only want to consider a few points in time and can easily describe the relations between those manually, you can still use the \code{\link{sim_from_dag}} function. If you want more complex data with time-varying variables, particularly with time-to-event outcomes, you should consider using the \code{\link{sim_discrete_time}} or \code{\link{sim_discrete_event}} functions.
3030

3131
\strong{\emph{What features are missing from this package?}}
3232

3333
The package currently only implements some possible child nodes. In the future we would like to implement more child node types, such as more complex survival time models and extending the already existing support for multilevel modeling to other node types.
3434

3535
\strong{\emph{Why should I use this package instead of the \pkg{simcausal} package?}}
3636

37-
The \pkg{simcausal} package was a big inspiration for this package. In contrast to it, however, it allows quite a bit more flexibility. A big difference is that this package includes a comprehensive framework for discrete-time simulations and the \pkg{simcausal} package does not.
37+
The \pkg{simcausal} package was a big inspiration for this package. In contrast to it, however, it allows quite a bit more flexibility. A big difference is that this package includes a comprehensive framework for discrete-time and discrete-event simulations and the \pkg{simcausal} package does not.
3838

3939
\strong{\emph{Where can I get more information?}}
4040

@@ -52,9 +52,9 @@ The documentation pages contain a lot of information, relevant examples and some
5252

5353
A separate (already peer-reviewed) article about this package has been provisionally accepted in the \emph{Journal of Statistical Software}. The preprint version of this article is available on arXiv (Denz and Timmesfeld 2025).
5454

55-
\strong{\emph{I have a problem using the \code{sim_discrete_time} function}}
55+
\strong{\emph{I have a problem using the \code{sim_discrete_time} or \code{sim_discrete_event} function}}
5656

57-
The \code{\link{sim_discrete_time}} function can become difficult to use depending on what kind of data the user wants to generate. For this reason we put in extra effort to make the documentation and examples as clear and helpful as possible. Please consult the relevant documentation pages and the vignettes before contacting the authors directly with programming related questions that are not clearly bugs in the code.
57+
The \code{\link{sim_discrete_time}} and \code{\link{sim_discrete_event}} functions can become difficult to use depending on what kind of data the user wants to generate. For this reason we put in extra effort to make the documentation and examples as clear and helpful as possible. Please consult the relevant documentation pages and the vignettes before contacting the authors directly with programming related questions that are not clearly bugs in the code.
5858

5959
\strong{\emph{I want to suggest a new feature / I want to report a bug. Where can I do this?}}
6060

man/sim_discrete_event.Rd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
\alias{sim_discrete_event}
33

44
\title{
5-
Simulate Data from a \code{DAG} with Time-Dependent Variables using Discrete-Event Simulation
5+
Simulate Data from a \code{DAG} with Time-Dependent Variables in Continuous Time
66
}
77
\description{
88
EXPERIMENTAL: Similar to \code{\link{sim_discrete_time}}, this function allows users to generate complex data with time-varying variables from a \code{DAG} defined using \code{\link{node}} and \code{\link{node_td}} calls. In contrast to \code{\link{sim_discrete_time}}, time is modelled as a continuous variable using a discrete-event simulation approach. See details.

0 commit comments

Comments
 (0)