openpharma / mmrm Goto Github PK
View Code? Open in Web Editor NEWMixed Models for Repeated Measures (MMRM) in R.
Home Page: https://openpharma.github.io/mmrm/
License: Other
Mixed Models for Repeated Measures (MMRM) in R.
Home Page: https://openpharma.github.io/mmrm/
License: Other
To do:
h_record_all_output()
in file R/output.R
testthat/test-output.R
roxygen2
documentation chunks
pkgdown.yaml
To do:
h_cov_estimate()
in file R/covariance.R
testthat/test-covariance.R
roxygen2
documentation chunks
pkgdown.yaml
To do:
Line 132 in a9b4972
assert_data()
function including helper functions in R/assert_data.R
roxygen2
documentation chunks for each functiontesttthat/test-assert_data.R
pkgdown.yaml
the function namesTo do:
method
@details
section with md list explaining the possible return valuesTo do:
This is nice for handling the results and will make testing of the package easier.
To do:
glmmTMB
: glmmTMB uses print.glmmTMB
to present their objects, with the typical model output that one would expect. Very similar to what is spec'd below.lme4
: for lmList4 they use show
to print out a model summary (coded within the function body), for merMod they first spec a S3 print
method, and then call that in the S4 show
method body. The difference seems to be that lmList4 inherits the print method from lme
objects from the nmle
package.This is important since we get the fixed effect estimates table from that.
To do:
glmmTMB:::summary.glmmTMB
produces such objectglmmTMB:::print.summary.glmmTMB
prints itsummary
:
vcov()
methodVarCorr()
methodcoef()
methodh_coef_table()
helperprint
:
h_print_call()
a la glmmTMB:::.prt.call.glmmTMB
h_print_aic_list()
a la lme4:::.prt.aictab
To do:
toeph
and keep the homogeneous as toep
- carry this through belowLine 60 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 109 in b1b6122
Line 41 in 01ae7f9
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
r2stream
is not available outside of Roche, so just run SAS differently and save log as text file in the design folder as reference1e-3
for this testdepends on #glmmTMB/glmmTMB#810
To do:
clubSandwich
does it
gls
result objects (since our model is also a general least squares model)mmrm
package
nlme::gls
+ clubSandwich::vcovCR(type = "CR3")
nlme::gls
+ clubSandwich::vcovCR
(Please note that the weight used in gls is the inverse of the weight used in SAS and mmrm)Just fyi: Here is some simple example of gls()
with clubSandwich
- might be nice to play around with
library(nlme)
m <- gls(
value ~ time*method,
correlation =corCompSymm(form=~1|patient_id),
data = x,
na.action = na.omit,
method = "REML"
)
emmeans::emmeans(
m,
specs = ~time*method,
vcov = matrix(clubSandwich::vcovCR(m, type = "CR3"), nrow=6)
)
To do:
mmrm()
and related functions (to be detailed here)
mmrm()
and fit_single_optimizer()
in file R/fit.R
h_mmrm_tmb
and h_mmrm_tmb_data
in file R/tmb.R
/src/mmrm.cpp
mmrm()
: Add integration test where we compare results with SAS results
print()
method: It would be good if we could include a line that mentions the weights there
To do:
AIC.mmrm_tmb
method definition / codeRdpack
(see https://github.com/insightsengineering/hermes/blob/b5f50a1c1788527d289e1f10c4219a9c227d4449/DESCRIPTION#L65 as example)Rdmacros
(see https://github.com/insightsengineering/hermes/blob/b5f50a1c1788527d289e1f10c4219a9c227d4449/DESCRIPTION#L84 as example)To do:
h_build_formula()
in file R/formula.R
testthat/test-formula.R
roxygen2
documentation chunks
pkgdown.yaml
To do:
h_free_cores()
in file R/parallel.R
testthat/test-parallel.R
roxygen2
documentation chunks
pkgdown.yaml
To do:
?car::Anova
glmmTMB
implementation: https://github.com/glmmTMB/glmmTMB/blob/master/glmmTMB/R/Anova.Rcar
package code: https://github.com/cran/car/tree/master/RNote (from ?car::Anova?
Details):
The designations "type-II" and "type-III" are borrowed from SAS, but the definitions used here do not correspond precisely to those employed by SAS.
depends on #25
To do:
formula()
for mmrm_tmb
refit_multiple_optimizers()
in file R/fit.R
testthat/test-fit.R
roxygen2
documentation chunks
pkgdown.yaml
Idea: Add a function that replaces glmmTMB::glmmTMB()
call in
Line 37 in b0a1ff3
fit_single_optimizer
, let's see)
To do:
/src
directory@useDynLib
fit_single_optimizer()
mmrm_tmb()
etch_build_formula()
to not use 0+
in the correlation structure termh_cov_estimate()
mmrm_tmb(formula, data, start, control)
formula
is a glmmTMB
style formula which is translated internallyh_mmrm_tmb_control()
mmrm_tmb
objectthis->
because of https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members)mmrm_tmb()
get_unstructured
get_covariance_lower_chol
get_select_matrix
To do:
Line 60 in 709f392
Line 157 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 23 in 709f392
Line 33 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
tern.mmrm
-> deferred until we have the other 3 priority structures in.To do:
Motivation: Currently we are mapping the covariance structures to their C++ enum
integer codes here
Line 169 in b1b6122
Ideal outcome: Can we avoid this completely and just pass the cov_type
string (e.g. ar1
) directly to C++ and then decide based on that?
Otherwise: Somehow do the enum
conversion in a more robust way.
Please re-read the NEWS.md file and make it human readable, i.e. re-organise, change order, combine entries, group them if applicable, full sentence, no new entries on the old version (!) etc.
Use F. Hoffmann-La Roche Ltd., 2022 instead of openpharma. Can discuss again when somebody else is contributing code.
To do:
Line 60 in 709f392
Line 157 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 23 in 709f392
Line 33 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
For marketing/usability, we should provide users with a description as to how/why mmrm
would be more appropriate than the other available packages.
In particular: lme4
, nlme
, and glmmTMB
.
This documentation could fit well in a separate vignette "Comparison with other packages", so that potential users can immediately understand where/when we think our development can help them compared to packages with higher levels of adoption.
Material:
pbkrtest
: https://people.math.aau.dk/~sorenh/software/pbkrtest/Otherwise it is too cumbersome for users. Required for insightsengineering/tern.mmrm#79
To do:
To do:
h_covbeta_fun()
(this should be much simpler now)h_jac_col_as_matrix()
(factored out helper)h_jac_list()
fit_model()
a la mmrm()
and rename it to mmrm()
mmrm
class to existing classes for return object, such that we know that we have the Jacobian in theremmrm_tmb()
to h_mmrm_tmb()
to avoid confusiondeviance()
AIC()
BIC()
h_quad_form_vec()
h_gradient()
h_df_1d_list()
df_1d()
h_quad_form_mat()
h_df_md_list()
h_md_denom_df()
df_md()
recover_data.mmrm()
needs
terms()
methodemm_basis.mmrm()
- also here more needs to be doneNote that we don't need:
h_general_jac_list()
(since no other jacobian calculations besides from our TMB fit)vars()
(since we can stick with the formula interface for this package)h_vcov_theta()
(since we have that explicitly as part of the object already)mmrm()
as another wrapper of fit_model()
(since we don't need vars interface and we can just calculate Jacobian in fit_model()
directly)diagnostics()
(this is more a wrapper for tern.mmrm
)To do:
To do:
adh
and keep the homogeneous as ad
- carry this through belowLine 60 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 109 in b1b6122
Line 41 in 01ae7f9
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
r2stream
is not available outside of Roche, so just run SAS differently and save log as text file in the design folder as reference --> change to use mmrm results for testing since homegeneous ad cov is not available in SASTo do:
mmrm_tmb_methods
for othersh_mmrm_tmb()
- mainly useful if you don't need Satterthwaite d.f. or try other optimizers, "barebones" functionemmeans
package, refer to emmeans_support
Background:
It can happen that the design matrix that is created from the model formula does not have full rank. This can be due to some visit and arm combinations not being present in the data set, or too many (categorical) covariates that lead to exact collinearities between columns.
Reprex:
dat <- fev_data[11:25, ]
fit <- h_mmrm_tmb(
formula = FEV1 ~ RACE + SEX + ARMCD * AVISIT + us(AVISIT | USUBJID),
data = dat
)
gives error
Error in h_mmrm_tmb_assert_start(tmb_object) :
negative log-likelihood is NaN at starting parameter values
Comparing this with the lm
result:
linmod <- lm(
formula = FEV1 ~ RACE + SEX + ARMCD * AVISIT,
data = dat
)
summary(linmod)
we can see that the coefficients for ARMCDTRT:AVISITVIS2
and ARMCDTRT:AVISITVIS3
are not defined due to singularities. We also note that lm()
has the corresponding argument singular.ok = TRUE
controlling whether this works or gives an error.
To do:
lm()
is organizing this
lm.fit()
, compiled code is called (see http://madrury.github.io/jekyll/update/statistics/2016/07/20/lm-in-R.html for a summary) which is implicitly handling the singular design matrix.coef()
and vcov()
methods have complete = TRUE
argument which controls if the NA coefficients should be representedsummary()
method finally computes the variance covariance matrix only for the identified coefficients, based on the QR decomposition. the resulting coefficient table has all NA for the non-identified coefficients. when printed it also says (2 not defined because of singularities)
e.g.h_mmrm_tmb_data()
accept_singular = TRUE
x_matrix
should already be the reduced one - need to be careful how the attributes map formula contents to columnsfull_frame
should contain still the full data framex_cols_aliased
element to say which coefficients have been removed alreadyaccept_singular = TRUE
to h_mmrm_tmb_control()
mmrm
component
print
coef
vcov
df_1d
, df_md
: these only refer to the actually estimated coefficients and their covariance matrixh_coef_table
summary
tern.mmrm
digests this and adapt if neededTo do:
fit_model()
in file R/fit.R
testthat/test-fit.R
roxygen2
documentation chunks
pkgdown.yaml
To do:
h_summarize_all_fits()
in file R/fit.R
testthat/test-fit.R
logLik
method in file R/tmb-methods.R
roxygen2
documentation chunks
pkgdown.yaml
To do:
h_labels()
in file R/labels.R
testthat/test-labels.R
roxygen2
documentation chunks
pkgdown.yaml
On enableR:
The following notes and warnings are generated when R CMD check
is run against this package.
* checking compilation flags used ... NOTE
Compilation used the following non-portable flag(s):
โ-Werror=format-securityโ โ-Wp,-D_FORTIFY_SOURCE=2โ
โ-Wp,-D_GLIBCXX_ASSERTIONSโ
* checking compiled code ... WARNING
File โmmrm/libs/mmrm.soโ:
Found โabortโ, possibly from โabortโ (C)
Objects: โmmrm.oโ, โtest-covariance.oโ, โtest-runner.oโ,
โtest-utils.oโ, โtmb.oโ
Found โprintfโ, possibly from โprintfโ (C)
Objects: โmmrm.oโ, โtest-covariance.oโ, โtest-runner.oโ,
โtest-utils.oโ, โtmb.oโ
File โmmrm/libs/mmrm.soโ:
Found no calls to: โR_registerRoutinesโ, โR_useDynamicSymbolsโ
Compiled code should not call entry points which might terminate R nor
write to stdout/stderr instead of to the console, nor use Fortran I/O
nor system RNGs.
It is good practice to register native routines and to disable symbol
search.
See โWriting portable packagesโ in the โWriting R Extensionsโ manual.
To do:
abort
and printf
symbols come from. I checked the C++ code and we don't have any of this or similar. Maximum we have is Rf_error
which is standard R interface for an error so cannot be the problem. Note that this warning does not occur on R 4.2On Daniel's Macbook with R CMD check --as-cran
on the built package tarball:
* checking compiled code ... NOTE
Datei โmmrm/libs/mmrm.soโ:
Found no calls to: โR_registerRoutinesโ, โR_useDynamicSymbolsโ
It is good practice to register native routines and to disable symbol
search.
See โWriting portable packagesโ in the โWriting R Extensionsโ manual.
To do:
Moreover parallel processes are not allowed in CRAN:
โ | 2 47 | fit [0.7s]
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Error (test-fit.R:175:3): mmrm falls back to other optimizers if default does not work
Error in `.check_ncores(cores)`: 3 simultaneous processes spawned
Backtrace:
1. testthat::expect_silent(mmrm(formula, data_small))
at test-fit.R:175:2
9. mmrm::mmrm(formula, data_small)
10. mmrm::refit_multiple_optimizers(fit, n_cores = n_cores, accept_singular = accept_singular)
13. parallel::mclapply(...)
14. parallel:::.check_ncores(cores)
Error (test-fit.R:185:3): mmrm fails if no optimizer works
Error in `.check_ncores(cores)`: 3 simultaneous processes spawned
Backtrace:
1. testthat::expect_error(...)
at test-fit.R:185:2
7. mmrm::mmrm(formula, data_small, reml = FALSE)
8. mmrm::refit_multiple_optimizers(fit, n_cores = n_cores, accept_singular = accept_singular)
11. parallel::mclapply(...)
12. parallel:::.check_ncores(cores)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
To do:
To do:
Line 60 in 709f392
Line 157 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 23 in 709f392
Line 109 in b1b6122
Line 33 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
To do:
Line 60 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 109 in b1b6122
Line 41 in 01ae7f9
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
r2stream
is not available outside of Roche, so just run SAS differently and save log as text file in the design folder as referenceplease refer to the discussion on tern#628. This error should be more informative or fixed before the call.
To do:
mmrm
To do:
Line 60 in 709f392
Line 157 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 23 in 709f392
Line 33 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
The hex sticker renders twice on index.html.
This is because we need to add
<p align="center"> <img src="man/figures/logo.svg" align="right" alt="mmrm-logo" style="width: 150px"> </p>
into README.Rmd
so that the .md
file in GitHub will show the sticker.
Then, in the website, pkgdown builder adds a header with logo.svg
for all of the pages rendered to HTML. In this way, index.html
has two hex stickers.
I thought that maybe we could follow some sort of approach with conditional code depending on output format, and we'd want the .md
to render with the above code, and the .html
to render without.
Since .Rd
is the first step in the .Rmd -> .md -> html
pipeline, the knitr::opts_knit
are identical for both .md
and .html
output, se we couldn't use this to differentiate.
If we care about this appearance issue, we would probably need to remove the code from the .Rmd
and inject it into the .md
file. Doing this by hand would be quite unsustainable.
Note: In that case we need covariance structure formula part like this:
us(visit | group / subjid)
To do:
group
needs to be a factor, drop levelsgroup
informationtheta
vector (e.g. store each covariance matrix in contiguous part)To do:
@keywords internal
tag and must not have examples (since those won't run)
#' packages having keyword internal and matching specific type
rd_index_installed <- function(pkg, type = NULL) {
db <- tools::Rd_db(pkg)
elo <- tools:::.build_Rd_index(tools:::Rd_contents(db), type = type)
elo$Name
}
man_files <- function(path) {
list.files(file.path(path, "man"), full.names = TRUE, pattern = ".Rd")
}
rd_index <- function(path = ".") {
all_docs <- man_files(path)
res <- vapply(all_docs, FUN.VALUE = logical(1), FUN = function(x) {
lines <- readLines(x)
any(grepl("keyword\\{.*internal\\}", lines))
})
sort(gsub(".Rd$", "", basename(names(res[!res]))))
}
badge <- function(path = ".") {
all_docs <- man_files(path)
res <- vapply(all_docs, FUN.VALUE = logical(1), FUN = function(x) {
lines <- readLines(x)
any(grepl("figure\\{lifecycle", lines))
})
sort(gsub(".Rd$", "", basename(names(res[res]))))
}
path <- "."
pkg <- "mmrm"
# has a badge but it's internal
setdiff(badge(path), rd_index(path))
# is in documentation index but has no badge
setdiff(rd_index(path), badge(path))
# unexported functions in the doc index
setdiff(rd_index(path), sort(getNamespaceExports(pkg)))
setdiff(rd_index_installed(pkg), sort(getNamespaceExports(pkg)))
Motivation: We still have trouble fitting a true MMRM (i.e. without residual variance, and obtaining correct Satterthwaite) with glmmTMB
, see https://github.com/glmmTMB/glmmTMB/blob/satterthwaite_df/glmmTMB/vignettes/satterthwaite_unstructured_example2.Rmd
So one idea is to directly use TMB
since then we have more freedom how to define the model. Plus TMB
supports Hessian and Jacobian calculations (with latest 1.9.0 version) which should help us with the Satterthwaite d.f. calculations to avoid numDeriv::jacobian()
.
Material:
TMB:::setupRStudio()
to setup R Studio (once)To do:
TMB
tutorialnumDeriv
approach and see if we get same or better results as with glmmTMB
MakeADFun()
, see also https://stackoverflow.com/questions/42800829/how-do-i-include-more-objective-functions-in-my-tmb-cpp-fileTMBad
framework
intern = TRUE
in MakeADFun()
beta
estimates (coefficients or covariance matrix) anymore from the sdreport
because it has been integrated out in C++ already. However it is really faster.Hi @dinakar29 , unfortunately me again :-) there seem to be problems with pdflatex:
https://github.com/openpharma/mmrm/runs/6211390165?check_suite_focus=true
?
To do:
Line 60 in 709f392
Line 157 in 709f392
Line 208 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 80 in 709f392
Line 23 in 709f392
Line 33 in 709f392
mmrm/tests/testthat/test-tmb.R
Line 256 in 709f392
To do:
fit_single_optimizer()
in file R/fit.R
nlminb
could be usedNelder-Mead
as an option since that does not make use of the automatic differentation in glmmTMB
testthat/test-fit.R
start
values you can use list(theta = c(3, 0.5))
e.g. in the case of AR1 modelroxygen2
documentation chunks
pkgdown.yaml
fit_single_optimizer <- function(formula,
data,
start = NULL,
optimizer = c("L-BFGS-B", "BFGS", "CG", "nlminb"))
and then inside the function:
control <- glmmTMB::glmmTMBControl(
optimizer = if (optimizer == "nlminb") stats::nlminb else stats::optim,
optArgs = if (optimizer == "nlminb") list() else list(method = optimizer),
parallel = 1L
)
Idea: Instead of replicating code across the package to e.g. look up number of subjects etc. it is better to have a getter function that can be used e.g. as component(object, "n_subjects")
.
To do:
component(object, name)
function in https://github.com/openpharma/mmrm/blob/main/R/tmb-methods.R (although it is not an S3 method makes sense here I guess)
getS3method("getME", class = "glmmTMB")
but no need to do it exactly like thatmmrm
package and see where we are directly accessing mmrm_tmb
contents.
object$beta_est
name
option to the component function
name == "beta"
and that will return object$beta_est
component()
callsA declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.