AMR/man/get_episode.Rd

194 lines
7.6 KiB
R

% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/get_episode.R
\name{get_episode}
\alias{get_episode}
\alias{is_new_episode}
\title{Determine (Clinical or Epidemic) Episodes}
\usage{
get_episode(x, episode_days = NULL, case_free_days = NULL, ...)
is_new_episode(x, episode_days = NULL, case_free_days = NULL, ...)
}
\arguments{
\item{x}{vector of dates (class \code{Date} or \code{POSIXt}), will be sorted internally to determine episodes}
\item{episode_days}{episode length in days to specify the time period after which a new episode begins, can also be less than a day or \code{Inf}, see \emph{Details}}
\item{case_free_days}{(inter-epidemic) interval length in days after which a new episode will start, can also be less than a day or \code{Inf}, see \emph{Details}}
\item{...}{ignored, only in place to allow future extensions}
}
\value{
\itemize{
\item \code{\link[=get_episode]{get_episode()}}: an \link{integer} vector
\item \code{\link[=is_new_episode]{is_new_episode()}}: a \link{logical} vector
}
}
\description{
These functions determine which items in a vector can be considered (the start of) a new episode. This can be used to determine clinical episodes for any epidemiological analysis. The \code{\link[=get_episode]{get_episode()}} function returns the index number of the episode per group, while the \code{\link[=is_new_episode]{is_new_episode()}} function returns \code{TRUE} for every new \code{\link[=get_episode]{get_episode()}} index. Both absolute and relative episode determination are supported.
}
\details{
Episodes can be determined in two ways: absolute and relative.
\enumerate{
\item Absolute
This method uses \code{episode_days} to define an episode length in days, after which a new episode will start. A common use case in AMR data analysis is microbial epidemiology: episodes of \emph{S. aureus} bacteraemia in ICU patients for example. The episode length could then be 30 days, so that new \emph{S. aureus} isolates after an ICU episode of 30 days will be considered a different (or new) episode.
Thus, this method counts \strong{since the start of the previous episode}.
\item Relative
This method uses \code{case_free_days} to quantify the duration of case-free days (the inter-epidemic interval), after which a new episode will start. A common use case is infectious disease epidemiology: episodes of norovirus outbreaks in a hospital for example. The case-free period could then be 14 days, so that new norovirus cases after that time will be considered a different (or new) episode.
Thus, this methods counts \strong{since the last case in the previous episode}.
}
In a table:\tabular{ccc}{
Date \tab Using \code{episode_days = 7} \tab Using \code{case_free_days = 7} \cr
2023-01-01 \tab 1 \tab 1 \cr
2023-01-02 \tab 1 \tab 1 \cr
2023-01-05 \tab 1 \tab 1 \cr
2023-01-08 \tab 2** \tab 1 \cr
2023-02-21 \tab 3 \tab 2*** \cr
2023-02-22 \tab 3 \tab 2 \cr
2023-02-23 \tab 3 \tab 2 \cr
2023-02-24 \tab 3 \tab 2 \cr
2023-03-01 \tab 4 \tab 2 \cr
}
** This marks the start of a new episode, because 8 January 2023 is more than 7 days since the start of the previous episode (1 January 2023). \cr
*** This marks the start of a new episode, because 21 January 2023 is more than 7 days since the last case in the previous episode (8 January 2023).
Either \code{episode_days} or \code{case_free_days} must be provided in the function.
\subsection{Difference between \code{get_episode()} and \code{is_new_episode()}}{
The \code{\link[=get_episode]{get_episode()}} function returns the index number of the episode, so all cases/patients/isolates in the first episode will have the number 1, all cases/patients/isolates in the second episode will have the number 2, etc.
The \code{\link[=is_new_episode]{is_new_episode()}} function returns \code{TRUE} for every new \code{\link[=get_episode]{get_episode()}} index, and is thus equal to \code{!duplicated(get_episode(...))}.
To specify, when setting \code{episode_days = 365} (using method 1 as explained above), this is how the two functions differ:\tabular{cccc}{
patient \tab date \tab \code{get_episode()} \tab \code{is_new_episode()} \cr
A \tab 2019-01-01 \tab 1 \tab TRUE \cr
A \tab 2019-03-01 \tab 1 \tab FALSE \cr
A \tab 2021-01-01 \tab 2 \tab TRUE \cr
B \tab 2008-01-01 \tab 1 \tab TRUE \cr
B \tab 2008-01-01 \tab 1 \tab FALSE \cr
C \tab 2020-01-01 \tab 1 \tab TRUE \cr
}
}
\subsection{Other}{
The \code{\link[=first_isolate]{first_isolate()}} function is a wrapper around the \code{\link[=is_new_episode]{is_new_episode()}} function, but is more efficient for data sets containing microorganism codes or names and allows for different isolate selection methods.
The \code{dplyr} package is not required for these functions to work, but these episode functions do support \link[dplyr:group_by]{variable grouping} and work conveniently inside \code{dplyr} verbs such as \code{\link[dplyr:filter]{filter()}}, \code{\link[dplyr:mutate]{mutate()}} and \code{\link[dplyr:summarise]{summarise()}}.
}
}
\examples{
# difference between absolute and relative determination of episodes:
x <- data.frame(dates = as.Date(c(
"2021-01-01",
"2021-01-02",
"2021-01-05",
"2021-01-08",
"2021-02-21",
"2021-02-22",
"2021-02-23",
"2021-02-24",
"2021-03-01",
"2021-03-01"
)))
x$absolute <- get_episode(x$dates, episode_days = 7)
x$relative <- get_episode(x$dates, case_free_days = 7)
x
# `example_isolates` is a data set available in the AMR package.
# See ?example_isolates
df <- example_isolates[sample(seq_len(2000), size = 100), ]
get_episode(df$date, episode_days = 60) # indices
is_new_episode(df$date, episode_days = 60) # TRUE/FALSE
# filter on results from the third 60-day episode only, using base R
df[which(get_episode(df$date, 60) == 3), ]
# the functions also work for less than a day, e.g. to include one per hour:
get_episode(
c(
Sys.time(),
Sys.time() + 60 * 60
),
episode_days = 1 / 24
)
\donttest{
if (require("dplyr")) {
# is_new_episode() can also be used in dplyr verbs to determine patient
# episodes based on any (combination of) grouping variables:
df \%>\%
mutate(condition = sample(
x = c("A", "B", "C"),
size = 100,
replace = TRUE
)) \%>\%
group_by(patient, condition) \%>\%
mutate(new_episode = is_new_episode(date, 365)) \%>\%
select(patient, date, condition, new_episode) \%>\%
arrange(patient, condition, date)
}
if (require("dplyr")) {
df \%>\%
group_by(ward, patient) \%>\%
transmute(date,
patient,
new_index = get_episode(date, 60),
new_logical = is_new_episode(date, 60)
) \%>\%
arrange(patient, ward, date)
}
if (require("dplyr")) {
df \%>\%
group_by(ward) \%>\%
summarise(
n_patients = n_distinct(patient),
n_episodes_365 = sum(is_new_episode(date, episode_days = 365)),
n_episodes_60 = sum(is_new_episode(date, episode_days = 60)),
n_episodes_30 = sum(is_new_episode(date, episode_days = 30))
)
}
# grouping on patients and microorganisms leads to the same
# results as first_isolate() when using 'episode-based':
if (require("dplyr")) {
x <- df \%>\%
filter_first_isolate(
include_unknown = TRUE,
method = "episode-based"
)
y <- df \%>\%
group_by(patient, mo) \%>\%
filter(is_new_episode(date, 365)) \%>\%
ungroup()
identical(x, y)
}
# but is_new_episode() has a lot more flexibility than first_isolate(),
# since you can now group on anything that seems relevant:
if (require("dplyr")) {
df \%>\%
group_by(patient, mo, ward) \%>\%
mutate(flag_episode = is_new_episode(date, 365)) \%>\%
select(group_vars(.), flag_episode)
}
}
}
\seealso{
\code{\link[=first_isolate]{first_isolate()}}
}