Skip to content

Comments

Implement occasion calculation#49

Merged
kylebaron merged 23 commits intomainfrom
occ
Mar 21, 2025
Merged

Implement occasion calculation#49
kylebaron merged 23 commits intomainfrom
occ

Conversation

@kylebaron
Copy link
Collaborator

@kylebaron kylebaron commented Dec 20, 2024

Optionally include occasion (OCC) in lastdose() (and friends) output. In general, a new occasion happens when there is a dose with "observation" records following before the next dose.

Status: work in progress; PR opened for review from the team. I'm working up these "Examples" so we can get a set of test cases that sufficiently cover the the range of behavior; this is tricky.

What we have so far:

  • OCC starts at 0; it doesn't increment until we have a dose with following observations
    • I'd be open to starting OCC at 1 or maybe letting the user decide how to do it
  • When we say "observations", we mean EVID==0; EVID==2 don't count at this time
    • We could include EVID==2 but IMO it makes it more difficult to define the occasion
    • IMO, it should be rare to have a dose followed by EVID==2 record(s) followed by another dose with no EVID==0 in between
  • OCC increments at the dose record by looking ahead for following observation records; this was done intentionally

Questions:

  • Do you want OCC to show up automatically by default? or do you have to opt in?
    • Currently you get it by default
  • What should OCC be for records prior to the first dose?
    • Currently it is set to zero

How are BLQ observations handled?

  • If they are EVID==0 and uncommented, they will be considered as observations for OCC determination
  • If they are EVID==2 and uncommented, they will not be considered as observations for OCC determination
  • If they are commented, they will not be considered as observations for OCC determination
  • Users can "temporarily" comment these records (only during OCC calculation) through the comment argument

Some examples are included below and will form the test cases for the unit tests (not written yet).

Examples

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(mrgsolve)
#> 
#> Attaching package: 'mrgsolve'
#> The following object is masked from 'package:stats':
#> 
#>     filter
library(lastdose)

mod <- house(delta = 2, end = 24, outvars = "CP")

Single dose

dose <- evd(amt = 100)
data <- mrgsim_df(mod, dose, carry_out = "amt,evid,cmt")
data <- lastdose(data)
  • OCC starts at zero
  • OCC increments to 1 at the time of the first dose
data
#>    ID TIME evid amt cmt       CP TAD LDOS OCC
#> 1   1    0    0   0   0 0.000000   0    0   0
#> 2   1    0    1 100   1 0.000000   0  100   1
#> 3   1    2    0   0   0 4.247580   2  100   1
#> 4   1    4    0   0   0 4.228701   4  100   1
#> 5   1    6    0   0   0 3.861243   6  100   1
#> 6   1    8    0   0   0 3.496969   8  100   1
#> 7   1   10    0   0   0 3.164476  10  100   1
#> 8   1   12    0   0   0 2.863362  12  100   1
#> 9   1   14    0   0   0 2.590880  14  100   1
#> 10  1   16    0   0   0 2.344325  16  100   1
#> 11  1   18    0   0   0 2.121233  18  100   1
#> 12  1   20    0   0   0 1.919371  20  100   1
#> 13  1   22    0   0   0 1.736719  22  100   1
#> 14  1   24    0   0   0 1.571448  24  100   1


data <- mrgsim_df(mod, dose, carry_out = "amt,evid,cmt", recsort = 3)
data <- lastdose(data)

Here, OCC starts at 1 because it was the first record

data
#>    ID TIME evid amt cmt       CP TAD LDOS OCC
#> 1   1    0    1 100   1 0.000000   0  100   1
#> 2   1    0    0   0   0 0.000000   0  100   1
#> 3   1    2    0   0   0 4.247580   2  100   1
#> 4   1    4    0   0   0 4.228701   4  100   1
#> 5   1    6    0   0   0 3.861243   6  100   1
#> 6   1    8    0   0   0 3.496969   8  100   1
#> 7   1   10    0   0   0 3.164476  10  100   1
#> 8   1   12    0   0   0 2.863362  12  100   1
#> 9   1   14    0   0   0 2.590880  14  100   1
#> 10  1   16    0   0   0 2.344325  16  100   1
#> 11  1   18    0   0   0 2.121233  18  100   1
#> 12  1   20    0   0   0 1.919371  20  100   1
#> 13  1   22    0   0   0 1.736719  22  100   1
#> 14  1   24    0   0   0 1.571448  24  100   1

Multi-dose, with doses explicit in the data set

dose <- evd(amt = 100, ii = 12, addl = 1) %>% realize_addl()
data <- mrgsim_df(mod, dose, carry_out = "amt,evid,cmt")
  • OCC starts at 0 again, increments with the first dose
  • OCC increments at the time of the second dose because we have obserations following
lastdose(data)
#>    ID TIME evid amt cmt       CP TAD LDOS OCC
#> 1   1    0    0   0   0 0.000000   0    0   0
#> 2   1    0    1 100   1 0.000000   0  100   1
#> 3   1    2    0   0   0 4.247580   2  100   1
#> 4   1    4    0   0   0 4.228701   4  100   1
#> 5   1    6    0   0   0 3.861243   6  100   1
#> 6   1    8    0   0   0 3.496969   8  100   1
#> 7   1   10    0   0   0 3.164476  10  100   1
#> 8   1   12    0   0   0 2.863362  12  100   1
#> 9   1   12    1 100   1 2.863362   0  100   2
#> 10  1   14    0   0   0 6.838459   2  100   2
#> 11  1   16    0   0   0 6.573026   4  100   2
#> 12  1   18    0   0   0 5.982476   6  100   2
#> 13  1   20    0   0   0 5.416340   8  100   2
#> 14  1   22    0   0   0 4.901194  10  100   2
#> 15  1   24    0   0   0 4.434810  12  100   2

Multi-dose, with doses coded via addl

dose <- evd(amt = 100, ii = 12, addl = 1)
data <- mrgsim_df(mod, dose, carry_out = "amt,evid,cmt,addl,ii")
  • OCC increments at 12 hours, the time of the second dose
lastdose(data)
#>    ID TIME evid amt cmt ii addl       CP TAD LDOS OCC
#> 1   1    0    0   0   0  0    0 0.000000   0    0   0
#> 2   1    0    1 100   1 12    1 0.000000   0  100   1
#> 3   1    2    0   0   0  0    0 4.247580   2  100   1
#> 4   1    4    0   0   0  0    0 4.228701   4  100   1
#> 5   1    6    0   0   0  0    0 3.861243   6  100   1
#> 6   1    8    0   0   0  0    0 3.496969   8  100   1
#> 7   1   10    0   0   0  0    0 3.164476  10  100   1
#> 8   1   12    0   0   0  0    0 2.863362  12  100   2
#> 9   1   14    0   0   0  0    0 6.838459   2  100   2
#> 10  1   16    0   0   0  0    0 6.573026   4  100   2
#> 11  1   18    0   0   0  0    0 5.982476   6  100   2
#> 12  1   20    0   0   0  0    0 5.416340   8  100   2
#> 13  1   22    0   0   0  0    0 4.901194  10  100   2
#> 14  1   24    0   0   0  0    0 4.434810  12  100   2

Notice that we’ve (intentionally) made OCC to increment at 12 hours, even
though the TAD behavior is different with dose_first

lastdose(data, addl_ties = "dose_first")
#>    ID TIME evid amt cmt ii addl       CP TAD LDOS OCC
#> 1   1    0    0   0   0  0    0 0.000000   0    0   0
#> 2   1    0    1 100   1 12    1 0.000000   0  100   1
#> 3   1    2    0   0   0  0    0 4.247580   2  100   1
#> 4   1    4    0   0   0  0    0 4.228701   4  100   1
#> 5   1    6    0   0   0  0    0 3.861243   6  100   1
#> 6   1    8    0   0   0  0    0 3.496969   8  100   1
#> 7   1   10    0   0   0  0    0 3.164476  10  100   1
#> 8   1   12    0   0   0  0    0 2.863362   0  100   2
#> 9   1   14    0   0   0  0    0 6.838459   2  100   2
#> 10  1   16    0   0   0  0    0 6.573026   4  100   2
#> 11  1   18    0   0   0  0    0 5.982476   6  100   2
#> 12  1   20    0   0   0  0    0 5.416340   8  100   2
#> 13  1   22    0   0   0  0    0 4.901194  10  100   2
#> 14  1   24    0   0   0  0    0 4.434810  12  100   2

Multi-dose via addl, but no observations after the doses

dose <- evd(amt = 100, ii = 1, addl = 3)
data <- mrgsim_df(mod, dose, end = -1, add = c(0, seq(6,12)), carry_out = "amt,evid,cmt,addl,ii")
  • In this case, OCC doesn’t increment at the time of the dose at TIME==0;
  • This is because there wasn’t an observation after the first dose, so we
    (intentionally) don’t increment OCC at that point
lastdose(data)
#>   ID TIME evid amt cmt ii addl       CP TAD LDOS OCC
#> 1  1    0    0   0   0  0    0  0.00000   0    0   0
#> 2  1    0    1 100   1  1    3  0.00000   0  100   0
#> 3  1    6    0   0   0  0    0 16.48841   3  100   1
#> 4  1    7    0   0   0  0    0 15.81578   4  100   1
#> 5  1    8    0   0   0  0    0 15.08405   5  100   1
#> 6  1    9    0   0   0  0    0 14.36032   6  100   1
#> 7  1   10    0   0   0  0    0 13.66355   7  100   1
#> 8  1   11    0   0   0  0    0 12.99826   8  100   1
#> 9  1   12    0   0   0  0    0 12.36465   9  100   1

We can see this explicitly here

dose <- evd(amt = 100, ii = 1, addl = 3) %>% realize_addl()
data <- mrgsim_df(mod, dose, end = -1, add = c(0, seq(6,12)), carry_out = "amt,evid,cmt,addl,ii")

The rule is: OCC doesn’t increment unless there are observations after the dose

data
#>    ID TIME evid amt cmt ii addl        CP
#> 1   1    0    0   0   0  0    0  0.000000
#> 2   1    0    1 100   1  0    0  0.000000
#> 3   1    1    1 100   1  0    0  3.391488
#> 4   1    2    1 100   1  0    0  7.639068
#> 5   1    3    1 100   1  0    0 11.987160
#> 6   1    6    0   0   0  0    0 16.488412
#> 7   1    7    0   0   0  0    0 15.815780
#> 8   1    8    0   0   0  0    0 15.084048
#> 9   1    9    0   0   0  0    0 14.360321
#> 10  1   10    0   0   0  0    0 13.663554
#> 11  1   11    0   0   0  0    0 12.998257
#> 12  1   12    0   0   0  0    0 12.364650
lastdose(data)
#>    ID TIME evid amt cmt ii addl        CP TAD LDOS OCC
#> 1   1    0    0   0   0  0    0  0.000000   0    0   0
#> 2   1    0    1 100   1  0    0  0.000000   0  100   0
#> 3   1    1    1 100   1  0    0  3.391488   0  100   0
#> 4   1    2    1 100   1  0    0  7.639068   0  100   0
#> 5   1    3    1 100   1  0    0 11.987160   0  100   1
#> 6   1    6    0   0   0  0    0 16.488412   3  100   1
#> 7   1    7    0   0   0  0    0 15.815780   4  100   1
#> 8   1    8    0   0   0  0    0 15.084048   5  100   1
#> 9   1    9    0   0   0  0    0 14.360321   6  100   1
#> 10  1   10    0   0   0  0    0 13.663554   7  100   1
#> 11  1   11    0   0   0  0    0 12.998257   8  100   1
#> 12  1   12    0   0   0  0    0 12.364650   9  100   1

EVID 2 or 3

These currently don’t count for establishing an occasion dose

data1 <- data.frame(
  ID = 1,
  TIME = c(0, 1, 2, 3, 4, 5, 6, 7),
  EVID = c(0, 1, 0, 1, 2, 1, 1, 0),
  AMT = 100
)

lastdose(data1)
#>   ID TIME EVID AMT TAD LDOS OCC
#> 1  1    0    0 100  -1    0   0
#> 2  1    1    1 100   0  100   1
#> 3  1    2    0 100   1  100   1
#> 4  1    3    1 100   0  100   1
#> 5  1    4    2 100   1  100   1
#> 6  1    5    1 100   0  100   1
#> 7  1    6    1 100   0  100   2
#> 8  1    7    0 100   1  100   2

data2 <- data.frame(
  ID = 2,
  TIME = c(0, 1, 2, 3, 4, 5, 6, 7),
  EVID = c(0, 1, 0, 1, 3, 1, 1, 0),
  AMT = 100
)

lastdose(data2)
#>   ID TIME EVID AMT TAD LDOS OCC
#> 1  2    0    0 100  -1    0   0
#> 2  2    1    1 100   0  100   1
#> 3  2    2    0 100   1  100   1
#> 4  2    3    1 100   0  100   1
#> 5  2    4    3 100   1  100   1
#> 6  2    5    1 100   0  100   1
#> 7  2    6    1 100   0  100   2
#> 8  2    7    0 100   1  100   2

But if we find an observation before the next dose, we start the occasion
there

data3 <- data.frame(
  ID = 3,
  TIME = c(0, 1, 2, 3, 4, 5, 6, 7),
  EVID = c(0, 1, 0, 1, 3, 0, 1, 0),
  AMT = 100
)

lastdose(data3)
#>   ID TIME EVID AMT TAD LDOS OCC
#> 1  3    0    0 100  -1    0   0
#> 2  3    1    1 100   0  100   1
#> 3  3    2    0 100   1  100   1
#> 4  3    3    1 100   0  100   2
#> 5  3    4    3 100   1  100   2
#> 6  3    5    0 100   2  100   2
#> 7  3    6    1 100   0  100   3
#> 8  3    7    0 100   1  100   3

Multiple subjects

data4 <- bind_rows(data1, data2, data3) %>% mutate(CMT = 1)
data4 <- mrgsim_df(mod, data4, end = -1, carry_out = "amt,evid,cmt,addl,ii")

lastdose(data4)
#>    ID TIME evid amt cmt ii addl        CP TAD LDOS OCC
#> 1   1    0    0   0   1  0    0  0.000000  -1    0   0
#> 2   1    1    1 100   1  0    0  0.000000   0  100   1
#> 3   1    2    0   0   1  0    0  3.391488   1  100   1
#> 4   1    3    1 100   1  0    0  4.247580   0  100   1
#> 5   1    4    2 100   1  0    0  7.739580   1  100   1
#> 6   1    5    1 100   1  0    0  8.476281   0  100   1
#> 7   1    6    1 100   1  0    0 11.789956   0  100   2
#> 8   1    7    0   0   1  0    0 15.729012   1  100   2
#> 9   2    0    0   0   1  0    0  0.000000  -1    0   0
#> 10  2    1    1 100   1  0    0  0.000000   0  100   1
#> 11  2    2    0   0   1  0    0  3.391488   1  100   1
#> 12  2    3    1 100   1  0    0  4.247580   0  100   1
#> 13  2    4    3 100   1  0    0  0.000000   1  100   1
#> 14  2    5    1 100   1  0    0  0.000000   0  100   1
#> 15  2    6    1 100   1  0    0  3.391488   0  100   2
#> 16  2    7    0   0   1  0    0  7.639068   1  100   2
#> 17  3    0    0   0   1  0    0  0.000000  -1    0   0
#> 18  3    1    1 100   1  0    0  0.000000   0  100   1
#> 19  3    2    0   0   1  0    0  3.391488   1  100   1
#> 20  3    3    1 100   1  0    0  4.247580   0  100   2
#> 21  3    4    3 100   1  0    0  0.000000   1  100   2
#> 22  3    5    0   0   1  0    0  0.000000   2  100   2
#> 23  3    6    1 100   1  0    0  0.000000   0  100   3
#> 24  3    7    0   0   1  0    0  3.391488   1  100   3

Created on 2025-01-02 with reprex v2.1.1

@kylebaron kylebaron marked this pull request as draft December 20, 2024 17:18
@kylebaron kylebaron changed the title Implement Occasion calculation Implement occasion calculation Dec 21, 2024
@kylebaron kylebaron linked an issue Jan 6, 2025 that may be closed by this pull request
@kylebaron
Copy link
Collaborator Author

Check behavior for commented doses.

kylebaron and others added 5 commits January 9, 2025 08:39
The previous two commits covered most of the data.frame() calls.
Adjust two more spots to resolve the remaining test failures under R
3.6.
@kylebaron
Copy link
Collaborator Author

Thanks for help with stringsAsFactors again, @kyleam.

@kylebaron
Copy link
Collaborator Author

Waiting on @andersone1 @graceannobrien @michaelmcd18 to review behavior and either confirm or propose alternate behavior.

@kylebaron kylebaron marked this pull request as ready for review February 12, 2025 13:02
@kylebaron kylebaron requested a review from kyleam February 21, 2025 12:41
@kylebaron
Copy link
Collaborator Author

@kyleam This is ready for review; I'd be happy to talk through any of the C++ code.

Copy link
Contributor

@kyleam kyleam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the conceptual details you laid out in the PR description are a bit beyond me, but it looks like you did a very thorough job of documenting those in inst/test-data/occ/occ-data.R, the lastdose docstring, and tests/testthat/test-occ.R.

Aside from my few comments inline, this looks good to me code-wise (and inline with what I expect from inspecting the surrounding code).

Copy link
Contributor

@kyleam kyleam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updates look good to me, thanks.

(I know my comment about colliding test descriptions is still unresolved, but that's not blocking.)

@kylebaron
Copy link
Collaborator Author

All of @kyleam review comments have been resolved (I believe); only waiting on input from Metrum datascience (@andersone1 , @michaelmcd18 , @graceannobrien ) on behavior.

@kylebaron kylebaron merged commit c66be6b into main Mar 21, 2025
7 checks passed
@kylebaron kylebaron deleted the occ branch March 21, 2025 11:19
@kylebaron kylebaron mentioned this pull request Mar 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add OCC to lastdose output

2 participants