Adherence to medication is defined as the agreement between prescribed and actual medication use. Adherence metrics are often computed from electronic healthcare data (EHD) based on a single data source, such as pharmacy dispensing, or other administrative data [Dima & Dediu, 2017]. However, information about the medication prescribed and dispensed to a patient in a given time period may be found in different data sources, which become increasingly accessible for linking and thus for a more precise description of adherence patterns.
The core AdhereR
functions estimate adherence based on durations for which medications have been prescribed and/or dispensed. However, medications might not be prescribed or dispensed for a specific duration in all circumstances. Many healthcare settings allow for multiple refills of prescriptions, and medications might be dispensed in fixed pre-packed quantities rather than from bulk. AdhereR
can compute event durations based on prescribed and dispensed quantities.
Various events might affect supply durations after medications have been dispensed. Prescribed dosage might change between dispensing events, changing the original supply duration. Treatments may be interrupted and resumed at later times, for which existing supplies may or may not be used. In contrast, patients might not use their own supplies during certain periods (e.g., when hospitalised). AdhereR
takes into account dosage changes, special periods, and treatment interruptions.
This vignette describes the functions compute_event_durations
and its arguments. We use the provided example datasets included in the package to illustrate the various options and their impact on the calculated durations. We also discuss the computation of adherence with the output of compute_event_durations
, introducing the functions prune_event_durations
, cover_special_periods
and time_to_initiation
.
Throughout AdhereR
, we use the same terms and definitions. For a complete list, you may refer to the AdhereR: Adherence to Medications vignette. Here we reiterate a selection of those terms and describe additional terms relevant for the context of this function:
compute_event_durations
If event durations need to be computed, at least two separate datasets are required: Dispensing events and Prescription events. Additionally, special periods for which medication is supplied but not documented in the dispensing dataset can be provided (e.g. hospitalisation events). Each of those datasets might require specific preparation steps to bring them into the format described below.
The minimum necessary dataset includes 4 variables for each dispensing event: patient unique identifier, dispensing event date, medication class, and dispensed quantity.
medication class can include multiple columns, which allows to distinguish medications on multiple levels. For example, one might want to differentiate between dosage forms of the same substance (e.g., tablets and inhalers of corticosteroids) and include both columns (substance and form) to describe medication class. The dispensed quantity could be the number of units dispensed (e.g., tablets), or a total number of subunits contained in each dispensed unit (e.g., milligrams in tablets). If multiple dosage forms for the same substance exist, it is useful to calculate the subunits because the dispensed dosage per unit might not correspond to the prescribed dosage per unit. For example, a prescription for 40 mg Atorvastatin 1 tablet daily might be dispensed as 80 mg tablets with the instruction to use half of a tablet per day.
For demonstration purposes, AdhereR
comes with a sample dataset containing dispensing events (one per row) for 16 patients over a period of roughly 24 months (1794 events in total). Each row represents an individual dispensing record for a specific dose of a specific medication for a patient at a given date. Six variables are included in this dataset:
ID
),DATE.DISP
; from 1 July 2056 to 12 July 2058, in the “yyyy-mm-dd” ISO format),ATC.CODE
; 49 different codes according to the Anatomical Therapeutic Chemical Classification [ATC] System),UNIT
; 57% MG, 12% MICROG, 31% UI),FORM
; 12% INHALATION VAPOUR, 3% INJECTION, 13% METERED INHALER, 72% ORAL FORM), andTOTAL.DOSE
; median 20,000, range 10-120,000,000).Table 1 shows the first 10 rows of the dispensing events in the example dataset durcomp.dispensing
.
The minimum necessary dataset includes 4 variables for each prescription event: patient unique identifier, prescription event date, medication class, and prescribed daily dose. A visit number and prescription duration are optional.
medication class can include multiple columns, corresponding to the columns in the dispensing dataset. A duration will only be calculated if the information in all columns for the medication class are the same in dispensing and prescription events. Similar to the dispensed quantity, the prescribed daily dose could be the number of units prescribed per day (e.g., 2 tablets), or a total dosage to be taken daily (e.g., 40 mg). If a medication is prescribed for regular but not daily use, the dosage should be recalculated, e.g. in case of 70 mg once per week, the prescribed daily dose should be 10 mg.
IMPORTANT TO NOTE: It is assumed that the prescribed daily dose can be accomodated with the dispensed medication, for example by splitting tablets. This requires careful consideration and exploratory analysis of the dispensed and prescribed dosage forms and posologies.
For demonstration purposes, AdhereR
comes with a sample dataset containing prescription events (one per row) for 16 patients over a period of roughly 15 months (1502 events in total). Each row represents an individual prescription record for a specific dose of a specific medication for a patient at a given date. Eight variables are included in this dataset:
ID
),DATE.PRESC
; from 15 September 2056 to 30 December 2057, in the “yyyy-mm-dd” ISO format),VISIT
; median 5, range 0-16),ATC.CODE
; 43 different codes according to the Anatomical Therapeutic Chemical Classification [ATC] System),UNIT
; 50% MG, 10% MICROG, 40% UI),FORM
; 18% INHALATION VAPOUR, 5% INJECTION, 13% METERED INHALER, 64% ORAL FORM),PRESC.DURATION
; median 30, range 30-90 days, 1437 not provided (NA
), andDAILY.DOSE
; median 600, range 0.07-8000000).Table 2 shows the first 10 rows of the prescription events in the example dataset durcomp.prescribing
.
During certain periods, medication use may differ from what is expected based on the available data. Typical examples of such periods are hospitalisations, holidays, incarcerations, or similar. If available, these periods can be taken into account during computation of event durations. The minimum required variables are: patient unique identifier, start date, and end date of special periods. Optional columns are type (indicating the type of special period), the method how to handle a specific period (explained below), and medication class (from those specified in dispensing and prescription datasets).
For demonstration purposes, AdhereR
comes with a sample dataset containing hospitalization periods (one per row) for 10 patients over a period of roughly 18 months (28 events in total). Each row represents an individual hospitalisation period of a patient for whom event durations should be calculated. All column names must match the format provided in this example:
ID
),DATE.IN
; from 15 September 2056 to 23 November 2057, in the “yyyy-mm-dd” ISO format), andDATE.OUT
; from 22 September 2056 to 24 December 2057, in the “yyyy-mm-dd” ISO format)Table 3 shows the first 10 rows of the hospitalisation events in the example dataset durcomp.hospitalisation
.
The function arguments for compute_event_durations
are:
disp.data
: A data.frame or data.table containing the dispensing events (see above). Must contain, at a minimum, the patient unique ID, one medication identifier, the dispensing date, and total dispensed dose, and might also contain additional columns to identify and group medications (the actual column names are defined in the medication.class.colnames
parameter).presc.data
: A data.frame or data.table containing the prescribing events (see above). Must contain, at a minimum, the same unique patient ID and medication identifier(s) as the dispensing data, the prescription date, the daily prescribed dose, and the prescription duration. Optionally, it might also contain a visit number.special.periods.data
: Optional, NULL
or a data.frame or data.table containing the information about special periods (see above). Must contain the same unique patient ID as dispensing and prescription data, the start and end dates of the special periods with the exact column names DATE.IN
and DATE.OUT
. Optional columns are TYPE
(indicating the type of special situation), customized instructions how to handle a specific period (see special.periods.method
), and any of those specified in medication.class.colnames
.ID.colname
: A string, the name of the column in disp.data
, presc.data
, and special.periods.data
containing the unique patient ID.medication.class.colnames
: A Vector of strings, the name(s) of the column(s) in disp.data
and presc.data
containing the classes/types/groups of medication.disp.date.colname
: A string, the name of the column in disp.data
containing the dispensing date (in the format given in the date.format
parameter).total.dose.colname
: A string, the name of the column in disp.data
containing the total dispensed dose as numeric
(e.g. 500
for 10 tablets of 50 mg).presc.date.colname
: A string, the name of the column in presc.data
containing the prescription date (in the format given in the date.format
parameter).presc.daily.dose.colname
: A string, the name of the column in presc.data
containing the daily prescribed dose as numeric
(e.g. 50
for 50 mg once per day, or 25 for 50 mg once ever 2 days).presc.duration.colname
: A string, the name of the column in presc.data
containing the duration of the prescription as numeric
or NA
if duration is unknown.visit.colname
: A string, the name of the column in presc.data
containing the number of the visit or a new column name if the prescribing data does not contain such a column.split.on.dosage.change
: Logical or string. If TRUE
split the dispensing event on days with dosage change and create a new event with the new dosage for the remaining supply. If string, the name of the column containing the Logical
in disp.data for each medication class separatly. Important if carryover should be considered later on. See details below.force.init.presc
: Logical. If TRUE
advance the date of the first prescription event to the date of the first dispensing event, if the first prescription event is after the first dispensing event for a specific medication. Only if the first prescription event is not limited in duration (as indicated in the presc.duration.colname
). See details below.force.presc.renew
: Logical or string. If TRUE
require a new prescription for all medications for every prescription event (visit), otherwise prescriptions end on the first visit without renewal. If string, the name of the column in disp.data containing the Logical
for each medication class separatly. See details below.trt.interruption
: Can be either of “continue”, “discard”, “carryover”, or a string. It indicates how to handle durations during treatment interruptions (see special.periods.method
). If string, the name of the (character) column in disp.data containing the information (“continue”, “discard”, or “carryover”) for each medication class separatly. See details below.special.periods.method
: Can be either of continue, discard, carryover, or custom. It indicates how to handle durations during special periods. With continue, special periods have no effect on durations and event start dates. With discard, durations are truncated at the beginning of special periods and the remaining quantity is discarded. With carryover, durations are truncated at the beginning of a special period and a new event with the remaining duration is created after the end of the end of the special period. With custom, the mapping has to be included in special.periods.data. See details below.date.format
: A string giving the format of the dates used in the data
and the other parameters; see the format
parameters of the as.Date
function for details (NB, this concerns only the dates given as strings and not as Date
objects).suppress.warnings
: Logical, if TRUE
don’t show any warnings.return.data.table
: Logical, if TRUE
return a data.table
object, otherwise a data.frame
.progress.bar
: Logical, if TRUE
show a progress bar....
: other possible parameters.In the following paragraphs, we discuss the various options regarding special periods and treatment interruption, prescription start and renewal, and dosage changes.
If the dosage changes before the end of a supply duration, split.on.dosage.change = TRUE
creates a new event on the day of dosage change and recalculates the duration for the remaining supply. If patients are expected to finish the previous supply with the original dose and starting with the new dosage recommendation from the next supply onward, split.on.dosage.change
should be set to FALSE
. Alternatively, this can be set for each medication class separately by providing the name of a column containing the information in the dispensing dataset (logical, TRUE
or FALSE
).
In Figure 5 below, the dosage for Insulin (NovoMix) changed while the patient still had an available supply. By setting split.on.dosage.change = TRUE
, a new event is created on the day of dosage change.
If the dispensing dataset of a patient covers events with earlier dates than the first prescription events for this specific medication, force.init.presc = TRUE
advances the date of the first prescription event to the date of the first dispensing event. For example, if prescribing data is only available during the observation window, but dispensing data covers a larger follow-up window, this setting allows the calculation of supply durations for carryover into the observation window.
IMPORTANT TO NOTE: This only applies if the first prescription event is not limited in duration (as indicated in presc.duration.colname
). This is to avoid assumptions about the true start date and duration of such prescriptions.
In the example in Figure 3 below, the patient had dispensing events for Salbutamol (R03AC02) and Salmeterol (R03AC12) starting from 2056-07-31, but the first prescribing event for either of those was not before 2056-12-10. Because the prescriptions were not limited in duration, by setting force.init.presc = TRUE
, durations for dispensing events before 2056-12-10 can be calculated.
If a medication is not prescribed during any given visit for a patient, force.prescription.renew = TRUE
will make sure that the prescription episode for this medication ends on the first visit without renewal. Alternatively, this can be set for each medication class separately by providing the name of a column containing the information in the dispensing dataset (logical, TRUE
or FALSE
).
If prescriptions are not routinely prescribed during all visits, force.prescription.renew
should be set to FALSE
. This can also be the case if prescription data covers multiple prescribers, because treatments prescribed by one prescriber might continue even when not prescribed during a subsequent visit to another prescriber.
In Table 4 and Figure 4 below, the medication (a leukotriene receptor antagonist) was prescribed for a limited duration initially (during visit 2 and 3). Later, it was represcribed during visits 7, 8, 10, and 11, but not during visit 9. By setting force.presc.renew = TRUE
, the prescription ends on the date of visit 9 and restarts on the date of visit 10.
During special periods and treatment interruptions, medication use may differ from usual conditions. The argument special.periods.method
tells AdhereR
what to do during such periods. Similarly, trt.interruption
specifies handling of treatment interruptions (periods without prescription). There are 3 options that can be set globally:
In addition to the global options, both settings accept a column name in the dispensing dataset (for trt.interruption
) or special periods dataset (for special.periods.method
). The column can contain either of continue, discard, or carryover per medication class (for trt.interruption
) or per special period type and/or medication class (for special.periods.method
).
Special periods may occur during prescription episodes or treatment interruptions and different types of special periods may co-occur. Treatment interruptions are prioritized over other special periods: If a prescription ends and trt.interruption = 'carryover'
, a different setting in special.periods.method
has no effect during periods of treatment interruption. However, if a special period of type continue overlaps with another special period of type carryover, the setting of the period with the later start date is used.
In Figure 1 below, the patient had frequent hospitalisation events (blue segments). By setting trt.interruptions = "carryover"
, supplies available before the start of hospitalisation are truncated and a new event is created on the day of discharge for the remaining supply.
Let’s consider a hypothetical scenario with some overlapping special periods.
ID | DATE.IN | DATE.OUT | TYPE | CUSTOM |
---|---|---|---|---|
1 | 2000-01-15 | 2000-02-20 | HOSP | carryover |
1 | 2000-01-25 | 2000-02-05 | REHAB | continue |
1 | 2000-03-01 | 2000-03-15 | HOLIDAY | continue |
1 | 2000-03-05 | 2000-03-10 | HOSP | carryover |
Figure 2 shows a hypothetical patient with one prescription and one dispensing event for 60 days. By providing the above dataset as special.periods = "special_episodes"
and setting special.periods.method = "CUSTOM"
, the 60-day supply is truncated and restarted according to the most recent special period that hasn’t ended yet.
compute_event_durations
The output of compute_event_durations
is a list containing all data required for CMA computations, plus additional information:
event_durations
: a data.table
or data.frame
with the following columns:
ID.colname
: the unique patient ID, as given by the ID.colname
parameter.disp.date.colname
: the date of the dispensing event, as given by the disp.date.colnema
parameter.medication.class.colnames
: the column(s) with classes/types/groups of medication, as given by the medication.class.colnames
parameter.total.dose.colname
: the total dispensed quantity, as given by the total.dose.colname
parameter.presc.daily.dose.colname
: the prescribed daily dose, as given by the presc.daily.dose.colname
parameter.DISP.START
: the start date of the dispensing event, either the same as in disp.date.colname
or a later date in case of dosage changes or treatment interruptions/hospitalisations.DURATION
: the calculated duration of the supply, based on the total dispensed dose and the prescribed daily dose, starting from the DISP.START
date.SPECIAL.DURATION
: the number of days during the current duration affected by special durations or treatment interruptions of type “continue”CARRYOVER.DURATION
: the number of days after the current duration affected by special durations or treatment interruptions of type “carryover”tot.presc.interruptions
: the total number of prescription interruptions per patient for a specific medication.tot.dosage.changes
: the total number of dosage changes per patient for a specific medication.prescription_episodes
: a data.table
or data.frame
with the following columns:
ID.colname
: the unique patient ID, as given by the ID.colname
parameter.medication.class.colnames
: the column(s) with classes/types/groups of medication, as given by the medication.class.colnames
parameter.presc.daily.dose.colname
: the prescribed daily dose, as given by the presc.daily.dose.colname
parameter.episode.start
: the start date of the prescription episode.episode.duration
: the duration of the prescription episode in days.episode.end
: the end date of the prescription episode.special_periods
: a data.table
or data.frame
with the following columns:
ID.colname
: the unique patient ID, as given by the ID.colname
parameter.DATE.IN
: the start date of the special periodDATE.OUT
: the end date of the special periodTYPE
: optional, the typeCUSTOM
: the special period mapping, either “carryover”, “continue”, or “discard”SPECIAL.DURATION
: the number of days between DATE.IN
and DATE.OUT
In addition, the output contains all the arguments to the function call:
special.periods.method
ID.colname
presc.date.colname
disp.date.colname
date.format
medication.class.colnames
total.dose.colname
presc.daily.dose.colname
presc.duration.colname
visit.colname
force.init.presc
force.presc.renew
trt.interruption
split.on.dosage.change
# use compute_event_duration with the included example dataset and default settings
durations <- compute_event_durations(disp.data = durcomp.dispensing, # the dispensing data
presc.data = durcomp.prescribing, # the prescribing data
special.periods.data = durcomp.hospitalisation, # the special periods data
ID.colname = "ID", # the unique Patient ID column in disp.data, presc.data, and special.periods.data
presc.date.colname = "DATE.PRESC", # the prescription date column
disp.date.colname = "DATE.DISP", # the dispensing date column
date.format = "%Y-%m-%d", # the date format
medication.class.colnames = c("ATC.CODE","UNIT", "FORM"), # the medication class columns
total.dose.colname = "TOTAL.DOSE", # the total dispensed dose column
presc.daily.dose.colname = "DAILY.DOSE", # the prescribed daily dose column
presc.duration.colname = "PRESC.DURATION", # the prescription duration column
visit.colname = "VISIT", # the prescribing event visit column
return.data.table = TRUE, # return data.table for better printing
progress.bar = FALSE) # don't display progress bar
# display output
durations
## $event_durations
## ID ATC.CODE UNIT FORM DATE.DISP TOTAL.DOSE DAILY.DOSE
## 1: 1 A02BC02 MG ORAL FORM 2057-09-04 560 20
## 2: 1 A02BC02 MG ORAL FORM 2058-06-03 560 20
## 3: 1 A02BC02 MG ORAL FORM 2058-07-09 560 20
## 4: 1 A02BC05 MG ORAL FORM 2056-08-05 1120 NA
## 5: 1 A02BC05 MG ORAL FORM 2056-10-03 1120 NA
## ---
## 1442: 16 R03AC12 MICROG METERED INHALER 2058-06-17 3000 50
## 1443: 16 R03AC12 MICROG METERED INHALER 2058-07-12 3000 50
## 1444: 16 R03AK06 MICROG METERED INHALER 2057-11-02 30000 500
## 1445: 16 R03AK06 MICROG METERED INHALER 2058-01-04 30000 500
## 1446: 16 R03AK06 MICROG METERED INHALER 2058-04-20 60000 500
## DISP.START DURATION episode.start episode.end SPECIAL.DURATION
## 1: 2057-09-04 28 2057-09-01 <NA> 0
## 2: 2058-06-03 28 2057-09-01 <NA> 0
## 3: 2058-07-09 28 2057-09-01 <NA> 0
## 4: <NA> NA <NA> <NA> NA
## 5: <NA> NA <NA> <NA> NA
## ---
## 1442: 2058-06-17 60 2057-11-23 <NA> 0
## 1443: 2058-07-12 60 2057-11-23 <NA> 0
## 1444: 2057-11-02 60 2057-11-02 <NA> 0
## 1445: 2058-01-04 60 2057-11-02 <NA> 0
## 1446: 2058-04-20 120 2057-11-02 <NA> 0
## tot.dosage.changes CARRYOVER.DURATION
## 1: 0 NA
## 2: 0 NA
## 3: 0 NA
## 4: NA NA
## 5: NA NA
## ---
## 1442: 0 NA
## 1443: 0 NA
## 1444: 0 NA
## 1445: 0 NA
## 1446: 0 NA
##
## $prescription_episodes
## ID ATC.CODE UNIT FORM DAILY.DOSE episode.ID
## 1: 1 A02BC02 MG ORAL FORM 20.000 1
## 2: 1 A09AA02 UI ORAL FORM 36000.000 1
## 3: 1 A09AA02 UI ORAL FORM 86000.000 2
## 4: 1 R03AC12 MICROG METERED INHALER 100.000 1
## 5: 2 A11CC05 UI ORAL FORM 1111.111 1
## ---
## 253: 16 J01MA02 MG ORAL FORM 1000.000 1
## 254: 16 J02AC02 MG ORAL FORM 400.000 1
## 255: 16 R03AC02 MG INHALATION VAPOUR 5.000 1
## 256: 16 R03AC12 MICROG METERED INHALER 50.000 1
## 257: 16 R03AK06 MICROG METERED INHALER 500.000 1
## episode.start episode.duration episode.end
## 1: 2057-09-01 NA <NA>
## 2: 2056-12-08 77 2057-02-23
## 3: 2057-02-23 NA <NA>
## 4: 2057-02-23 NA <NA>
## 5: 2056-09-22 NA <NA>
## ---
## 253: 2057-10-12 30 2057-11-11
## 254: 2057-11-23 NA <NA>
## 255: 2056-11-17 NA <NA>
## 256: 2057-11-23 NA <NA>
## 257: 2057-11-02 NA <NA>
##
## $special_periods
## ID DATE.IN DATE.OUT
## 1: 1 2057-03-03 2057-03-06
## 2: 1 2057-09-01 2057-09-04
## 3: 3 2057-03-04 2057-03-17
## 4: 3 2057-03-26 2057-05-01
## 5: 3 2057-06-15 2057-06-22
## 6: 3 2057-08-04 2057-08-12
## 7: 3 2057-08-19 2057-08-26
## 8: 3 2057-10-05 2057-10-16
## 9: 3 2057-11-02 2057-12-24
## 10: 4 2057-07-15 2057-07-16
## 11: 4 2057-11-23 2057-11-30
## 12: 6 2056-09-15 2056-09-22
## 13: 6 2057-03-11 2057-03-18
## 14: 6 2057-10-14 2057-10-21
## 15: 7 2057-03-12 2057-03-19
## 16: 7 2057-05-14 2057-05-21
## 17: 7 2057-07-17 2057-07-31
## 18: 9 2057-04-09 2057-04-10
## 19: 9 2057-05-25 2057-06-26
## 20: 9 2057-09-02 2057-09-04
## 21: 10 2057-03-27 2057-04-03
## 22: 10 2057-07-13 2057-07-20
## 23: 12 2057-07-07 2057-07-13
## 24: 14 2057-01-16 2057-01-27
## 25: 14 2057-02-11 2057-02-13
## 26: 14 2057-02-24 2057-02-27
## 27: 14 2057-10-26 2057-10-30
## 28: 15 2057-09-07 2057-09-10
## ID DATE.IN DATE.OUT
##
## $ID.colname
## [1] "ID"
##
## $medication.class.colnames
## [1] "ATC.CODE" "UNIT" "FORM"
##
## $disp.date.colname
## [1] "DATE.DISP"
##
## $total.dose.colname
## [1] "TOTAL.DOSE"
##
## $presc.date.colname
## [1] "DATE.PRESC"
##
## $presc.daily.dose.colname
## [1] "DAILY.DOSE"
##
## $presc.duration.colname
## [1] "PRESC.DURATION"
##
## $visit.colname
## [1] "VISIT"
##
## $split.on.dosage.change
## [1] TRUE
##
## $force.init.presc
## [1] FALSE
##
## $force.presc.renew
## [1] FALSE
##
## $trt.interruption
## [1] "continue"
##
## $special.periods.method
## [1] "continue"
##
## $date.format
## [1] "%Y-%m-%d"
compute_event_durations
In principle the output of compute_event_durations
can be used directly for CMA computation as described in the main vignette (after removing rows with zero or missing durations). However, there are some specificities to consider.
In compute_event_durations
, one or multiple columns to specify medication classes can be provided. Using multiple columns is especially useful when different formulations or brands of the same medication need to be matched between dispensing and prescribing data. This is in contrast to CMA computations (for single medications), where only one column for the medication class can be specified.
The event_durations
dataset has two columns that can be used as event date: disp.date.colname
, the original dispensing date in the dispensing dataset or DISP.START
, which might differ from the former in case of dosage changes, treatment interruptions or special periods of type carryover. For CMA versions accounting for carryover, there will be no difference between the two choices, as long as the events take place within the observation period. If an event start gets pushed out of the observation period (e.g., because of carryover during a special period), this will affect CMA calculation. Generally, it is appropriate to use DISP.START
as input for CMA calculations.
Figure 6 shows CMA7-plots of the same event_durations
, on top with event.date.colname = "DATE.DISP"
and below with event.date.colname = "DISP.START"
. The observation window (OW) begins and ends during special periods of type carryover (blue areas). With event.date.colname = "DISP.START"
, part of a supply is pushed into the OW and another supply is partially pushed out of the OW.
prune_event_durations
Dosage changes, special periods, and treatment interruptions of type carryover may lead to overestimation of implementation, e.g. if patients get a refill after discharge from hospital and don’t continue to use their previous supply. Likewise, it may also lead to overestimation of persistence, e.g. when patients do in fact discontinue treatments after the end of a special period or treatment interruption.
To correct for these situations,AdhereR
offers the prune_event_durations
function. The function looks for new dispensing events within a user-specified time after dosage changes, special periods, or treatment interruptions and flags or removes leftover supply from previous dispensing events within the same period.
It accepts the raw list output of compute_event_durations
and additional arguments to specify event durations that need to be removed:
data
: a list, the output of compute_event_durations
,include
: indicates whether to include dosage changes, special periods, and/or treatment interruptions. The function only works for special periods and treatment interruptions of type carryover,medication.class.colnames
: indicate columns in event_durations
to identify medication classes. Defaults to the columns used in compute_event_durations
,days.within.out.date.1
: durations after dosage change or carryover supply from before the special period or treatment interruptions are removed if there is a new dispensing event within the number of days after the dosage change or end of the special period/treatment interruption,days.within.out.date.2
: durations after dosage change or carryover supply from before the special period are removed if there is NO new dispensing event within the number of days after the dosage change or end of the special period/treatment interruption.,keep.all
: Logical, should events be kept and marked for removal? If TRUE
, a new column .prune.event
will be added to the event_durations
, if FALSE
these events will be removed.The function output is the pruned event_durations
dataset.
cover_special_periods
Without further processing of event_durations for CMA computations, special periods will appear as gaps, possibly leading to underestimation of implementation or even assumption of discontinuation and non-persistence. To consider such periods as covered, they can be added to the event_durations
dataset, for example when it is assumed that hospitalized patients are adherent during the hospitalization period. This can be achieved by merging the special periods with the event_durations
dataset and should be done after pruning with prune_event_durations
.
AdhereR offers the cover_special_periods
function to identify special periods that are in proximity to already covered durations and adds an additional event for these durations. It requires at least the following arguments:
events.data
: a data.frame or data.table with the event durations,special.periods.data
: a data.frame or data.table with the special periods,ID.colname
: the unique patient ID,disp.start.colname
: the start date of the medication event,duration.colname
: the calculated duration for the medication event,medication.class.colnames
: the column(s) with classes/types/groups of medication,days.before
: an integer, the number of days before the start of a special period within which an event duration must end to consider the special period as covered,days.after
: an integer, the number of days after a special period within which an event duration must start to consider the special period as covered,date.format
: a string with the date format.The function output is the event.data
dataset with the additional durations for special periods covered.
# select medication class of interest and compute event durations
event_durations_list <- compute_event_durations(disp.data = durcomp.dispensing[ID == 3 & grepl("J01EE01", ATC.CODE)],
presc.data = durcomp.prescribing[ID == 3 & grepl("J01EE01", ATC.CODE)],
special.periods.data = durcomp.hospitalisation[ID == 3],
special.periods.method = "carryover",
ID.colname = "ID",
presc.date.colname = "DATE.PRESC",
disp.date.colname = "DATE.DISP",
date.format = "%Y-%m-%d",
medication.class.colnames = c("ATC.CODE","UNIT", "FORM"),
total.dose.colname = "TOTAL.DOSE",
presc.daily.dose.colname = "DAILY.DOSE",
presc.duration.colname = "PRESC.DURATION",
visit.colname = "VISIT",
force.init.presc = TRUE,
force.presc.renew = TRUE,
split.on.dosage.change = TRUE,
trt.interruption = "carryover",
suppress.warnings = FALSE,
return.data.table = TRUE,
progress.bar = FALSE)
# prune dataset
event_durations <- prune_event_durations(event_durations_list,
include = c("special periods"), # only consider special periods
medication.class.colnames = "ATC.CODE",
days.within.out.date.1 = 7, # flag carryover durations if there are new events within 7 days after the end of special periods
days.within.out.date.2 = 30, # flag carryover durations if there are no new events within 30 days after the end of special periods
keep.all = FALSE # remove flagged events from dataset
)
# cover special periods
event_durations_covered <- cover_special_periods(events.data = event_durations,
special.periods.data = event_durations_list$special_periods,
ID.colname = "ID",
disp.start.colname = "DISP.START",
duration.colname = "DURATION",
medication.class.colnames = "ATC.CODE",
days.before = 7,
days.after = 7,
date.format = "%Y-%m-%d",
return.data.table = TRUE)
Figure 7 shows the same plot as Figure 1, but with pruned durations and added events for special durations.
Periods without prescriptions (treatment interruptions) or certain special episodes can be excluded from CMA computations to avoid under-estimation of adherence. In these instances CMA_per_episode
can be used with precomputed episodes, e.g. prescription episodes from the output of compute_event_durations
. Precomputed episodes can be specified with the treat.epi
parameter in CMA_per_episode
. The episodes have to be a data.frame
or data.table
with the following columns:
ID.colname
: the patient ID,medication.class.colname
: the medication class used for CMA computations,episode.ID
: the episode unique ID (increasing sequentially),episode.start
: the episode start date,episode.duration
: the episode duration in days,episode.end
: the episode end date.The prescription episodes in the output of compute_event_durations
come in the correct format to use with CMA_per_episode
:
# select medication class of interest and compute event durations
event_durations_list <- compute_event_durations(disp.data = durcomp.dispensing[grepl("J01EE01", ATC.CODE)],
presc.data = durcomp.prescribing[grepl("J01EE01", ATC.CODE)],
ID.colname = "ID",
presc.date.colname = "DATE.PRESC",
disp.date.colname = "DATE.DISP",
date.format = "%Y-%m-%d",
medication.class.colnames = c("ATC.CODE","UNIT", "FORM"),
total.dose.colname = "TOTAL.DOSE",
presc.daily.dose.colname = "DAILY.DOSE",
presc.duration.colname = "PRESC.DURATION",
visit.colname = "VISIT",
force.init.presc = FALSE,
force.presc.renew = TRUE,
split.on.dosage.change = TRUE,
trt.interruption = "carryover",
suppress.warnings = FALSE,
return.data.table = TRUE,
progress.bar = FALSE)
# get event durations and prescription episodes
event_durations <- copy(event_durations_list$event_durations)
prescription_episodes <- copy(event_durations_list$prescription_episodes)
# if no prescription enddate, set to end of follow-up window
treatment_episodes <- copy(prescription_episodes[is.na(episode.end), episode.end := as.Date("2058-01-01")])
# calculate episode duration
treatment_episodes[is.na(episode.duration), episode.duration := as.numeric(episode.end-episode.start)]
# drop unnecessary columns
treatment_episodes[,`:=` (ATC.CODE = NULL,
UNIT = NULL,
FORM = NULL,
DAILY.DOSE = NULL)]
event_durations[,`:=` (episode.start = NULL,
episode.end = NULL)]
# compute CMA per episode
df_cma_episode <- CMA_per_episode(data = event_durations[DURATION > 0],
treat.epi = treatment_episodes, # supply precomputed prescription episodes to CMA_per_episode
CMA.to.apply = "CMA7",
ID.colname = "ID",
event.date.colname = "DISP.START",
event.duration.colname = "DURATION",
event.daily.dose.colname = "DAILY.DOSE",
medication.class.colname = "ATC.CODE",
followup.window.start = as.Date("2056-01-01"),
followup.window.duration = 3*365,
observation.window.start = as.Date("2057-01-01"),
observation.window.duration = 365,
suppress.warnings = TRUE)
# get CMA per episode
cma_episode <- getCMA(df_cma_episode)
# display as markdown table
knitr::kable(cma_episode)
ID | episode.ID | episode.start | end.episode.gap.days | episode.duration | episode.end | CMA |
---|---|---|---|---|---|---|
3 | 1 | 2057-01-01 | 8.000000 | 159 | 2057-06-09 | 0.8113208 |
3 | 1 | 2057-01-01 | 8.000000 | 159 | 2057-06-09 | 0.8113208 |
3 | 1 | 2057-01-01 | 8.000000 | 159 | 2057-06-09 | 0.8113208 |
3 | 1 | 2057-01-01 | 8.000000 | 159 | 2057-06-09 | 0.8113208 |
3 | 1 | 2057-01-01 | 8.000000 | 159 | 2057-06-09 | 0.8113208 |
3 | 2 | 2057-06-09 | 46.000000 | 206 | 2058-01-01 | 0.5339806 |
3 | 2 | 2057-06-09 | 46.000000 | 206 | 2058-01-01 | 0.5339806 |
3 | 2 | 2057-06-09 | 46.000000 | 206 | 2058-01-01 | 0.5339806 |
3 | 2 | 2057-06-09 | 46.000000 | 206 | 2058-01-01 | 0.5339806 |
5 | 1 | 2057-02-23 | 9.666667 | 30 | 2057-03-25 | 0.4444444 |
6 | 1 | 2057-01-16 | 4.000000 | 30 | 2057-02-15 | 0.8333333 |
6 | 2 | 2057-12-15 | NA | 17 | 2058-01-01 | 0.8823529 |
6 | 2 | 2057-12-15 | NA | 17 | 2058-01-01 | 0.8823529 |
7 | 1 | 2057-12-10 | 8.000000 | 22 | 2058-01-01 | 0.4545455 |
9 | 1 | 2057-01-07 | NA | 30 | 2057-02-06 | NA |
9 | 2 | 2057-04-10 | 0.000000 | 30 | 2057-05-10 | 1.0000000 |
10 | 1 | 2057-01-06 | NA | 30 | 2057-02-05 | NA |
10 | 2 | 2057-04-21 | 0.000000 | 30 | 2057-05-21 | 1.0000000 |
10 | 2 | 2057-04-21 | 0.000000 | 30 | 2057-05-21 | 1.0000000 |
10 | 3 | 2057-06-09 | 15.666667 | 30 | 2057-07-09 | 0.4444444 |
10 | 4 | 2057-08-21 | 0.000000 | 30 | 2057-09-20 | 0.1666667 |
12 | 1 | 2057-03-30 | 0.000000 | 30 | 2057-04-29 | 0.5000000 |
12 | 2 | 2057-07-07 | 20.000000 | 30 | 2057-08-06 | 0.3333333 |
13 | 1 | 2057-01-01 | 8.000000 | 10 | 2057-01-11 | 0.2000000 |
13 | 2 | 2057-04-23 | 53.000000 | 253 | 2058-01-01 | 0.2964427 |
13 | 2 | 2057-04-23 | 53.000000 | 253 | 2058-01-01 | 0.2964427 |
13 | 2 | 2057-04-23 | 53.000000 | 253 | 2058-01-01 | 0.2964427 |
13 | 2 | 2057-04-23 | 53.000000 | 253 | 2058-01-01 | 0.2964427 |
13 | 2 | 2057-04-23 | 53.000000 | 253 | 2058-01-01 | 0.2964427 |
13 | 2 | 2057-04-23 | 53.000000 | 253 | 2058-01-01 | 0.2964427 |
14 | 1 | 2057-02-28 | 0.000000 | 200 | 2057-09-16 | 0.3200000 |
14 | 1 | 2057-02-28 | 0.000000 | 200 | 2057-09-16 | 0.3200000 |
14 | 1 | 2057-02-28 | 0.000000 | 200 | 2057-09-16 | 0.3200000 |
14 | 1 | 2057-02-28 | 0.000000 | 200 | 2057-09-16 | 0.3200000 |
14 | 1 | 2057-02-28 | 0.000000 | 200 | 2057-09-16 | 0.3200000 |
14 | 1 | 2057-02-28 | 0.000000 | 200 | 2057-09-16 | 0.3200000 |
14 | 2 | 2057-09-16 | 2.000000 | 30 | 2057-10-16 | 0.9333333 |
16 | 1 | 2057-09-22 | 0.000000 | 48 | 2057-11-09 | 0.6875000 |
16 | 1 | 2057-09-22 | 0.000000 | 48 | 2057-11-09 | 0.6875000 |
16 | 1 | 2057-09-22 | 0.000000 | 48 | 2057-11-09 | 0.6875000 |
16 | 1 | 2057-09-22 | 0.000000 | 48 | 2057-11-09 | 0.6875000 |
16 | 1 | 2057-09-22 | 0.000000 | 48 | 2057-11-09 | 0.6875000 |
16 | 2 | 2057-11-09 | 3.000000 | 30 | 2057-12-09 | 0.9000000 |
16 | 2 | 2057-11-09 | 3.000000 | 30 | 2057-12-09 | 0.9000000 |
time_to_initiation
CMA calculations for precomputed episodes do not necessarily reflect implementation. Delayed initiation and early discontinuation (non-persistence) may reduce CMA values. Non-persistence can be identified by looking at end.episode.gap.days
in the output of CMA_per_episodes
, which indicates how many days before the end of an episode are not covered.
Delayed initiation becomes evident when we calculate time to initiation for the same data. The function time_to_initiation
calculates the time between the start of a prescription episode and the first dispensing event, taking into account multiple variables to differentiate between treatments. It requires at least the following input:
presc.data
: A data.frame or data.table containing the prescription episodes. Must contain, at a minimum, the patient unique ID, one medication identifier, and the start date of the prescription episode, and might also contain additional columns to identify and group medications (the actual column names are defined in the medication.class.colnames
parameter).disp.data
: A data.frame or data.table containing the dispensing events. Must contain, at a minimum, the patient unique ID, one medication identifier, the dispensing date, and might also contain additional columns to identify and group medications (the actual column names are defined in the medication.class.colnames
parameter).ID.colname
: A string, the name of the column in presc.data
and disp.data
containing the unique patient ID, or NA
if not defined.presc.start.colname
: A string, the name of the column in presc.data
containing the prescription date (in the format given in the date.format
parameter), or NA
if not defined.disp.date.colname
: A string, the name of the column in disp.data
containing the dispensing date (in the format given in the date.format
parameter), or NA
if not defined.date.format
: A string giving the format of the dates used in the data
and the other parameters; see the format
parameters of the as.Date
function for details (NB, this concerns only the dates given as strings and not as Date
objects).medication.class.colnames
: A Vector of strings, the name(s) of the column(s) in data
containing the classes/types/groups of medication, or NA
if not defined.Its output is a data.frame or data.table with the following columns:
ID.colname
: the unique patient ID, as given by the ID.colname parameter.medication.class.colnames
: the column(s) with classes/types/groups of medication, as given by the medication.class.colnames parameter.episode.start
: the start date of a prescription episode,first.disp
: the date of the first dispensing event in an episode,time.to.initiation
: the difference in days between the first dispensing date and the first prescription date per episode.time_init <- time_to_initiation(presc.data = prescription_episodes,
disp.data = event_durations,
ID.colname = "ID",
presc.start.colname = "episode.start",
disp.date.colname = "DATE.DISP",
medication.class.colnames = c("ATC.CODE"),
date.format = "%Y-%m-%d",
suppress.warnings = FALSE,
return.data.table = TRUE)
# display as markdown table
knitr::kable(time_init)
ID | ATC.CODE | episode.start | first.disp | time.to.initiation |
---|---|---|---|---|
3 | J01EE01 | 2056-09-23 | 2056-10-18 | 25 |
3 | J01EE01 | 2057-06-09 | 2057-06-25 | 16 |
5 | J01EE01 | 2057-02-23 | 2057-03-02 | 7 |
6 | J01EE01 | 2057-01-16 | 2057-01-17 | 1 |
6 | J01EE01 | 2057-12-15 | 2057-12-16 | 1 |
7 | J01EE01 | 2057-12-10 | 2057-12-14 | 4 |
9 | J01EE01 | 2057-04-10 | NA | NA |
9 | J01EE01 | 2057-01-07 | 2057-03-19 | 71 |
10 | J01EE01 | 2057-01-06 | 2057-02-18 | 43 |
10 | J01EE01 | 2057-04-21 | 2057-04-24 | 3 |
10 | J01EE01 | 2057-06-09 | 2057-06-10 | 1 |
10 | J01EE01 | 2057-08-21 | 2057-09-15 | 25 |
11 | J01EE01 | 2057-10-19 | NA | NA |
12 | J01EE01 | 2057-07-07 | NA | NA |
12 | J01EE01 | 2057-03-30 | 2057-04-14 | 15 |
13 | J01EE01 | 2056-12-12 | 2056-12-24 | 12 |
13 | J01EE01 | 2057-04-23 | 2057-04-25 | 2 |
14 | J01EE01 | 2057-09-16 | NA | NA |
14 | J01EE01 | 2057-02-28 | 2057-03-14 | 14 |
16 | J01EE01 | 2057-09-22 | 2057-09-22 | 0 |
16 | J01EE01 | 2057-11-09 | 2057-11-09 | 0 |
In this vignette, we presented the AdhereR
functions compute_event_durations
, prune_event_durations
, cover_special_periods
, and time_to_initiation
. These functions aim to assist with the preparation and exploration of EHD for adherence estimation.
The functions can be used when information about the medication prescribed and dispensed to a patient in a given time period are available from different data sources and can be linked via a common identifiers for patients and medications. The compute_event_durations
functions is especially useful when medications are not be prescribed or dispensed for a specific duration but dispensed quantities and prescribed daily dosages are available. The functions prune_event_durations
and cover_special_periods
are especially useful when dates of special periods (e.g., hospitalizations) or treatment interruptions are available. The function time_to_initiation
helps to further distinguish between initiation and implementation.
Various parameters allow for flexible and transparent customization of the functions to many situations and needs. Before applying the functions to large datasets, users should carefully explore their data for all medications included.
Dima A.L., Dediu D. (2017) Computation of adherence to medication and visualization of medication histories in R with AdhereR: Towards transparent and reproducible use of electronic healthcare data. PLoS ONE 12(4): e0174426. doi:10.1371/journal.pone.0174426.