Skip to content

Conversation

@roninsightrx
Copy link

@roninsightrx roninsightrx commented Dec 5, 2024

Hi @billdenney , this is a PR for allowing the return of individual fit info from the half-life fits in the NCA. This is something I absolutely need for a project I’m working on, but I think it’s very useful info to have in general for PKNCA users, both for checking/confirming correct lambda-z/half-life calculation manually, and e.g. for inclusion in reports. Let me know what you think.

Since the data that needs to be returned from the half-life calculation won’t fit in the current data units passed between the functions (data.frames) I instead attached it to the core data units as attribute. This seems to me the easiest option here, but let me know what you think. If you prefer not to use attributes, an alternative option is to just pass back the coefficients from the fit in the existing data.frame. Then at least the curve fits can be re-constructed afterwards, although it’s not as convenient to the user.

FYI, this PR is a work-in-progress, just wanted to see if this aligns with your ideas for the package. Will do some more testing and add unit tests if you agree with the overall approach.

Below is a minimal example. Thanks

## Minimal example to test functionality
library(PKNCA)
library(dplyr)
library(ggplot2)

## Load the PK & dose data
d_conc <- as.data.frame(datasets::Theoph) %>%
  mutate(Subject = as.numeric(as.character(Subject)))
d_dose <- d_conc[d_conc$Time == 0,]
d_dose$Time <- 0
conc_obj <- PKNCAconc(d_conc, conc ~ Time|Subject)
dose_obj <- PKNCAdose(d_dose, Dose ~ Time|Subject)
data_obj <- PKNCAdata(conc_obj, dose_obj)

## Calculate the NCA parameters
res <- pk.nca(data_obj)

## Basic plot
obs_fit <- bind_rows(
  purrr::map(
    .x = attr(res$result, "individual_fits"),
    .f = function(x) {
      tmp <- x$data %>%
        dplyr::mutate(
          prediction = exp(log_prediction),
          conc = exp(log_conc)
        )
    }
  )
)
ggplot(obs_fit, aes(x = time, y = conc)) +
  geom_point() +
  geom_line(aes(y = prediction)) +
  facet_wrap(~Subject)

@billdenney
Copy link
Member

@roninsightrx, thanks for providing the code. Can you please clarify the motivating example? Is the core need to get the residuals or the intercept, or something else? With tlast, clast.pred, lambda.z, and lambda.z.time.first I think you will have all the details of the model (but, maybe there is something missing).

I hesitate to add a fit object to the outputs by default because sometimes people use PKNCA for simulation results with 10,000+ individuals. The fit could be large in memory in such cases. It would be preferred to have an optional return rather than returning by default.

@roninsightrx
Copy link
Author

roninsightrx commented Dec 5, 2024

The reason is that I need to be able to inspect the fits done within PKNCA, to possibly manual remove outliers based on fit plots, or potentially change the adjusted r-squared threshold. I was actually surprised that this was not included already in PKNCA. I don't do a lot of NCAs myself, but some folks mentioned this is a required step in the NCA process for filings. Also Phoenix and PKanalix have this option, see e.g. screenshot from this PKanalix tutorial.

To create those same plots I mostly need the intercept and slope from the fit. The other data I put in the attribute is mostly for convenience, but I could see the impact on speed that would have. Would you be more open to the second option I mentioned: to (optionally) store just the 2 coefficients from the fit in the data.frame. This would avoid having to add attributes to the data.frame. It would just mean a little more data processing on the user-end afterwards, but I could live with that.

image

@roninsightrx
Copy link
Author

O wait, now I see that with the variables you mention I can reconstruct the curve as well. I didn't realize I did have all the information. Let me explore that.

@roninsightrx
Copy link
Author

roninsightrx commented Dec 5, 2024

Yes, managed to do it with existing data. Thanks a bunch!

Below is what I used to generate the plots, perhaps would be nice to include in one of the vignettes for other users that are looking for this? Could even imagine having something like an extract_fit_data() function for convenience.

res <- pk.nca(data_obj)

fit_data <- res$result %>%
  tidyr::pivot_wider(names_from = PPTESTCD, values_from = PPORRES) %>%
  dplyr::filter(!is.na(lambda.z)) %>%
  dplyr::select(Subject, tlast, clast.pred, lambda.z, lambda.z.n.points, lambda.z.time.first) %>%
  dplyr::mutate(
    intercept = log(clast.pred) + tlast * lambda.z, 
    slope = -lambda.z
  )
predictions <- dplyr::bind_rows(
    purrr::pmap(
      .l = fit_data,
      .f = function(...) {
        x <- tibble(...)
        grid <- seq(from = x$lambda.z.time.first, to = x$tlast, length.out = 40)
        data.frame(
          Subject = x$Subject,
          time = grid,
          prediction = exp(x$intercept - grid * x$lambda.z)
        )
      }
    )
ggplot(res$data$conc$data, aes(x = Time, y = conc)) +
  geom_point() +
  geom_line(data = predictions, aes(x = time, y = prediction), colour = "blue") +
  facet_wrap(~Subject)

image

@billdenney
Copy link
Member

I'm glad that you were able to make it work for you!

FYI, your code will work for parallel studies, but it won't work for crossover studies or studies with additional analytes (e.g. metabolites). You'll want to use grouping variables.

A long time ago, I had a lot of visualizations, but most of the comments that PKNCA received related to minor formatting differences such as wanting to change colors. So, those were removed. Based on some upcoming work, I expect that visualizations will come back into PKNCA.

As an aside, I would recommend some changes to future-proof your PKNCA-related code:

  • res$result would follow the expected PKNCA API more closely by using as.data.frame(res).
  • res$data$conc$data would preferably be as.data.frame(as_PKNCAconc(res))

Both of those are long-term stable, while using the $ indexing into the list structure could change since it's not part of the API. (There are no changes planned for this, but the suggested methods will always work.)

@roninsightrx
Copy link
Author

Thanks Bill for the context and pointers!
I realize the limitations of the above code, this was just a basic example. I indeed have more elaborate parsing code for groupings already implemented for my project.
Let me know if at some point you want to bring back more plotting-related code, happy to help. In my opinion, the plotting code itself should not be part of this package (I think you mention that somewhere in a vignette as well), but I think it would be helpful to provide some helper-functions in PKNCA that prepare the data for plotting.

@billdenney
Copy link
Member

I am planning to add plotting, table, and figure generation to a separate package. PKNCA will stay focused on the calculations; a related package will be created for reporting.

@kylebaron
Copy link

I had to do something similar in my project. I couldn't see how to get the intercept from the output like @roninsightrx did and ended up re-doing the the lambda.z regression for each patient (I didn't have that many). I did this b/c I wanted to interpolate some data into the analysis (sponsor wanted AUC to a timepoint that wasn't actually collected) and better or worse this is what I did. Whether this is a good idea or not (or maybe there is some existing functionality that I don't know about) I think it would be nice have API available to do this.

@billdenney
Copy link
Member

Here's a benefit of using GitHub. A feature that I didn't know people wanted or needed is helpful, and the community is coming together to talk about it. I'll open an issue and tag you both in it to work through the specifics.

@kylebaron, FYI, the aucint calculations should do the interpolation for you without manual effort. If you knew those and it didn't do what you're meaning, could you please open a discussion or issue to clarify them?

@kylebaron
Copy link

Thanks, @billdenney ; I suspected there was something in there (I expect this to be pretty tricked out since it's coming from you), but burned enough of your time asking super basic questions. With the deadline and inheriting some legacy code, it really was easier to roll my own on this one and figure out the power features another day. You can tell how much NCA I do (!) but will definitely take time to check into this for the next time around. Thanks for all of your work on PKNCA and interacting with users on GitHub. I agree ... I love having this platform for seeing what others are doing. I always learn something new!

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.

3 participants