Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closes #73: add ADVS example and update ADSL example and corresponding specs #83

Open
wants to merge 23 commits into
base: main
Choose a base branch
from

Conversation

Fanny-Gautier
Copy link
Collaborator

@Fanny-Gautier Fanny-Gautier commented Jan 23, 2025

Pull Request

DESCRIPTION GOES HERE

Before you submit your pull request, take a look at the following checklist. Many thanks for your contribution!

  • Title: Place Closes #<insert_issue_number> at the beginning of your PR title. Use the Edit button in the top-right if you need to update.
  • Linked Issue: Ensure the related issue is linked in the "Development Section" on the right-hand side.
  • First Contribution: If this is your first contribution, add yourself to the DESCRIPTION file.
  • Impact on Examples: If your updates impact any examples, review locally for warnings or errors in the impacted example pages.
  • Merge Conflicts: Developers should address any merge conflicts and merge upon successful review.
  • New Packages: If new packages were used, ensure they are included in the DESCRIPTION file's Imports section.
  • Updated Examples: If you added or updated an example, ensure it runs on the latest CRAN release versions of all packages used.
  • Testing Instructions: Provide instructions on how to test the code if necessary.

Copy link
Collaborator

@bms63 bms63 left a comment

Choose a reason for hiding this comment

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

I am so excited about this @Fanny-Gautier !!!! I will try to get to early next week.

@pharmaverse/admiral we got some new integrated examples coming online as a result of @Fanny-Gautier hard work. Anyone got time to help out with the review?

This aligns ADSL closer to admiral ADSL and introduces ADVS as another example

image

@Fanny-Gautier Fanny-Gautier marked this pull request as ready for review January 24, 2025 09:42
adam/adsl.qmd Outdated
Comment on lines 69 to 70
where_sep_sheet = FALSE,
quiet = TRUE
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we mention why we are setting these arguments like this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

where_sep_sheet explained
quiet dropped and thus left to the default value since we do not have any warnings to mute

ds_list = list("dm" = dm),
predecessor_only = FALSE, keep = TRUE)
ds_list = list("dm" = dm_suppdm, "suppdm" = dm_suppdm),
predecessor_only = FALSE, keep = FALSE)
head(adsl_preds, n=10)
Copy link
Collaborator

Choose a reason for hiding this comment

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

could we display this in a prettier way using reactable package? https://github.com/pharmaverse/admiraldiscovery/blob/v0.3.0/R/interactive.R here is what Daniel uses it in admiraldiscovery

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's not looking that great without customizing it:
image

Could you please suggest where I could save the customization that it's not directly in the code itself ?
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

to compare with head()
image

Copy link
Collaborator

Choose a reason for hiding this comment

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

we could just create another folder called functions at the top-level. and store the fancy code in there. and then your code could call the reactable code from the functions folder

Copy link
Collaborator

Choose a reason for hiding this comment

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

but yeah agree the vanilla reactable looks sad

adam/adsl.qmd Outdated Show resolved Hide resolved
Now we have the base dataset, we can start to create some variables.
There are a few options to create grouping variables and their corresponding numeric variables.

Option 1: We can start with creating the subgroups using the controlled terminology, in this case `AGEGR1`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we make use of how the logs section does this? This is a pretty long section
image

Copy link
Collaborator

Choose a reason for hiding this comment

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

At least "Option 1" should be bold.

Now we have sorted out what we can easily do with controlled terminology it is time to start deriving some variables. Here you could refer directly to using the `{admiral}` template and [vignette](https://pharmaverse.github.io/admiral/cran-release/articles/adsl.html) in practice, but for the purpose of this end-to-end ADaM vignette we will share a few exposure derivations from there. We derive the start and end of treatment (which requires dates to first be converted from DTC to DTM), the treatment duration, and the safety population flag.
### Exposure derivations

Now we have sorted out what we can easily do with controlled terminology it is time to start deriving some variables.
Copy link
Collaborator

Choose a reason for hiding this comment

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

So good! I'm excited!!

adam/adsl.qmd Show resolved Hide resolved
adam/adsl.qmd Outdated
derive_var_merged_exist_flag(
dataset_add = ex,
by_vars = exprs(STUDYID, USUBJID),
new_var = SAFFL,
condition = (EXDOSE > 0 | (EXDOSE == 0 & str_detect(EXTRT, "PLACEBO")))
) %>%
drop_unspec_vars(metacore) # This will drop any columns that aren't specified in the metacore object
drop_unspec_vars(metacore) # This will drop any columns that are not specified in the metacore object
Copy link
Collaborator

Choose a reason for hiding this comment

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

ummm...should this step just go at the end or am i missing something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

dropped

Copy link
Collaborator

@bms63 bms63 left a comment

Choose a reason for hiding this comment

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

Got a bit reviewed. Looking so good.

Maybe we could use the link package like how we do in the blog to help make functions and packages linkable? https://github.com/pharmaverse/blog/blob/main/posts/2025-01-17_1.2_admiral.../1.2_admiral_release.qmd

@rossfarrugia and @pawelru this is nice package we use on blogging site that automatically creates links for functions and packages. very handy - maybe should become standard (add to checklist?)

@jimrothstein
Copy link

jimrothstein commented Jan 25, 2025

As someone with no clinical trials experience, anything remotely like jargon leaps out.
(Please note: I can do many more like these, but if too trivial, tangenial, annoying I won't)

  • harmonize ?? You mean merging, mapping, comparing, identifying? .... 2 different things into one?

- [`{metacore}`](https://atorus-research.github.io/metacore/): provides harmonized metadata/specifications object.

  • I assume SAS people understand this, I don't

    # Combine Parent and Supp - very handy! ----

  • This and next paragraph are excellent and clear, more than a running start to decifiering the actual R code. More like this!

    The first derivation step we are going to do is to pull through all the columns that come directly from the SDTM datasets.

  • Another excellent description, though do not know what controlled terminology means.

    Option 1: We can start with creating the subgroups using the controlled terminology, in this case `AGEGR1`.

  • Ah! So AGEGP1 has something to do with a group and an AGE variable. Didn't register at first.

    Because this controlled terminology is written in a fairly standard format we can automate the creation of `AGEGR1`.

  • In line 102, change , (comma) to and
    image

  • Lines 114-117
    More excellence, by looking both paragraph and R code I now see what is meant by lookup_table (conditions) and have clue what admiral::derive_vars_cat() can do. So very useful to have BOTH words and simple code . The R switch statement is always tricky to me. Possibly add a link? https://rdrr.io/r/base/switch.html

image

@pawelru
Copy link
Collaborator

pawelru commented Jan 27, 2025

this is nice package we use on blogging site that automatically creates links for functions and packages. very handy - maybe should become standard (add to checklist?)

I have no strong opinion honestly speaking. In general, I'm not a fan of extending dependencies and keep things small and simple but if this helps and it's robust enough - let's go with it!

@rossfarrugia
Copy link
Contributor

this is nice package we use on blogging site that automatically creates links for functions and packages. very handy - maybe should become standard (add to checklist?)

I have no strong opinion honestly speaking. In general, I'm not a fan of extending dependencies and keep things small and simple but if this helps and it's robust enough - let's go with it!

Same here @bms63 - happy if you want to go ahead and make an issue for this one to get it added across all the blogs, if anyone has time to take it on.

Copy link
Collaborator

@bundfussr bundfussr left a comment

Choose a reason for hiding this comment

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

Is it possible to add a TOC to the pages like in the admiral vignettes?

Do we need to repeat so much information from the admiral vignettes?
It's not clear to me what we want to show. Do we want to show how to create an ADSL or ADVS dataset or how admiral and other pharmaverse packages work together?
In the former case I wonder why we show this in two places (admiral vignettes and pharmaverse examples). In the later case I would reduce the number of admiral function calls and focus on those where other packages are used like checks or writing xpt files or those where other packages provide alternative functions like deriving grouping variables.


head(adsl_ct, n=10)
```

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would swap option 1 and option 2 because using derive_vars_cat() is more general and more reviewer friendly while create_cat_var() works only in some scenarios.

Now we have the base dataset, we can start to create some variables.
There are a few options to create grouping variables and their corresponding numeric variables.

Option 1: We can start with creating the subgroups using the controlled terminology, in this case `AGEGR1`.
Copy link
Collaborator

Choose a reason for hiding this comment

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

At least "Option 1" should be bold.

adam/adsl.qmd Outdated Show resolved Hide resolved
grp_var = AGEGR1, num_grp_var = AGEGR1N) %>%
grp_var = AGEGR1, num_grp_var = AGEGR1N)

head(adsl_ct, n=10)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The new variables should be displayed in the output.

mutate(ARM = if_else(ARM == "Screen Failure", NA_character_, ARM),
TRT01P = if_else(TRT01P == "Screen Failure", NA_character_, TRT01P)
)
out_var = RACEN)

head(adsl_ct, n=10)
Copy link
Collaborator

Choose a reason for hiding this comment

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

The new variable should be displayed in the output.

head(adsl_raw %>% select(STUDYID, USUBJID, TRTSDTM, TRTSTM, TRTSTMF, TRTSDT, TRTEDTM, TRTETMF, TRTEDT, TRTDURD, SAFFL), n=10)
```

This call returns the original data frame with the column `TRTSDTM`, `TRTSTMF`, `TRTEDTM`, and `TRTETMF` added.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should be moved or rephrased because there is more than one call in the chunk above and more than the four columns are created.

Option 3: We can also solve this subgroups task with custom functions.

```{r grouping_option_3}
format_agegr1 <- function(var_input) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would rename var_input to age. I think this is easier to read.


```{r demographics}
adsl_preds <- build_from_derived(metacore,
ds_list = list("dm" = dm),
predecessor_only = FALSE, keep = TRUE)
ds_list = list("dm" = dm_suppdm, "suppdm" = dm_suppdm),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think an explanation would be helpful. Why is dm_suppdm specified twice?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We combined [DM] and [SUPPDM] at the beginning with combine_supp() to show case this fantastic function.
In the specs it is specified from which domain the variables come from:
image
We then get the metacore object with the corresponding domain to look for. Since we combined [DM] and [SUPPDM], we need to specify that [DM] variables come from the dm_suppdm dataset that we just created, and [SUPPDM] variables also come from dm_suppdm.
image
Setting ds_list = list("dm" = dm_suppdm) only does not gather the variables from [SUPPDM]
image
If we want to separate ds_list = list("dm" = dm, "suppdm" = suppdm) we would need to transpose [SUPPDM] to get the parameters (QNAM) as variables.
image

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

will try to think how to shorter the explanation without screenshots 🤓

adam/advs.qmd Outdated Show resolved Hide resolved

The other way to assign the parameter level values is using the `metatools` package with the `{metacore}` objects
that we created at the beginning. The function `metatools::create_var_from_codelist()` is used in below exemple.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we show the metadata?

Maybe we should also mention the requirements for using the metadata. As far as I remember the codelist can be either a pair of code/decode values or a list of permitted values. I think for using the metatools function code/decide pairs are needed.

@bms63
Copy link
Collaborator

bms63 commented Jan 28, 2025

Is it possible to add a TOC to the pages like in the admiral vignettes?

@Fanny-Gautier I think this is definitely needed

Do we need to repeat so much information from the admiral vignettes? It's not clear to me what we want to show. Do we want to show how to create an ADSL or ADVS dataset or how admiral and other pharmaverse packages work together? In the former case I wonder why we show this in two places (admiral vignettes and pharmaverse examples). In the later case I would reduce the number of admiral function calls and focus on those where other packages are used like checks or writing xpt files or those where other packages provide alternative functions like deriving grouping variables.

Yes, primary goal is to show how the different packages work together. Secondary goal is to have more ready to go examples for teaching workshops.

I think expanding the script is handy especially as @Fanny-Gautier has created a more robust specification file for the two datasets - we could handwave away/hide some of the derivations and say see the full script if it is indeed too wordy. TBD!

Understood on we are repeating information here, but folks coming to this site I would think would like having the full script available to them rather than an abbreviated script and then have to go to admiral to get more derivations/information on that dataset. Maintaining alignment is something giving me anxiety.

Regarding workshop goal - for ADSL we found a lot of stuff was pretty out of date and spec was pretty barebones. This update really brings stuff up to date for us. Hopefully, if someone wants to teach another workshop on programming ADSL from a spec to xpt file it will be almost 100% ready to go.

Happy to keep discussing!! Thanks for the feedback

@rossfarrugia
Copy link
Contributor

Hi all, once all the above review comments are resolved I'm happy for this one to be approved/merged. I only had chance to review high level but I understand the perspective of having more "workshop-ready" examples, and this is something I also hear reflected in feedback from Posit colleagues. Thanks @Fanny-Gautier for all the efforts!! 🌟

@rossfarrugia
Copy link
Contributor

@Fanny-Gautier as per chat today please name the specs file with "safety_specs" rather than ADSL, ADVS so that i can then add ADAE later in same file

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.

ADaM example for ADVS and update ADSL (as being used for upcoming R/Pharma workshop)
6 participants