From d15ed14cf8dde1dd834d92d94a2cc76fa0b6dd82 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Mon, 26 Sep 2022 10:34:09 +0200 Subject: [PATCH 01/45] start with clean slated package --- DESCRIPTION | 31 +++++++++-------- NAMESPACE | 10 +++--- NEWS.md | 5 ++- R/hello.R | 61 ---------------------------------- R/package.R | 13 ++++++++ man/hello.Rd | 20 ----------- man/plumber_api.Rd | 18 ---------- man/shiny_app.Rd | 15 --------- man/tern.gee-package.Rd | 27 +++++++++++++++ tests/testthat.R | 10 ++---- tests/testthat/dummy.R | 3 ++ tests/testthat/shiny-app/app.R | 1 - tests/testthat/test-api.R | 30 ----------------- tests/testthat/test-hello.R | 5 --- tests/testthat/test-shiny.R | 25 -------------- vignettes/hello.Rmd | 12 ------- vignettes/introduction.Rmd | 10 ++++++ 17 files changed, 80 insertions(+), 216 deletions(-) delete mode 100644 R/hello.R create mode 100644 R/package.R delete mode 100644 man/hello.Rd delete mode 100644 man/plumber_api.Rd delete mode 100644 man/shiny_app.Rd create mode 100644 man/tern.gee-package.Rd create mode 100644 tests/testthat/dummy.R delete mode 100644 tests/testthat/shiny-app/app.R delete mode 100644 tests/testthat/test-api.R delete mode 100644 tests/testthat/test-hello.R delete mode 100644 tests/testthat/test-shiny.R delete mode 100644 vignettes/hello.Rmd create mode 100644 vignettes/introduction.Rmd diff --git a/DESCRIPTION b/DESCRIPTION index 96327d0..ea71c98 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,29 +1,32 @@ Type: Package Package: tern.gee -Title: TLGs using Generalized Estimating Equations (GEE) -Version: 0.0.0.9001 +Title: Tables and Graphs for Generalized Estimating Equations (GEE) Model Fits +Version: 0.0.9001 Date: 2022-08-22 -Authors@R: - person("insightsengineering", , , "insightsengineering@example.com", role = c("aut", "cre")) -Description: Create TLGs using Generalized Estimating Equations (GEE). +Authors@R: c( + person("Daniel", "Sabanés Bové", , "daniel.sabanes_bove@roche.com", role = c("aut", "cre")), + person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")) + ) +Description: Create tables and graphs for GEE model fits. License: Apache License 2.0 | file LICENSE URL: https://github.com/insightsengineering/tern.gee/ BugReports: https://github.com/insightsengineering/tern.gee/issues Depends: - R (>= 3.6) + R (>= 3.6), + tern (>= 0.7.9) Imports: - plumber, - shiny, - stringr + checkmate, + gee, + rtables (>= 0.5.2), + stats Suggests: - future, - httr, knitr, - shinytest, - testthat (>= 2.0) + rmarkdown, + testthat (>= 3.1), + vdiffr VignetteBuilder: knitr -biocViews: +Config/testthat/edition: 3 Encoding: UTF-8 Language: en-US LazyData: true diff --git a/NAMESPACE b/NAMESPACE index 62a3c9a..e1bba4c 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,6 +1,8 @@ # Generated by roxygen2: do not edit by hand -export(hello) -export(plumber_api) -export(shiny_app) -importFrom(utils,packageName) +import(checkmate) +import(rtables) +importFrom(gee,gee) +importFrom(rtables,add_colcounts) +importFrom(stats,acf) +importFrom(tern,f_conf_level) diff --git a/NEWS.md b/NEWS.md index a975c52..824388f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,3 @@ -# tern.gee 0.0.0.9001 -### New features +# tern.gee 0.0.9001 -* New package \ No newline at end of file +* First release of the `tern.gee` package. diff --git a/R/hello.R b/R/hello.R deleted file mode 100644 index 58efee5..0000000 --- a/R/hello.R +++ /dev/null @@ -1,61 +0,0 @@ -#' Personal greeting -#' -#' @description Greet a person and appropriately capitalize their name. -#' -#' @param name Your name (character string; e.g. "john doe"). -#' -#' @return A character string, capitalized to title case. -#' @export -#' -#' @examples -#' hello("james bond") -hello <- function(name = "your name") { - name <- stringr::str_to_title(name) - print(paste("Hello,", name)) -} - -#' Personal greeting as a Shiny app -#' -#' @description Greet a person and appropriately capitalize their name -#' as a Shiny app. -#' -#' @return Shiny app showcasing the personal greeting feature. -#' @export -#' -shiny_app <- function() { - ui <- shiny::fluidPage( - shiny::textInput("name", "What is your name?"), - shiny::actionButton("greet", "Greet"), - shiny::textOutput("greeting") - ) - - server <- function(input, output, session) { - output$greeting <- shiny::renderText({ - shiny::req(input$greet) - hello(shiny::isolate(input$name)) - }) - } - - shiny::shinyApp(ui, server) -} - -#' Personal greeting as a Plumber API -#' -#' @importFrom utils packageName -#' -#' @description Greet a person and appropriately capitalize their name -#' as a Plumber API. -#' -#' @param ... Additional arguments to plumber::pr_run() -#' -#' @return Plumber API showcasing the personal greeting feature. -#' @export -#' -plumber_api <- function(...) { - plumber::pr_run( - plumber::plumb_api( - package = packageName(), name = "hello" - ), - ... - ) -} diff --git a/R/package.R b/R/package.R new file mode 100644 index 0000000..399bd80 --- /dev/null +++ b/R/package.R @@ -0,0 +1,13 @@ +#' `tern.gee` Package +#' +#' Create tables and graphs for GEE model fits. +#' +"_PACKAGE" + +#' @import checkmate +#' @import rtables +#' @importFrom gee gee +#' @importFrom rtables add_colcounts +#' @importFrom stats acf +#' @importFrom tern f_conf_level +NULL diff --git a/man/hello.Rd b/man/hello.Rd deleted file mode 100644 index e7eb880..0000000 --- a/man/hello.Rd +++ /dev/null @@ -1,20 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hello.R -\name{hello} -\alias{hello} -\title{Personal greeting} -\usage{ -hello(name = "your name") -} -\arguments{ -\item{name}{Your name (character string; e.g. "john doe").} -} -\value{ -A character string, capitalized to title case. -} -\description{ -Greet a person and appropriately capitalize their name. -} -\examples{ -hello("james bond") -} diff --git a/man/plumber_api.Rd b/man/plumber_api.Rd deleted file mode 100644 index 84ef92b..0000000 --- a/man/plumber_api.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hello.R -\name{plumber_api} -\alias{plumber_api} -\title{Personal greeting as a Plumber API} -\usage{ -plumber_api(...) -} -\arguments{ -\item{...}{Additional arguments to plumber::pr_run()} -} -\value{ -Plumber API showcasing the personal greeting feature. -} -\description{ -Greet a person and appropriately capitalize their name -as a Plumber API. -} diff --git a/man/shiny_app.Rd b/man/shiny_app.Rd deleted file mode 100644 index 8171c58..0000000 --- a/man/shiny_app.Rd +++ /dev/null @@ -1,15 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hello.R -\name{shiny_app} -\alias{shiny_app} -\title{Personal greeting as a Shiny app} -\usage{ -shiny_app() -} -\value{ -Shiny app showcasing the personal greeting feature. -} -\description{ -Greet a person and appropriately capitalize their name -as a Shiny app. -} diff --git a/man/tern.gee-package.Rd b/man/tern.gee-package.Rd new file mode 100644 index 0000000..adf28b9 --- /dev/null +++ b/man/tern.gee-package.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/package.R +\docType{package} +\name{tern.gee-package} +\alias{tern.gee} +\alias{tern.gee-package} +\title{\code{tern.gee} Package} +\description{ +Create tables and graphs for GEE model fits. +} +\seealso{ +Useful links: +\itemize{ + \item \url{https://github.com/insightsengineering/tern.gee/} + \item Report bugs at \url{https://github.com/insightsengineering/tern.gee/issues} +} + +} +\author{ +\strong{Maintainer}: Daniel Sabanés Bové \email{daniel.sabanes_bove@roche.com} + +Other contributors: +\itemize{ + \item F. Hoffmann-La Roche AG [copyright holder, funder] +} + +} diff --git a/tests/testthat.R b/tests/testthat.R index a13b4c1..ff07f92 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,9 +1,3 @@ pkg_name <- "tern.gee" -if (requireNamespace("testthat", quietly = TRUE)) { - library(testthat) - reporter <- MultiReporter$new(list( - CheckReporter$new() - )) - test_results <- test_check(pkg_name, reporter = reporter) - saveRDS(test_results, "unit_testing_results.rds") -} +library(pkg_name, character.only = TRUE) +testthat::test_check(pkg_name) diff --git a/tests/testthat/dummy.R b/tests/testthat/dummy.R new file mode 100644 index 0000000..366b074 --- /dev/null +++ b/tests/testthat/dummy.R @@ -0,0 +1,3 @@ +test_that("placeholder", { + expect_true(TRUE) +}) diff --git a/tests/testthat/shiny-app/app.R b/tests/testthat/shiny-app/app.R deleted file mode 100644 index 25dc784..0000000 --- a/tests/testthat/shiny-app/app.R +++ /dev/null @@ -1 +0,0 @@ -tern.gee::shiny_app() diff --git a/tests/testthat/test-api.R b/tests/testthat/test-api.R deleted file mode 100644 index a76ccab..0000000 --- a/tests/testthat/test-api.R +++ /dev/null @@ -1,30 +0,0 @@ -test_that("API greets the person", { - host <- "127.0.0.1" - port <- 9000 - - # Start the API - future::plan(future::multisession) - future::future( - tern.gee::plumber_api(host = host, port = port) - ) - Sys.sleep(3) - - # Make request - res <- httr::GET( - url = paste0( - "http://", - host, - ":", - port, - "/echo" - ), - query = "name=tim" - ) - - # Get response - result <- httr::content(res)[[1]] - - # Compare - expected <- "Hello, Tim" - expect_identical(result, expected) -}) diff --git a/tests/testthat/test-hello.R b/tests/testthat/test-hello.R deleted file mode 100644 index 4dccc45..0000000 --- a/tests/testthat/test-hello.R +++ /dev/null @@ -1,5 +0,0 @@ -test_that("hello greets the entity", { - result <- hello("foo") - expected <- "Hello, Foo" - expect_identical(result, expected) -}) diff --git a/tests/testthat/test-shiny.R b/tests/testthat/test-shiny.R deleted file mode 100644 index 6fed2d6..0000000 --- a/tests/testthat/test-shiny.R +++ /dev/null @@ -1,25 +0,0 @@ -test_that("The Shiny App returns a proper greeting", { - library(shinytest) - app <- ShinyDriver$new( - "shiny-app/", - loadTimeout = 1e5, - debug = "all", - phantomTimeout = 1e5, - seed = 123 - ) - app$getDebugLog() - - # Set input - app$setInputs(name = "john") - app$setInputs(greet = "click") - output <- app$getValue(name = "greeting") - - # test - expect_equal(output, "Hello, John") - - # wait for the process to close gracefully - # this allows covr to write out the coverage results - p <- app$.__enclos_env__$private$shinyProcess - p$interrupt() - p$wait() -}) diff --git a/vignettes/hello.Rmd b/vignettes/hello.Rmd deleted file mode 100644 index 5ec24d9..0000000 --- a/vignettes/hello.Rmd +++ /dev/null @@ -1,12 +0,0 @@ - ---- -title: "Hello" -date: "`r Sys.Date()`" ---- - -Hello World! - -```{r} -library(tern.gee) -hello("tern.gee!") -``` diff --git a/vignettes/introduction.Rmd b/vignettes/introduction.Rmd new file mode 100644 index 0000000..8ea852a --- /dev/null +++ b/vignettes/introduction.Rmd @@ -0,0 +1,10 @@ +--- +title: "Introduction to `tern.gee`" +date: "`r Sys.Date()`" +--- + +Placeholder for introduction vignette. + +```{r} +library(tern.gee) +``` From 1d8089af90528438e3b393d3ec9dcddbf43626bc Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Mon, 26 Sep 2022 12:34:27 +0200 Subject: [PATCH 02/45] first stab at fit_gee --- NAMESPACE | 1 + R/data.R | 15 ++++ R/fit_gee.R | 167 +++++++++++++++++++++++++++++++++++++++ _pkgdown.yml | 12 +++ data/fev_data.rda | Bin 0 -> 8979 bytes man/fev_data.Rd | 27 +++++++ man/fit_gee.Rd | 42 ++++++++++ tests/testthat/fit_gee.R | 15 ++++ 8 files changed, 279 insertions(+) create mode 100644 R/data.R create mode 100644 R/fit_gee.R create mode 100644 data/fev_data.rda create mode 100644 man/fev_data.Rd create mode 100644 man/fit_gee.Rd create mode 100644 tests/testthat/fit_gee.R diff --git a/NAMESPACE b/NAMESPACE index e1bba4c..5b84bea 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +export(fit_gee) import(checkmate) import(rtables) importFrom(gee,gee) diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..da037e4 --- /dev/null +++ b/R/data.R @@ -0,0 +1,15 @@ +#' Example dataset for `tern.gee` package. +#' +#' Measurements of FEV1 (forced expired volume in one second) is a measure of +#' how quickly the lungs can be emptied. +#' Low levels of FEV1 may indicate chronic obstructive pulmonary disease (COPD). +#' @format A `tibble` with 800 rows and 7 variables: +#' +#' - `USUBJID`: price, in US dollars. +#' - `AVISIT`: visit number. +#' - `ARMCD`: treatment, `TRT` or `PBO`. +#' - `RACE`: 3-category race. +#' - `SEX`: sex. +#' - `FEV1_BL`: FEV1 at baseline (%). +#' - `FEV1`: FEV1 at study visits. +"fev_data" diff --git a/R/fit_gee.R b/R/fit_gee.R new file mode 100644 index 0000000..2b31572 --- /dev/null +++ b/R/fit_gee.R @@ -0,0 +1,167 @@ +vars_gee <- function(response = "AVAL", + covariates = c(), + id = "USUBJID", + arm = "ARM", + visit = "AVISIT") { + list( + response = response, + covariates = covariates, + id = id, + arm = arm, + visit = visit + ) +} + +control_gee <- function(tol = 0.001, + maxiter = 25, + silent = TRUE, + scale.fix = TRUE, + scale.value = 1) { + list( + tol = tol, + maxiter = maxiter, + silent = silent, + scale.fix = scale.fix, + scale.value = scale.value + ) +} + +#' @keywords internal +build_formula <- function(vars) { + assert_list(vars) + covariates_part <- paste( + vars$covariates, + collapse = " + " + ) + arm_part <- if (is.null(vars$arm)) NULL else vars$arm + rhs_formula <- paste( + c(arm_part, covariates_part), + collapse = " + " + ) + stats::as.formula(paste( + vars$response, + "~", + rhs_formula + )) +} + +#' @keywords internal +build_family <- function(regression) { + assert_string(regression) + + result_object <- switch( + regression, + logistic = stats::binomial(link = "logit"), + stop(paste("regression type", regression, "not supported")) + ) + + result_class <- paste0("tern_gee_", regression) + + list( + object = result_object, + class = result_class + ) +} + +#' @keywords internal +build_cor_details <- function(cor_str, vars, data) { + assert_string(cor_str) + assert_list(vars) + assert_data_frame(data) + + result_str <- switch( + cor_str, + "unstructured" = "unstructured", + "toeplitz" = "stat_M_dep", + "compound symmetry" = "exchangeable", + "auto-regressive" = "AR-M", + stop(paste("correlation structure", cor_str, "not available")) + ) + + result_mv <- switch( + cor_str, + "unstructured" = 1, + "toeplitz" = nlevels(data[[vars$visit]]), + "compound symmetry" = 1, + "auto-regressive" = 1 + ) + + list( + str = result_str, + mv = result_mv + ) +} + +order_data <- function(data, vars) { + assert_data_frame(data) + assert_list(vars) + + if (is.character(data[[vars$visit]])) { + message(paste("visit variable", vars$visit, "will be coerced to factor for ordering")) + message("order is:") + data[[vars$visit]] <- factor(data[[vars$visit]]) + cat(toString(levels(data[[vars$visit]]))) + } + if (is.factor(data[[vars$id]]) || is.character(data[[vars$id]])) { + data[[vars$id]] <- as.integer(as.factor(data[[vars$id]])) + } + assert_numeric(data[[vars$id]]) + + right_order <- order(data[[vars$id]], data[[vars$visit]]) + data[right_order, ] +} + +#' Fit a GEE Model +#' +#' @param vars (`list`)\cr see [vars_gee()]. +#' @param data (`data.frame`)\cr input data. +#' @param regression (`string`)\cr choice of regression model. +#' @param cor_struct (`string`)\cr assumed correlation structure. +#' @param control (`list`)\cr see [control_gee()]. +#' +#' @details The correlation structure can be: +#' * `unstructured`: No constraints are placed on the correlations. +#' * `toeplitz`: Assumes a banded correlation structure, i.e. the correlation +#' between two time points depends on the distance between the time indices. +#' * `compound symmetry`: Constant correlation between all time points. +#' * `auto-regressive`: Auto-regressive order 1 correlation matrix. +#' +#' @return Object of class `tern_gee` as well as specific to the kind of regression +#' which was used. +#' @export +fit_gee <- function(vars = vars_gee(), + data, + regression = c("logistic"), + cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive"), + control = control_gee()) { + formula <- build_formula(vars) + + regression <- match.arg(regression) + family <- build_family(regression) + + data <- order_data(data, vars) + data[[".id"]] <- data[[vars$id]] + + cor_struct <- match.arg(cor_struct) + cor_details <- build_cor_details(cor_struct, vars, data) + + fit <- gee::gee( + formula = formula, + id = .id, + data = data, + na.action = na.omit, # That is the only option here. + tol = control$tol, + maxiter = control$maxiter, + family = family$object, + corstr = cor_details$str, + Mv = cor_details$mv, + silent = control$silent, + scale.fix = control$scale.fix, + scale.value = control$scale.value + ) + + structure( + fit, + class = c(family$class, "tern_gee", class(fit)) + ) +} diff --git a/_pkgdown.yml b/_pkgdown.yml index ce33f8f..b0f2810 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -7,3 +7,15 @@ navbar: right: - icon: fa-github href: https://github.com/insightsengineering/tern.gee + +reference: + - title: Analysis Helper Functions + desc: these functions are useful to help definining the analysis + contents: + - control_gee + - vars_gee + + - title: Model Specific Functions + desc: these functions help with fitting or extracting results from specific models + contents: + - fit_gee diff --git a/data/fev_data.rda b/data/fev_data.rda new file mode 100644 index 0000000000000000000000000000000000000000..585676a4722c6c6c7cd19065da601172f09bb5c0 GIT binary patch literal 8979 zcmai2RZJWJlV03)aSDY(@#0e4oyA>>yF-gC?k*&-w2sZU(DL zopI)RjNBFeFuU~tPE>$ehijMx0I3RaBvM?93KP!FIFG=cv;tyHo3Wb3iBX6lA#w4I9JRMpz{D*`jZ6$za#U@8e zNla;Oc35c!jQ6TG=RuPwmCmg;cPCKVNPC>4a)g1UHg^Szv@VGQ%78(_vLu{V;27vJ zI~at)IsG$7_ShaoVttS_3xbCGLhF=d_eOBkUR6s<%iiDBp+9AAo8 z;G9AVD>F8tXdqBZfuawC-O36BfGC1WNxD&Y6(F9Gijt?J}e?&|`=>uhTIwIR8a`8zh>MDFe<_76gibgXU)E zhK#I}z*2J<&_dDp+n_mal_SpCxjR7+34#V5r9UkEmWlz` z?mP&&kEk{Qz6m}aMOAEoi3nFlwNlaqxxAUP&V)&mu7^dmII>?wUUwm;JT1H@3ddfZ znmRDDyqrz495rc5O&(7&a)w?MnW0}-E?PwlNApu;Wda*cumnPkdXRcge6m=7o^DA5 zbsmM(&YR*Tu6zxHim0e+N_mJ4C%!y@M6O&8MMI^Wm;%R<$SDODen@IO55DP@E|@BW z0nXQ))q!OgfY$tE9`e}A+LtxQZq`6nAs~s0xekkiE)k7`J&j6F=Z-*BmepY zn4lV|qd;HGT8ej_r!EKKRv*d#sv%dW#fV^5h5xffOLnGRYZS(qKe3HTGWsWh>X^=` zs6K6!tj!nm+NPA94Slo-7iRORwp5ezZ)n3{7E3on%*zYK$IjHj6byS|Ncc7GNL;C6?_l*uw&Th!{xi5fjjOl@+LuUyQ-VM!@|QNQHLOl z*)wBskC1-4rGY1s0T#VJoSpbS1vECxPb_K$AtM8DbYW4b{gGAR14SsMr3aY}vRYjZ zO^;Qz+If8wWU2oV;QzqYH@LgHL1}5}56*)`|A+pch^TE=aa4u%FDCAQ5T<@2oxd!N zGKEaPL@DEzof?_ATMXbLgG)$B@c{^n#6KA(M5qrB2qh68xAG%2 zfWoG7MB5`HeObcH^+4>kv}rR$UFcl)YJpFTq=Zipp`Zvv>woClRVVhJcJdG+1e0?z z;bLboAnp;f^~SObmK9-1giMAZ)hxQaSr64u7Y`sKQ}h^x$6;cNPSiU5e3BgzdJmQ& z;A@w_77_gwA|8fMc%q6mNCpcL5sExwGQiP>T6;k9s6CeL33r7>7i!XejcMMKO_}X|A+a~pi1OUOefRbOdLE#6Pj?cm@ z6LAIKM(Z}z_At6^OuK|$&csItWr4Iy;*+cp&9Bwp;@j|Jn+Wgo-G%y1Ykf0iIUKx7 zu0MS5q}>-L2{9eyy}$T6<2Be(+BQo5W)s%E=b2d!YFj>jQm>csHcP_5L)}=}Qy;E& zHjL#Yy5>bVRO0XF+E>o*vx|s6i!M=qeC12Z*FBqlQ(S+8a+_3o%O8@$`wA0BB%#vO z>~C(x&Qk?>6+Acb@lq(NcWy{;X}tAbWOkjG3^eG|Xnyt&2d76#-?QX?s#HTF%i zqEO9(^jCKs_U{%bXp^_9PO^v!9C}>}(^k`k&XQwu)z>uT6!qVlUR6zGHe^%$;=nHF zTq{_q_mDoHzCSl{gX=9vQ9Zrwv*+fs{CC<#7#FJA<+-n~7W|PRg~d>IP4Zknu9g*z z8@?UwyJ~Z0n|AlRu6wG3sa-VQsNPe)=__ewD1))tW7=f1jp~V^;o;QUvucKJ3Aa`~ z);Il21}%1)o7DTbgf*=b()M>n&Yx5AF(;GM?m5hp!sk*z@O5_0+hKq8GJ}W9>n2Ao zJgaeDc$k%1CtCK)_-&=%#^EF*xpPx0XUG0!(fGL%)fFoq2{%eD&3y1;2Vw2EBz}n^0`}8me665S%8_(zi`|Uo`f; z6d$saW~DDRWsa&N_jVu2OW~xfm`|{?P@$8D`o+wqGS)dwengRqPG+I$MbgO6OjvYa zH6OdIp~V_w)KXloh-WX=-dct}OmMUF=qmkvRB&@$*)m2U7Ozi#QL3G}ZyAhqI{G^P zjbQO7R5rJ=#!)Al-km(acV+%;00=pg!)NfLIK$m*4HDEQ%l(9G6l$S9KXHM^Hb})K zVAbpAe4JRPTFgi5*m-60!u|;}qUY8^;O7;E>m)ft&&B#dwG?fC>nmO%h8#cGk1;u= zPgxGZ5kHA@mXzwODieK!TQ6O0!@O2Vy>1Xun!P{-nSU22!p`o24MgSi>E#<8@*WJ@ z-ZLb5T0|s3UbN|*6A%0ZEcf%v@jtg|;5&bLcA&27Dqp@T3MGN$&*9C`Mo>$qFpx!$ zK-d=(2}_`4Xrka2$gN!x#IO1JtqHJ{BAY3y^!f`Zn4MMGp8v7CBI9kcXAQim;68ln zT#qkA|K>76$zvBsg~Dq*Y{!qvv6co0g~nUa`uJO#it>6(kQq#RdRq)(L-QCh*by zbMv1u(xq)9JYX}9H%_CD9O;-lw$f^yi|~e`q137=1RsYr*p@}zq~JW1JL5ct^e?&#W3ytOL_Sfu}?~@x;KEt zSC5qUT)d$hZayE1Mcj-3wMy_jMYABXvDb3uGlohEWNzz69lv-0WpyZgI?85jw@ zdQ}#mJSz!4!?arM*Z7M#)qodfaD_iT+lR(AHIt1>%q zv~HMFq=Fe_C+P3js3C)(J)d_&7o{y{WFu|~I$U@`Wbp_;&MQ_1AOr){Miq8yL+p;s zk=>C$mgn2OcnoLi_7|NKkSNnjow#4ZvyS1fH|w5E8IIWij4<{LBgl3;cKFgezR4kc z5VUR)lwXUdhq84d#()f>yk_#DlRcfcMGDCb&R$}t>O)SVOnl)<5k;=DjOT9*Q}2wz zoI}|u7mU0eh-Lqa>0jYN>agN>XtY&A0*KZPb0r4YkibDlss>Y}>MEQN<)2_&JOrF1 z!hIVC<%{f4pi@CDVN-}!H^jer1T>TzgD{-r;KX3r8_+_gO?p=Mg3h6)1pKQZVHS*1 zEUGrCdC+s6zl&pjd*Q7bLO~KeC|Vw&7u-WpNRP=+#Q8@~WRDf`{ARfYA1V70ezE-C z3m`b2v{@rdCFQ!T%Ne!>{|*ndS1kk^OM2+Uk#K8MXTBLQNxNOjYLWh=tIwE7*csI> z@t%(n4wB68u`=4jOEOD5{)~fq>KtneN{;@Sd8;cSIi0Nh1>^q1>20mLl#+!V~(oDhn3PXF%)Zo|;JJQdpACC* zl|^wBS)?H;tkBo{=Dfu8VWk9Yt$N~a?(@5rdBr0w@t119>_n8>ep-cD#E)aSrJxY< zywe}l1B^OL=;=Ku5+;~46>i%cqsl^CRhQzK1VtbLFMH`Y z=Z+q+FDk<6(Wpq8Tog6Sd+5N8N4#yAJx3Q&5Y1@wW!Q;qWT?_e-gsL6B~$pq5bppo zwvz_g&8z-Qy+_vubYo>~)2+kvFw6cjMjHi2i$ab>B-*7EVy(+>dzhMo&$wP+vnz=m zTxHy8vFHsqJ;kb$xg;Xwgf{Vq`#=-6v#y`-rD;yvhn`G@@g)%iax!+Ec+0UTezOk6 z!r1|=Rmmr^+6qbD`#6lpk4LK+{;CFsJ8j+?j>e8rPSEqG%Y7k0;^^VhV6T7T5ID-& zcDIgDlSw^6Avg12IQA24bC?;RoXEfB$c87UP8_StQLe@wm6#HiRFj+VW9TW}RP9EV zj>$LrSN$2BI6imTXGO`^q%?tk8mawO6?|$3QUq@%-ENqK0_J0LuTib-jtk(7* zDaS9c@=1yRg*$->NXFD`G8I6r{O$+ES>X$g?M3-fA%_v-x#_Zw=@1 zTQMXHnH_WjH_vwnMI?1^HI7Yhc#aOZ6Q%H0EC|#`HiQ>q5XYHS>b_udbX&P>$mYY| zNuOZ1)i#7@H!gx=8q5oWD4}7H0+1zfQbVE<)~9Pe<-AF!WLO^x4}b@t=VoaUdsDEP zm1y?e+Ja@FZB_UL!m(n5UO!|7SI~foz&EKRaRQU+!3h;7RSz&F#(DZBV!G0MsFpgqmfO7UCMVmE0ZUtD2 zLRPJ3yfU`l3+5aVH^8Ps*6S!CFxqAa4}-M@hB65F6Ftqqfw8g9o-nEpBlov}^Z-Q> zUSPZ`s;CS-0!0x6G(h$r!wbw4M}=q2la&&J+B8p636*E=T3Owh!{^RW+-^6$nyHiL z{|a|U#Nx>oo_VHF58vcV^XwEF(Y1+}ypdT}a%%s3KW=e*#Wr``Q6B2>7s>YSW~hE7 zUqU5vfd6Co`jhAE<5jV&)l+mV`MO)u3No?~i3Z2*bT3l9|+=;EZ zL!l?_=Ozl4t0=3Apsu;?pP88pr13o7CMDX$^+gnMGF%mOm258D#(npr1%jDhbVeR* zzHG@|!zrY9sQ}lQ%Ck~>ov6%M>Kbn-^ggmaia$%k3*R#*`dn$lM!8v2@gr08u(qF_ zY(eYJ+q%Rw*^|{Sv=3VQ$&J{+n?$FKYf3^C2PZsogMg)i)9roijCxxxErDrX2i2ep z1pM-Z%MTS)35ReY$<4Q%BuE1eE~+;7oL|;CQoWgmquTmL-kwap)}`x(%&;(Wlm|Y{ z2W^DX2B@6oNVU}sUZeE`)~DojPRBnKsL@9Jn5b?4J@lPUA233J@)pe5O`5vJOW}c9 zO@D#$iH@H>V8B>lLlelDoA>)(Ns~R$+lS=oJk{i^_R@lFrjcrl`C2zlw@;-wt|SaY z;n9;QvZ`LqEOuSweE%wgvs8!#VOuiKB061+x;AXg@P~eaz&ZKd2E>SC<33VmWyGLT zvc60um4((A2kwyNDHr`z%WTYneR|=!i(TsoGX1+7Y;MkL&)TJ-DMb*Oa0Jr?diVwSI!x@x~ z80H@md1k@>qZ5Wf8DGYx6tnj!cib(C(CfPD`>^R$NoJg{`t2#PPq85jFIzv2xFs6z+xhdt-QAJWu8j8oe?^cR5*pK7ROlCG+yxgFb{QDZD!M-fP z#K!#27s0pN_~g$F?n*m8(;1Hnt@$O1XJKlKxy$T2p6o4#C?+PwyOwo2K;jY(?#J1$ z;4XK#)yg!Ya6Vf1s&=B9gi8G}dPw@KtSRWlwRKmubRdw0YbKMi z|IOR`?@Wodop8TM#eqD5+^Ex|MdVAmB^tWI2Q-5U(6MaX# zAG~6P&#!kp8mww=u3I(*<-rw=`$)%p@_649*^!2%16z|vPB6e0(QmEhRlq$umGW=n zMk(zXP>4r&pv>0wJBC50oS>^$yLin)7GYZA#+$bwtoAI>7NapoPGy3-;#2gqjcc$x zTq)5BS>__{_&`x!(|FdL#u3Yaj)e2h=csztFS`U{j**_RCdGF8_&hdhpP4FdBg0My zVOXiK=^k2Ttq?5B!l|(?6z_lmt0B4{ry0iSSjyd~Ry1jqNkFd4lZ;8y%LTifo?5>p z-PtG>)qeb{(V~x&H?>NnYlwZtv$Ht{3_a;EXMd@YJX|~Hxz~5k+`hkDcl@5hYdB5p zwsGVAn@B*OKr1)&t3Wn!UD+RR&tH=8ug~VbQEJ7PihD0@4~jjvc;6HtM@P*cTG@>b zt=x#yD1(Y|^cWjA+Z+e>jRY|ClXs0@r%_9r`>VH4+fBz02FU3{d2eVQi?d#HB#zZu zx=O)Xu_bcKYqz#88dL_iYfSYey}-||rFwj{otFDCNGsqx;d4}4Cg_+~XDa{w%4?i6 zh3p-9cQ9N6TsPUblir@Se(dz#*jkyD2Zvh(WFn$Sx*xS`QUGBA!0s@d68p`V$+}X< zC0cZfrv8%A8L)p-e7SM2`==BU`j`H>kv&p|(ThV0c+6aLJh-pqEL6B{F}KL9Y|XZH z{2IjI>PxllJ9>9hxRa%px%Y+*T;GsRe=|o!t8SI({-5Fc;cv2)t$oP`gSPDEoy53R z_8iOM9KnM9VnTdWC}ww7JWyP{)%gr)=3+#);Cl&pTCZ~ADO4C;oxOBklqHE>{g6Bf zk}WJO?JB59W$ylKqkq?nLD~Sc=xaS(F|pU<-u24AX<0|hBj!|LkyGw@;EE!_f5S1i zjEm^E|C1zRvc~%5tIKDGK;5Anwd^$ek*n~|$emV1g4 zDGPUhx9y!&!oxo?&#qI688fVnf1#;i*5Ll|S9+h!U1)7uL_w@l2?Q$2C zI~yXpeJ_^QT+!*9IP|>KoW)wFdRq4r6BL)K-__Ddh8K14tm2D83O%_yK))*Wy%YYz z#%+AKW-*~%oUOGYD`_!>>ju6X6X_&fpR!J!vH^+?)%xkx3fUq`%2i;9f1`mt{!F0D zKKums}RL;Mm8_MUaxa`Y=s zg`?cM)5eoq#R=R)#SRcDFbGWd?jGCE+!{|@*SbQ|ToCm8%m;^-CL4;wlcewA6~|~}>EgSacLr2$x=K-!GCiKu$!VK?BMc{6;6{U1b*q2M0m#0VnkK0-BQ=#`QoVW+9+SEFYU0- zPPH6Zop%gh9KvnrMhep1p`4_AKg0JljflzLw%j}=oFT4ayCxIIcWcP%+U$S)4ANM1 z^e*^5YEY=B(>+PHk%>P>J>kF-E$8$8x3{S8sRcL6vMifDpT>J|`}>YlBzI_?^s5)4 zKqr<4D7#QDg<&A=b@(H2dv%`qXKp^ZCIftf&I;A>r6$U9$et}gv| zTtu8;moD)R{SyOI_aEsk9}=}z+#91_1c-Xs%sDh`e#a7B zo==;#+sYFUabm30ogL&U5LHK3+XL$Jll1_nDT_7O4A0ONk*U;kPkx zsdA1@49l7Q9avZtJ#Rm;^PFbR3_??Rjj$s8GR;fkZB`0>?Vr0klxEsUfs{nti*4=S zDft_Hsy)=hTRx~_qxQ*T5s64whc{5#MmT~&DS=U84$-^LhMW(}f^%P4^@L5)66f#U za{F#EKiZ~gzcCKW(DS2r3F#S2o?OHZ9u?O;-UdR$&d>0~i98!^KI@Ols77n%pzm>y zgjKO|I@+Xjd6s=naCUIQ0Ib(R;D(kKpkZ)RfCa7(HP{H3muQ@9_zwPMj!{!H4eT=g z=X4G3+)M6%jo7=D13tiq)3KctveJu`t?#1x#$4`Ue4MCbTlm)?p(|YQ_?SL7(i$j%4E1z; z>auhLCoqf3uV5Xi10yt!Q$8bWa!$_jX}OX%eOEoaBR{9zmEm!Wdn$IL zT8U@)^XgI$MbDW=N%4`!Ba*+mM*iPTsAyeQm3-0rd{f-{Oi$X>L40ZttY3KMdIf!K zPJJEmquw{n>*{@YoZro}^^kD*oo!rg-0_n$-{|J`X?zfPY}|3jGS6i2r&m(+7j*$j z&d#~Pu3c()>yzL!_Cyw3ij>TtFK`Y{P`_z3E2>fCxc~aZLwR4$_PDah)gzvkf%B(N z>=Hqqk)>Xr;S=FnM@v)Ha=@SyBB3^_cKWE4q>$aK0E(YL$Fxh)YO*kNo@L(mp`Jju z1Jk$ARuDA%(LG4K-tK4iw%X4`NC#{{Dvx>YW*m={?~tD~+;&!C5+nI$4JhHOOGyfk zrXN1BRueD%J~b&Ni$2| zYu*<|kd49vsmz}iFAs(L%k7#t$8x8~yKbpUQI970Qlg;N*9R`G=vDstB2@#=8ESvp z6Ww4XVi><=O+v$2IbxXl6+R7*f_n?5;KqW!x&4cN!D8i@ z3~4R3$gBQDrw0}*GrBZh5uSzeM>OwRoHjUGYiDglGneAYwHsh=2@&nWU*f^`?9<1i zn=$qW#({+9DI``BAvxjY=0;+9A}mr zOCPyr=XUSEi0Loiuh>vL>{&~WCFc3_vAXbZa*4N8?PgVv{caXx zKAg*fzgc#lyp%-g9q-+v?lDCChsEc1vaZ>mNZQ><6AtC#3J7GeMt2Q}KS<)4=5Iy9 zy*#F19#@-8Nu5J~Y|7XJil~TX&_xQ2sjKGG_e?Uj$95i)w)hf*-}~1fig?thb%J1# F{{p$uR#^Z5 literal 0 HcmV?d00001 diff --git a/man/fev_data.Rd b/man/fev_data.Rd new file mode 100644 index 0000000..6747c07 --- /dev/null +++ b/man/fev_data.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{fev_data} +\alias{fev_data} +\title{Example dataset for \code{tern.gee} package.} +\format{ +A \code{tibble} with 800 rows and 7 variables: +\itemize{ +\item \code{USUBJID}: price, in US dollars. +\item \code{AVISIT}: visit number. +\item \code{ARMCD}: treatment, \code{TRT} or \code{PBO}. +\item \code{RACE}: 3-category race. +\item \code{SEX}: sex. +\item \code{FEV1_BL}: FEV1 at baseline (\%). +\item \code{FEV1}: FEV1 at study visits. +} +} +\usage{ +fev_data +} +\description{ +Measurements of FEV1 (forced expired volume in one second) is a measure of +how quickly the lungs can be emptied. +Low levels of FEV1 may indicate chronic obstructive pulmonary disease (COPD). +} +\keyword{datasets} diff --git a/man/fit_gee.Rd b/man/fit_gee.Rd new file mode 100644 index 0000000..65ab730 --- /dev/null +++ b/man/fit_gee.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fit_gee.R +\name{fit_gee} +\alias{fit_gee} +\title{Fit a GEE Model} +\usage{ +fit_gee( + vars = vars_gee(), + data, + regression = c("logistic"), + cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive"), + control = control_gee() +) +} +\arguments{ +\item{vars}{(\code{list})\cr see \code{\link[=vars_gee]{vars_gee()}}.} + +\item{data}{(\code{data.frame})\cr input data.} + +\item{regression}{(\code{string})\cr choice of regression model.} + +\item{cor_struct}{(\code{string})\cr assumed correlation structure.} + +\item{control}{(\code{list})\cr see \code{\link[=control_gee]{control_gee()}}.} +} +\value{ +Object of class \code{tern_gee} as well as specific to the kind of regression +which was used. +} +\description{ +Fit a GEE Model +} +\details{ +The correlation structure can be: +\itemize{ +\item \code{unstructured}: No constraints are placed on the correlations. +\item \code{toeplitz}: Assumes a banded correlation structure, i.e. the correlation +between two time points depends on the distance between the time indices. +\item \verb{compound symmetry}: Constant correlation between all time points. +\item \code{auto-regressive}: Auto-regressive order 1 correlation matrix. +} +} diff --git a/tests/testthat/fit_gee.R b/tests/testthat/fit_gee.R new file mode 100644 index 0000000..ce49357 --- /dev/null +++ b/tests/testthat/fit_gee.R @@ -0,0 +1,15 @@ +test_that("fit_gee works in logistic regression example", { + fev_data$FEV1_BINARY <- as.integer(fev_data$FEV1 > 30) + result <- fit_gee( + vars = vars_gee( + response = "FEV1_BINARY", + covariates = "RACE", + arm = "ARMCD", + id = "USUBJID", + visit = "AVISIT" + ), + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" + ) +}) From 30451795e12580f177dc1aa705e58d1e5ab9a832 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Wed, 28 Sep 2022 21:56:46 +0200 Subject: [PATCH 03/45] first functional lsmeans table --- NAMESPACE | 12 +++ R/fit_gee.R | 13 ++- R/gee_methods.R | 21 +++++ R/lsmeans.R | 65 +++++++++++++++ R/tabulate_gee.R | 146 ++++++++++++++++++++++++++++++++++ man/gee_methods.Rd | 15 ++++ man/lsmeans.Rd | 26 ++++++ man/reexports.Rd | 16 ++++ man/tabulate_gee.Rd | 63 +++++++++++++++ tests/testthat/fit_gee.R | 58 +++++++++++--- tests/testthat/helper-setup.R | 11 +++ tests/testthat/lsmeans.R | 18 +++++ tests/testthat/tabulate_gee.R | 31 ++++++++ 13 files changed, 481 insertions(+), 14 deletions(-) create mode 100644 R/gee_methods.R create mode 100644 R/lsmeans.R create mode 100644 R/tabulate_gee.R create mode 100644 man/gee_methods.Rd create mode 100644 man/lsmeans.Rd create mode 100644 man/reexports.Rd create mode 100644 man/tabulate_gee.Rd create mode 100644 tests/testthat/helper-setup.R create mode 100644 tests/testthat/lsmeans.R create mode 100644 tests/testthat/tabulate_gee.R diff --git a/NAMESPACE b/NAMESPACE index 5b84bea..199cbea 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,9 +1,21 @@ # Generated by roxygen2: do not edit by hand +S3method(VarCorr,tern_gee) +S3method(as.rtable,tern_gee) +S3method(lsmeans,tern_gee_logistic) +S3method(vcov,gee) +export(a_lsmeans_logistic) +export(as.rtable) export(fit_gee) +export(lsmeans) +export(s_lsmeans_logistic) +export(summarize_gee_logistic) import(checkmate) import(rtables) importFrom(gee,gee) +importFrom(nlme,VarCorr) importFrom(rtables,add_colcounts) importFrom(stats,acf) +importFrom(stats,vcov) +importFrom(tern,as.rtable) importFrom(tern,f_conf_level) diff --git a/R/fit_gee.R b/R/fit_gee.R index 2b31572..f6f5397 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -81,7 +81,7 @@ build_cor_details <- function(cor_str, vars, data) { result_mv <- switch( cor_str, "unstructured" = 1, - "toeplitz" = nlevels(data[[vars$visit]]), + "toeplitz" = nlevels(data[[vars$visit]]) - 1, "compound symmetry" = 1, "auto-regressive" = 1 ) @@ -145,7 +145,7 @@ fit_gee <- function(vars = vars_gee(), cor_struct <- match.arg(cor_struct) cor_details <- build_cor_details(cor_struct, vars, data) - fit <- gee::gee( + capture.output(fit <- suppressMessages(gee::gee( formula = formula, id = .id, data = data, @@ -158,10 +158,17 @@ fit_gee <- function(vars = vars_gee(), silent = control$silent, scale.fix = control$scale.fix, scale.value = control$scale.value - ) + ))) + + fit$visit_levels <- levels(data[[vars$visit]]) + fit$vars <- vars + fit$data <- data + fit$ref_level <- levels(data[[vars$arm]])[1L] structure( fit, class = c(family$class, "tern_gee", class(fit)) ) } + + diff --git a/R/gee_methods.R b/R/gee_methods.R new file mode 100644 index 0000000..a088c76 --- /dev/null +++ b/R/gee_methods.R @@ -0,0 +1,21 @@ +#' Method for GEE Models +#' +#' Additional methods which can simplify working with the GEE result object. +#' @name gee_methods +NULL + +#' @rdname gee_methods +#' @importFrom stats vcov +#' @exportS3Method +vcov.gee <- function(object, ...) { + object$robust.variance +} + +#' @rdname gee_methods +#' @importFrom nlme VarCorr +#' @exportS3Method +VarCorr.tern_gee <- function(x, sigma = x$scale, ...) { + mat <- x$working.correlation * sigma^2 + rownames(mat) <- colnames(mat) <- x$visit_levels + mat +} diff --git a/R/lsmeans.R b/R/lsmeans.R new file mode 100644 index 0000000..6d43e84 --- /dev/null +++ b/R/lsmeans.R @@ -0,0 +1,65 @@ +#' Extract Least Square Means from a GEE Model +#' +#' @param object (`tern_gee`)\cr result of [fit_gee()]. +#' @param conf_level (`proportion`)\cr confidence level +#' @param weights (`string`)\cr type of weights to be used for the least square means, +#' see [emmeans::emmeans()] for details. +#' @return A `data.frame` with least-square means and contrasts. Additional +#' classes allow to dispatch downstream methods correctly, too. +#' +#' @export +lsmeans <- function(object, + conf_level = 0.95, + weights = "proportional", + ...) { + UseMethod("lsmeans", object) +} + +#' @rdname lsmeans +#' @exportS3Method +lsmeans.tern_gee_logistic <- function(object, + conf_level = 0.95, + weights = "proportional", + ...) { + specs <- as.formula(paste("~", object$vars$arm)) + prop_emm <- emmeans::emmeans( + object = object, + specs = specs, + weights = weights, + type = "response", + data = object$data + ) + + prop_df <- cbind( + as.data.frame(prop_emm)[, c(object$vars$arm, "prob", "SE", "asymp.LCL", "asymp.UCL")], + n = as.list(prop_emm)$extras[, ".wgt."] + ) + names(prop_df) <- c(object$vars$arm, "prop_est", "prop_est_se", "prop_lower_cl", "prop_upper_cl", "n") + ref_level <- levels(object$data[[object$vars$arm]])[1L] + + or_emm <- stats::confint( + graphics::pairs(prop_emm, reverse = TRUE), + level = conf_level + ) + or_df <- as.data.frame(or_emm) + or_df$comparator <- gsub(pattern = ".+ / (.+)", replacement = "\\1", x = or_df$contrast) + or_df[[object$vars$arm]] <- gsub(pattern = "(.+) / .+", replacement = "\\1", x = or_df$contrast) + or_df <- or_df[or_df$comparator == ref_level, ] + or_df <- rbind(NA, or_df) + or_df[1L, object$vars$arm] <- ref_level + or_df <- or_df[, c(object$vars$arm, "odds.ratio", "asymp.LCL", "asymp.UCL")] + or_df <- cbind(or_df, log(or_df[, -1L]), conf_level) + names(or_df) <- c( + object$vars$arm, + "or_est", "or_lower_cl", "or_upper_cl", + "log_or_est", "log_or_lower_cl", "log_or_upper_cl", + "conf_level" + ) + + result <- merge(prop_df, or_df, by = object$vars$arm) + + structure( + result, + class = c("lsmeans_logistic", class(result)) + ) +} diff --git a/R/tabulate_gee.R b/R/tabulate_gee.R new file mode 100644 index 0000000..2df80a7 --- /dev/null +++ b/R/tabulate_gee.R @@ -0,0 +1,146 @@ +#' Tabulation of a GEE Model +#' +#' These functions can be used to produce tables from a fitted GEE produced with +#' [fit_gee()]. +#' +#' @name tabulate_gee +NULL + +#' @importFrom tern as.rtable +#' @export +tern::as.rtable + +#' @exportS3Method +#' @describeIn tabulate_gee extracts the coefficient table or covariance matrix +#' estimate from a `tern_gee` object. +as.rtable.tern_gee <- function(x, # nolint + type = c("coef", "cov"), + ...) { + type <- match.arg(type) + switch( + type, + coef = h_gee_coef(x, ...), + cov = h_gee_cov(x, ...) + ) +} + +#' @keywords internal +h_gee_coef <- function(x, format = "xx.xxxx", conf_level = 0.95, ...) { + fixed_table <- as.data.frame(stats::coef(summary(x))) + assert_number(conf_level, lower = 0.001, upper = 0.999) + + fixed_table[["Std. Error"]] <- fixed_table[["Robust S.E."]] + fixed_table[["z value"]] <- fixed_table[["Robust z"]] + fixed_table[["Pr(>|z|)"]] <- 2 * stats::pnorm(abs(fixed_table[["z value"]]), lower.tail = FALSE) + q <- stats::qnorm((1 + conf_level) / 2) + fixed_table[["Lower 95% CI"]] <- fixed_table$Estimate - q * fixed_table[["Std. Error"]] + fixed_table[["Upper 95% CI"]] <- fixed_table$Estimate + q * fixed_table[["Std. Error"]] + + est_se_ci_table <- as.rtable( + fixed_table[, c("Estimate", "Std. Error", "Lower 95% CI", "Upper 95% CI")], + format = format + ) + z_table <- as.rtable(fixed_table[, c("z value"), drop = FALSE], format = format) + pvalue_table <- as.rtable(fixed_table[, "Pr(>|z|)", drop = FALSE], format = "x.xxxx | (<0.0001)") + + cbind_rtables(est_se_ci_table, z_table, pvalue_table) +} + +#' @keywords internal +h_gee_cov <- function(x, format = "xx.xxxx") { + cov_estimate <- VarCorr(x) + as.rtable(as.data.frame(cov_estimate), format = format) +} + +# lsmeans_logistic ---- + +#' @describeIn tabulate_gee Statistics function which is extracting estimates from a +#' [lsmeans()] data frame based on a logistic GEE model. +#' @param df (`data.frame`)\cr data set being a result from [lsmeans()]. +#' @param .in_ref_col (`logical`)\cr `TRUE` when working with the reference level, `FALSE` otherwise. +#' @export +s_lsmeans_logistic <- function(df, .in_ref_col) { + if_not_ref <- function(x) `if`(.in_ref_col, character(), x) + list( + n = df$n, + adj_prop_se = c(df$prop_est, df$prop_est_se), # to be confirmed + adj_prop_ci = formatters::with_label(c(df$prop_lower_cl, df$prop_upper_cl), f_conf_level(df$conf_level)), + odds_ratio_est = if_not_ref(df$or_est), + odds_ratio_ci = formatters::with_label( + if_not_ref(c(df$or_lower_cl, df$or_upper_cl)), + f_conf_level(df$conf_level) + ), + log_odds_ratio_est = if_not_ref(df$log_or_est), + log_odds_ratio_ci = formatters::with_label( + if_not_ref(c(df$log_or_lower_cl, df$log_or_upper_cl)), + f_conf_level(df$conf_level) + ) + ) +} + +## a_lsmeans_logistic ---- + +#' @describeIn tabulate_gee Formatted Analysis function which can be further customized by calling +#' [rtables::make_afun()] on it. It is used as `afun` in [rtables::analyze()]. +#' @export +#' +a_lsmeans_logistic <- make_afun( + s_lsmeans_logistic, + .labels = c( + adj_prop_se = "Adjusted Mean Proportion (SE)", + odds_ratio_est = "Odds Ratio", + log_odds_ratio_est = "Log Odds Ratio" + ), + .formats = c( + n = "xx.", + adj_prop_se = sprintf_format("%.2f (%.2f)"), + adj_prop_ci = "(xx.xx, xx.xx)", + odds_ratio_est = "xx.xx", + odds_ratio_ci = "(xx.xx, xx.xx)", + log_odds_ratio_est = "xx.xx", + log_odds_ratio_ci = "(xx.xx, xx.xx)" + ), + .indent_mods = c( + adj_prop_ci = 1L, + odds_ratio_ci = 1L, + log_odds_ratio_ci = 1L + ), + .null_ref_cells = FALSE +) + +# Note: In production it would be nice to allow an S3 dispatch according to the +# class of the lsmeans input, however for now in the prototype we keep it simple. +# see later then to tern::summarize_variables for how to do that. + +#' @describeIn tabulate_gee Analyze function for tabulating least-squares means estimates +#' from logistic GEE least square mean results. +#' @param lyt (`layout`)\cr input layout where analyses will be added to. +#' @param table_names (`character`)\cr this can be customized in case that the same `vars` +#' are analyzed multiple times, to avoid warnings from `rtables`. +#' @param .stats (`character`)\cr statistics to select for the table. +#' @param .formats (named `character` or `list`)\cr formats for the statistics. +#' @param .indent_mods (named `integer`)\cr indent modifiers for the labels. +#' @param .labels (named `character`)\cr labels for the statistics (without indent). +#' @export +summarize_gee_logistic <- function(lyt, + ..., + table_names = "lsmeans_logistic_summary", + .stats = NULL, + .formats = NULL, + .indent_mods = NULL, + .labels = NULL) { + afun <- make_afun( + a_lsmeans_logistic, + .stats = .stats, + .formats = .formats, + .indent_mods = .indent_mods, + .labels = .labels + ) + analyze( + lyt = lyt, + vars = "n", + afun = afun, + table_names = table_names, + extra_args = list(...) + ) +} diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd new file mode 100644 index 0000000..c7d3f63 --- /dev/null +++ b/man/gee_methods.Rd @@ -0,0 +1,15 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gee_methods.R +\name{gee_methods} +\alias{gee_methods} +\alias{vcov.gee} +\alias{VarCorr.tern_gee} +\title{Method for GEE Models} +\usage{ +\method{vcov}{gee}(object, ...) + +\method{VarCorr}{tern_gee}(x, sigma = x$scale, ...) +} +\description{ +Additional methods which can simplify working with the GEE result object. +} diff --git a/man/lsmeans.Rd b/man/lsmeans.Rd new file mode 100644 index 0000000..0c60b88 --- /dev/null +++ b/man/lsmeans.Rd @@ -0,0 +1,26 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/lsmeans.R +\name{lsmeans} +\alias{lsmeans} +\alias{lsmeans.tern_gee_logistic} +\title{Extract Least Square Means from a GEE Model} +\usage{ +lsmeans(object, conf_level = 0.95, weights = "proportional", ...) + +\method{lsmeans}{tern_gee_logistic}(object, conf_level = 0.95, weights = "proportional", ...) +} +\arguments{ +\item{object}{(\code{tern_gee})\cr result of \code{\link[=fit_gee]{fit_gee()}}.} + +\item{conf_level}{(\code{proportion})\cr confidence level} + +\item{weights}{(\code{string})\cr type of weights to be used for the least square means, +see \code{\link[emmeans:emmeans]{emmeans::emmeans()}} for details.} +} +\value{ +A \code{data.frame} with least-square means and contrasts. Additional +classes allow to dispatch downstream methods correctly, too. +} +\description{ +Extract Least Square Means from a GEE Model +} diff --git a/man/reexports.Rd b/man/reexports.Rd new file mode 100644 index 0000000..e4880f2 --- /dev/null +++ b/man/reexports.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tabulate_gee.R +\docType{import} +\name{reexports} +\alias{reexports} +\alias{as.rtable} +\title{Objects exported from other packages} +\keyword{internal} +\description{ +These objects are imported from other packages. Follow the links +below to see their documentation. + +\describe{ + \item{tern}{\code{\link[tern]{as.rtable}}} +}} + diff --git a/man/tabulate_gee.Rd b/man/tabulate_gee.Rd new file mode 100644 index 0000000..888c695 --- /dev/null +++ b/man/tabulate_gee.Rd @@ -0,0 +1,63 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tabulate_gee.R +\name{tabulate_gee} +\alias{tabulate_gee} +\alias{as.rtable.tern_gee} +\alias{s_lsmeans_logistic} +\alias{a_lsmeans_logistic} +\alias{summarize_gee_logistic} +\title{Tabulation of a GEE Model} +\usage{ +\method{as.rtable}{tern_gee}(x, type = c("coef", "cov"), ...) + +s_lsmeans_logistic(df, .in_ref_col) + +a_lsmeans_logistic(df, .in_ref_col) + +summarize_gee_logistic( + lyt, + ..., + table_names = "lsmeans_logistic_summary", + .stats = NULL, + .formats = NULL, + .indent_mods = NULL, + .labels = NULL +) +} +\arguments{ +\item{df}{(\code{data.frame})\cr data set being a result from \code{\link[=lsmeans]{lsmeans()}}.} + +\item{.in_ref_col}{(\code{logical})\cr \code{TRUE} when working with the reference level, \code{FALSE} otherwise.} + +\item{lyt}{(\code{layout})\cr input layout where analyses will be added to.} + +\item{table_names}{(\code{character})\cr this can be customized in case that the same \code{vars} +are analyzed multiple times, to avoid warnings from \code{rtables}.} + +\item{.stats}{(\code{character})\cr statistics to select for the table.} + +\item{.formats}{(named \code{character} or \code{list})\cr formats for the statistics.} + +\item{.indent_mods}{(named \code{integer})\cr indent modifiers for the labels.} + +\item{.labels}{(named \code{character})\cr labels for the statistics (without indent).} +} +\description{ +These functions can be used to produce tables from a fitted GEE produced with +\code{\link[=fit_gee]{fit_gee()}}. +} +\section{Functions}{ +\itemize{ +\item \code{as.rtable(tern_gee)}: extracts the coefficient table or covariance matrix +estimate from a \code{tern_gee} object. + +\item \code{s_lsmeans_logistic()}: Statistics function which is extracting estimates from a +\code{\link[=lsmeans]{lsmeans()}} data frame based on a logistic GEE model. + +\item \code{a_lsmeans_logistic()}: Formatted Analysis function which can be further customized by calling +\code{\link[rtables:make_afun]{rtables::make_afun()}} on it. It is used as \code{afun} in \code{\link[rtables:analyze]{rtables::analyze()}}. + +\item \code{summarize_gee_logistic()}: Analyze function for tabulating least-squares means estimates +from logistic GEE least square mean results. + +}} diff --git a/tests/testthat/fit_gee.R b/tests/testthat/fit_gee.R index ce49357..fe97ead 100644 --- a/tests/testthat/fit_gee.R +++ b/tests/testthat/fit_gee.R @@ -1,15 +1,51 @@ -test_that("fit_gee works in logistic regression example", { - fev_data$FEV1_BINARY <- as.integer(fev_data$FEV1 > 30) - result <- fit_gee( - vars = vars_gee( - response = "FEV1_BINARY", - covariates = "RACE", - arm = "ARMCD", - id = "USUBJID", - visit = "AVISIT" - ), +# fit_gee ---- + +## logistic ---- + +test_that("unstructured", { + result <- expect_silent(fit_gee( + vars = fev_vars, data = fev_data, regression = "logistic", cor_struct = "unstructured" - ) + )) + expect_class(result, c("tern_gee_logistic", "tern_gee", "gee", "glm")) +}) + +test_that("auto-regressive", { + skip("auto-regressive does not work yet") + + result <- expect_silent(fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "auto-regressive" + )) + # error: + # cgee: M-dependence, M=1, but clustsize=1 + expect_class(result, c("tern_gee_logistic", "tern_gee", "gee", "glm")) +}) + +test_that("compound symmetry", { + result <- expect_silent(fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "compound symmetry" + )) + expect_class(result, c("tern_gee_logistic", "tern_gee", "gee", "glm")) +}) + +test_that("toeplitz", { + skip("toeplitz does not work yet") + + result <- expect_silent(fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "toeplitz" + )) + # error: + # cgee: M-dependence, M=3, but clustsize=2 + expect_class(result, c("tern_gee_logistic", "tern_gee", "gee", "glm")) }) diff --git a/tests/testthat/helper-setup.R b/tests/testthat/helper-setup.R new file mode 100644 index 0000000..19b0ef3 --- /dev/null +++ b/tests/testthat/helper-setup.R @@ -0,0 +1,11 @@ +# example ---- + +fev_data$FEV1_BINARY <- as.integer(fev_data$FEV1 > 30) +fev_vars <- vars_gee( + response = "FEV1_BINARY", + covariates = "RACE", + arm = "ARMCD", + id = "USUBJID", + visit = "AVISIT" +) + diff --git a/tests/testthat/lsmeans.R b/tests/testthat/lsmeans.R new file mode 100644 index 0000000..dd8661a --- /dev/null +++ b/tests/testthat/lsmeans.R @@ -0,0 +1,18 @@ +test_that("lsmeans works for logistic model", { + model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" + ) + + result <- lsmeans(model) + expect_data_frame(result) + expect_class(result, "lsmeans_logistic") + expect_named(result, c( + "ARMCD", "prop_est", "prop_est_se", "prop_lower_cl", "prop_upper_cl", "n", + "or_est", "or_lower_cl", "or_upper_cl", "log_or_est", "log_or_lower_cl", "log_or_upper_cl", + "conf_level" + )) + expect_identical(nrow(result), 2L) +}) diff --git a/tests/testthat/tabulate_gee.R b/tests/testthat/tabulate_gee.R new file mode 100644 index 0000000..a58f237 --- /dev/null +++ b/tests/testthat/tabulate_gee.R @@ -0,0 +1,31 @@ +model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" +) + +test_that("h_gee_coef works", { + result <- expect_silent(h_gee_coef(model)) + # to do: add expectation +}) + +test_that("h_gee_cov works", { + result <- expect_silent(h_gee_cov(model)) + # to do: add expectation +}) + +test_that("summarize_gee_logistic works", { + dat_adsl <- fev_data %>% + dplyr::select(USUBJID, ARMCD) %>% + dplyr::distinct() + result <- basic_table() %>% + split_cols_by("ARMCD", ref_group = model$ref_level) %>% + add_colcounts() %>% + summarize_gee_logistic() %>% + build_table( + df = lsmeans(model), + alt_counts_df = dat_adsl + ) + # to do: add expectation +}) From 935b267f54ec13895708e73506ac50548df7182b Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Sep 2022 19:59:56 +0000 Subject: [PATCH 04/45] [skip actions] Restyle files --- R/fit_gee.R | 13 ++++--------- R/tabulate_gee.R | 3 +-- tests/testthat/helper-setup.R | 1 - 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/R/fit_gee.R b/R/fit_gee.R index f6f5397..14a0a52 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -49,8 +49,7 @@ build_formula <- function(vars) { build_family <- function(regression) { assert_string(regression) - result_object <- switch( - regression, + result_object <- switch(regression, logistic = stats::binomial(link = "logit"), stop(paste("regression type", regression, "not supported")) ) @@ -69,8 +68,7 @@ build_cor_details <- function(cor_str, vars, data) { assert_list(vars) assert_data_frame(data) - result_str <- switch( - cor_str, + result_str <- switch(cor_str, "unstructured" = "unstructured", "toeplitz" = "stat_M_dep", "compound symmetry" = "exchangeable", @@ -78,8 +76,7 @@ build_cor_details <- function(cor_str, vars, data) { stop(paste("correlation structure", cor_str, "not available")) ) - result_mv <- switch( - cor_str, + result_mv <- switch(cor_str, "unstructured" = 1, "toeplitz" = nlevels(data[[vars$visit]]) - 1, "compound symmetry" = 1, @@ -149,7 +146,7 @@ fit_gee <- function(vars = vars_gee(), formula = formula, id = .id, data = data, - na.action = na.omit, # That is the only option here. + na.action = na.omit, # That is the only option here. tol = control$tol, maxiter = control$maxiter, family = family$object, @@ -170,5 +167,3 @@ fit_gee <- function(vars = vars_gee(), class = c(family$class, "tern_gee", class(fit)) ) } - - diff --git a/R/tabulate_gee.R b/R/tabulate_gee.R index 2df80a7..7973645 100644 --- a/R/tabulate_gee.R +++ b/R/tabulate_gee.R @@ -17,8 +17,7 @@ as.rtable.tern_gee <- function(x, # nolint type = c("coef", "cov"), ...) { type <- match.arg(type) - switch( - type, + switch(type, coef = h_gee_coef(x, ...), cov = h_gee_cov(x, ...) ) diff --git a/tests/testthat/helper-setup.R b/tests/testthat/helper-setup.R index 19b0ef3..1639fac 100644 --- a/tests/testthat/helper-setup.R +++ b/tests/testthat/helper-setup.R @@ -8,4 +8,3 @@ fev_vars <- vars_gee( id = "USUBJID", visit = "AVISIT" ) - From ae3066d734fb8a1c131d81fba3bb70721f9923ff Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Thu, 29 Sep 2022 10:28:01 +0200 Subject: [PATCH 05/45] trigger checks From 6f482a8d17eb05377a9d6f2783f129fbd2ae6194 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Mon, 3 Oct 2022 12:15:52 +0200 Subject: [PATCH 06/45] fix dependencies --- DESCRIPTION | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ea71c98..4550fb1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,7 @@ Type: Package Package: tern.gee Title: Tables and Graphs for Generalized Estimating Equations (GEE) Model Fits Version: 0.0.9001 -Date: 2022-08-22 +Date: 2022-10-03 Authors@R: c( person("Daniel", "Sabanés Bové", , "daniel.sabanes_bove@roche.com", role = c("aut", "cre")), person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")) @@ -17,7 +17,8 @@ Depends: Imports: checkmate, gee, - rtables (>= 0.5.2), + nlme, + rtables, stats Suggests: knitr, @@ -27,6 +28,8 @@ Suggests: VignetteBuilder: knitr Config/testthat/edition: 3 +Remotes: + insightsengineering/tern@*release Encoding: UTF-8 Language: en-US LazyData: true From fa3d1bb8520d066ab6007a84e79d00fafd0def41 Mon Sep 17 00:00:00 2001 From: Dinakar <26552821+cicdguy@users.noreply.github.com> Date: Mon, 3 Oct 2022 06:27:19 -0500 Subject: [PATCH 07/45] Add tern as an upstream repo Signed-off-by: Dinakar <26552821+cicdguy@users.noreply.github.com> --- staged_dependencies.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index 3da3daa..977155a 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -4,4 +4,6 @@ current_repo: repo: insightsengineering/tern.gee host: https://github.com upstream_repos: + repo: insightsengineering/tern + host: https://github.com downstream_repos: From c6512c39df62f761fa4a42200876fb7bb84ccfa8 Mon Sep 17 00:00:00 2001 From: cicdguy <26552821+cicdguy@users.noreply.github.com> Date: Mon, 3 Oct 2022 06:34:50 -0500 Subject: [PATCH 08/45] Add nestcolor as a dep --- staged_dependencies.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index 3da3daa..23636c3 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -4,4 +4,6 @@ current_repo: repo: insightsengineering/tern.gee host: https://github.com upstream_repos: + - repo: insightsengineering/nestcolor + host: https://github.com downstream_repos: From 60c9f73c554111fbef4a7efef44027060e42016b Mon Sep 17 00:00:00 2001 From: cicdguy <26552821+cicdguy@users.noreply.github.com> Date: Mon, 3 Oct 2022 09:55:32 -0500 Subject: [PATCH 09/45] Add tern --- DESCRIPTION | 2 -- staged_dependencies.yaml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4550fb1..bedbd41 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,8 +28,6 @@ Suggests: VignetteBuilder: knitr Config/testthat/edition: 3 -Remotes: - insightsengineering/tern@*release Encoding: UTF-8 Language: en-US LazyData: true diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index 23636c3..1ae7880 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -6,4 +6,6 @@ current_repo: upstream_repos: - repo: insightsengineering/nestcolor host: https://github.com + - repo: insightsengineering/tern + host: https://github.com downstream_repos: From e6a87a0070d40cf1b126c50e2e6f9de85a545198 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Mon, 3 Oct 2022 20:42:20 +0200 Subject: [PATCH 10/45] Update staged_dependencies.yaml add `rtables` upstream dependency Signed-off-by: Daniel Sabanes Bove --- staged_dependencies.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index 1ae7880..d9d786c 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -6,6 +6,8 @@ current_repo: upstream_repos: - repo: insightsengineering/nestcolor host: https://github.com + - repo: Roche/rtables + host: https://github.com - repo: insightsengineering/tern host: https://github.com downstream_repos: From de93faed5b83cb16fdf65365d49ad05f4f95e9aa Mon Sep 17 00:00:00 2001 From: Franciszek Walkowiak Date: Thu, 6 Oct 2022 11:01:51 +0200 Subject: [PATCH 11/45] Add nestcolor to DESCRIPTION --- DESCRIPTION | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index bedbd41..fce630d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -13,7 +13,8 @@ URL: https://github.com/insightsengineering/tern.gee/ BugReports: https://github.com/insightsengineering/tern.gee/issues Depends: R (>= 3.6), - tern (>= 0.7.9) + tern (>= 0.7.9), + nestcolor (>= 0.0.1) Imports: checkmate, gee, From 03a900d6bff5aab4b079e634ee97f1245178aaf9 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Mon, 10 Oct 2022 14:16:16 +0200 Subject: [PATCH 12/45] fix build_formula when there are no covariates --- R/fit_gee.R | 6 +----- tests/testthat/fit_gee.R | 8 ++++++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/R/fit_gee.R b/R/fit_gee.R index 14a0a52..3411897 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -29,13 +29,9 @@ control_gee <- function(tol = 0.001, #' @keywords internal build_formula <- function(vars) { assert_list(vars) - covariates_part <- paste( - vars$covariates, - collapse = " + " - ) arm_part <- if (is.null(vars$arm)) NULL else vars$arm rhs_formula <- paste( - c(arm_part, covariates_part), + c(arm_part, vars$covariates), collapse = " + " ) stats::as.formula(paste( diff --git a/tests/testthat/fit_gee.R b/tests/testthat/fit_gee.R index fe97ead..70ba1c0 100644 --- a/tests/testthat/fit_gee.R +++ b/tests/testthat/fit_gee.R @@ -1,3 +1,11 @@ +# build_formula ---- + +test_that("build_formula works without covariates", { + result <- expect_silent(build_formula(vars_gee())) + expected <- AVAL ~ ARM + expect_equal(result, expected, ignore_attr = TRUE) +}) + # fit_gee ---- ## logistic ---- From 3e45c0153166044c762595c08aba486e19f169eb Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Mon, 10 Oct 2022 14:55:31 +0200 Subject: [PATCH 13/45] assert arm factor, fix ci for coef table --- R/fit_gee.R | 1 + R/tabulate_gee.R | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/R/fit_gee.R b/R/fit_gee.R index 3411897..2d54d68 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -156,6 +156,7 @@ fit_gee <- function(vars = vars_gee(), fit$visit_levels <- levels(data[[vars$visit]]) fit$vars <- vars fit$data <- data + assert_factor(data[[vars$arm]]) fit$ref_level <- levels(data[[vars$arm]])[1L] structure( diff --git a/R/tabulate_gee.R b/R/tabulate_gee.R index 7973645..73f1546 100644 --- a/R/tabulate_gee.R +++ b/R/tabulate_gee.R @@ -32,11 +32,14 @@ h_gee_coef <- function(x, format = "xx.xxxx", conf_level = 0.95, ...) { fixed_table[["z value"]] <- fixed_table[["Robust z"]] fixed_table[["Pr(>|z|)"]] <- 2 * stats::pnorm(abs(fixed_table[["z value"]]), lower.tail = FALSE) q <- stats::qnorm((1 + conf_level) / 2) - fixed_table[["Lower 95% CI"]] <- fixed_table$Estimate - q * fixed_table[["Std. Error"]] - fixed_table[["Upper 95% CI"]] <- fixed_table$Estimate + q * fixed_table[["Std. Error"]] + ci_string <- tern::f_conf_level(conf_level) + lower_string <- paste("Lower", ci_string) + upper_string <- paste("Upper", ci_string) + fixed_table[[lower_string]] <- fixed_table$Estimate - q * fixed_table[["Std. Error"]] + fixed_table[[paste("Upper", ci_string)]] <- fixed_table$Estimate + q * fixed_table[["Std. Error"]] est_se_ci_table <- as.rtable( - fixed_table[, c("Estimate", "Std. Error", "Lower 95% CI", "Upper 95% CI")], + fixed_table[, c("Estimate", "Std. Error", lower_string, upper_string)], format = format ) z_table <- as.rtable(fixed_table[, c("z value"), drop = FALSE], format = format) From 2a510d0d7258c20b1e8cc5410e786986d04f1c45 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 15:33:07 +0000 Subject: [PATCH 14/45] [skip actions] Restyle files --- tests/testthat/test-tabulate_gee.R | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/test-tabulate_gee.R b/tests/testthat/test-tabulate_gee.R index 66a1bfa..3f1e17c 100644 --- a/tests/testthat/test-tabulate_gee.R +++ b/tests/testthat/test-tabulate_gee.R @@ -7,7 +7,6 @@ model <- fit_gee( test_that("h_gee_coef works", { result <- expect_silent(h_gee_coef(model)) - }) test_that("h_gee_cov works", { From 2df64021a32a185a32a51f9a32774ae6089c0666 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 20 Oct 2022 15:42:57 +0000 Subject: [PATCH 15/45] [skip actions] Roxygen Man Pages Auto Update --- man/fit_gee.Rd | 2 +- man/gee_methods.Rd | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/man/fit_gee.Rd b/man/fit_gee.Rd index 65ab730..383cda1 100644 --- a/man/fit_gee.Rd +++ b/man/fit_gee.Rd @@ -9,7 +9,7 @@ fit_gee( data, regression = c("logistic"), cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive"), - control = control_gee() + control = geeasy::geelm.control() ) } \arguments{ diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd index c7d3f63..793afe9 100644 --- a/man/gee_methods.Rd +++ b/man/gee_methods.Rd @@ -2,12 +2,9 @@ % Please edit documentation in R/gee_methods.R \name{gee_methods} \alias{gee_methods} -\alias{vcov.gee} \alias{VarCorr.tern_gee} \title{Method for GEE Models} \usage{ -\method{vcov}{gee}(object, ...) - \method{VarCorr}{tern_gee}(x, sigma = x$scale, ...) } \description{ From dd5e7e04fc6aa3bffba737c17724db46ce609368 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Fri, 21 Oct 2022 10:03:40 +0200 Subject: [PATCH 16/45] add the VarCorr part --- NAMESPACE | 2 -- R/gee_methods.R | 35 ++++++++++++++++++----- man/gee_methods.Rd | 2 +- tests/testthat/test-gee_methods.R | 46 ++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 199cbea..40292d6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,7 +3,6 @@ S3method(VarCorr,tern_gee) S3method(as.rtable,tern_gee) S3method(lsmeans,tern_gee_logistic) -S3method(vcov,gee) export(a_lsmeans_logistic) export(as.rtable) export(fit_gee) @@ -16,6 +15,5 @@ importFrom(gee,gee) importFrom(nlme,VarCorr) importFrom(rtables,add_colcounts) importFrom(stats,acf) -importFrom(stats,vcov) importFrom(tern,as.rtable) importFrom(tern,f_conf_level) diff --git a/R/gee_methods.R b/R/gee_methods.R index 7810c1b..c1ac04a 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -7,12 +7,33 @@ NULL #' @rdname gee_methods #' @importFrom nlme VarCorr #' @exportS3Method -VarCorr.tern_gee <- function(x, sigma = x$scale, ...) { - # todo - need to figure out how to build the matrix from - # x$geese$alpha and the x$corstr etc - maybe just 4 different cases - # and build the matrix each time. +VarCorr.tern_gee <- function(x, sigma = 1, ...) { + dim_mat <- length(x$visit_levels) + tmp <- id_mat <- diag(dim_mat) + corest <- x$geese$alpha - # mat <- x$working.correlation * sigma^2 - # rownames(mat) <- colnames(mat) <- x$visit_levels - # mat + # Start with lower-triangular matrix part. + lower_mat <- switch( + x$corstr, + unstructured = , # Since this is the same as exchangeable, we can do this. + exchangeable = { + tmp[lower.tri(tmp)] <- corest + tmp + }, + ar1 = { + row_col_diff <- row(tmp) - col(tmp) + tmp[lower.tri(tmp)] <- corest^(row_col_diff[lower.tri(row_col_diff)]) + tmp + }, + `m-dependent` = { + row_col_diff <- row(tmp) - col(tmp) + tmp[lower.tri(tmp)] <- corest[row_col_diff[lower.tri(row_col_diff)]] + tmp + } + ) + + # Construct the full symmetric matrix. + mat <- lower_mat + t(lower_mat) - id_mat + rownames(mat) <- colnames(mat) <- x$visit_levels + mat } diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd index 793afe9..541903f 100644 --- a/man/gee_methods.Rd +++ b/man/gee_methods.Rd @@ -5,7 +5,7 @@ \alias{VarCorr.tern_gee} \title{Method for GEE Models} \usage{ -\method{VarCorr}{tern_gee}(x, sigma = x$scale, ...) +\method{VarCorr}{tern_gee}(x, sigma = 1, ...) } \description{ Additional methods which can simplify working with the GEE result object. diff --git a/tests/testthat/test-gee_methods.R b/tests/testthat/test-gee_methods.R index e377dfa..780d6b5 100644 --- a/tests/testthat/test-gee_methods.R +++ b/tests/testthat/test-gee_methods.R @@ -10,6 +10,50 @@ test_that("vcov works as expected", { expect_matrix(result, nrows = 4, ncols = 4) }) -test_that("VarCorr works as expected", { +test_that("VarCorr works as expected for unstructured correlation structure", { + model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" + ) + + result <- expect_silent(VarCorr(model)) + expect_true(all(eigen(result)$values > 0)) +}) + +test_that("VarCorr works as expected for compound symmetry correlation structure", { + model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "compound symmetry" + ) + + result <- expect_silent(VarCorr(model)) + expect_true(all(eigen(result)$values > 0)) +}) + +test_that("VarCorr works as expected for AR1 correlation structure", { + model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "auto-regressive" + ) + + result <- expect_silent(VarCorr(model)) + expect_true(all(eigen(result)$values > 0)) +}) + +test_that("VarCorr works as expected for Toeplitz correlation structure", { + model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "toeplitz" + ) + result <- expect_silent(VarCorr(model)) + expect_true(all(eigen(result)$values > 0)) }) From 9435fc89dc2a63e0504203d5df293b6bfb40a965 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 08:06:25 +0000 Subject: [PATCH 17/45] [skip actions] Restyle files --- R/gee_methods.R | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/R/gee_methods.R b/R/gee_methods.R index c1ac04a..67edcf0 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -13,8 +13,7 @@ VarCorr.tern_gee <- function(x, sigma = 1, ...) { corest <- x$geese$alpha # Start with lower-triangular matrix part. - lower_mat <- switch( - x$corstr, + lower_mat <- switch(x$corstr, unstructured = , # Since this is the same as exchangeable, we can do this. exchangeable = { tmp[lower.tri(tmp)] <- corest From 6b0e838d595d5091fb0be4bfb2dc2bef4d9511f5 Mon Sep 17 00:00:00 2001 From: Daniel Sabanes Bove Date: Fri, 21 Oct 2022 11:56:44 +0200 Subject: [PATCH 18/45] compare with SAS --- DESCRIPTION | 1 + NAMESPACE | 2 + R/fit_gee.R | 11 +- R/gee_methods.R | 7 + design/compare_sas.R | 32 +++ design/reprex.R | 383 +++++++++++++++++++++++++++++ design/sas_log.txt | 105 ++++++++ man/gee_methods.Rd | 3 + tests/testthat/test-tabulate_gee.R | 2 + 9 files changed, 541 insertions(+), 5 deletions(-) create mode 100644 design/compare_sas.R create mode 100644 design/reprex.R create mode 100644 design/sas_log.txt diff --git a/DESCRIPTION b/DESCRIPTION index b4d8bff..ba7e719 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,6 +18,7 @@ Depends: Imports: checkmate, geeasy, + geepack, nlme, rtables, stats diff --git a/NAMESPACE b/NAMESPACE index 40292d6..f02cbac 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,5 +1,6 @@ # Generated by roxygen2: do not edit by hand +S3method(QIC,tern_gee) S3method(VarCorr,tern_gee) S3method(as.rtable,tern_gee) S3method(lsmeans,tern_gee_logistic) @@ -12,6 +13,7 @@ export(summarize_gee_logistic) import(checkmate) import(rtables) importFrom(gee,gee) +importFrom(geepack,QIC) importFrom(nlme,VarCorr) importFrom(rtables,add_colcounts) importFrom(stats,acf) diff --git a/R/fit_gee.R b/R/fit_gee.R index c262012..a93f2d2 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -40,7 +40,8 @@ build_family <- function(regression) { list( object = result_object, - class = result_class + class = result_class, + control = geeasy::geelm.control(scale.fix = TRUE) ) } @@ -96,7 +97,6 @@ order_data <- function(data, vars) { #' @param data (`data.frame`)\cr input data. #' @param regression (`string`)\cr choice of regression model. #' @param cor_struct (`string`)\cr assumed correlation structure. -#' @param control (`list`)\cr see [control_gee()]. #' #' @details The correlation structure can be: #' * `unstructured`: No constraints are placed on the correlations. @@ -111,8 +111,7 @@ order_data <- function(data, vars) { fit_gee <- function(vars = vars_gee(), data, regression = c("logistic"), - cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive"), - control = geeasy::geelm.control()) { + cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive")) { formula <- build_formula(vars) regression <- match.arg(regression) @@ -125,6 +124,7 @@ fit_gee <- function(vars = vars_gee(), cor_struct <- match.arg(cor_struct) cor_details <- build_cor_details(cor_struct, vars, data) + browser() fit <- geeasy::geelm( formula = formula, id = .id, @@ -133,9 +133,10 @@ fit_gee <- function(vars = vars_gee(), family = family$object, corstr = cor_details$str, Mv = cor_details$mv, - control = control + control = family$control ) + fit$qic <- geepack::QIC(fit) fit$visit_levels <- levels(data[[vars$visit]]) fit$vars <- vars fit$data <- data diff --git a/R/gee_methods.R b/R/gee_methods.R index c1ac04a..4b61875 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -37,3 +37,10 @@ VarCorr.tern_gee <- function(x, sigma = 1, ...) { rownames(mat) <- colnames(mat) <- x$visit_levels mat } + +#' @rdname gee_methods +#' @importFrom geepack QIC +#' @exportS3Method +QIC.tern_gee <- function(object, ...) { + object$qic +} diff --git a/design/compare_sas.R b/design/compare_sas.R new file mode 100644 index 0000000..ee6e3c5 --- /dev/null +++ b/design/compare_sas.R @@ -0,0 +1,32 @@ +library(r2stream) + +test_data <- fev_data +test_vars <- fev_vars + +model <- fit_gee( + vars = test_vars, + data = test_data, + regression = "logistic", + cor_struct = "unstructured" +) + +sascode <- list( + "test" = " + proc genmod data=ana.dat descending; + class RACE(ref = 'Asian') USUBJID ARMCD(ref = 'PBO') AVISIT; + model FEV1_BIN = ARMCD RACE / dist=bin link=logit ; + repeated subject=USUBJID / within=AVISIT type=UN CORRW; + run; + " +) + +sas_data <- test_data +sas_data$FEV1_BIN <- sas_data$FEV1_BINARY +sas_data$FEV1_BINARY <- NULL + +result <- r2stream::bee_sas(dat = list("dat" = sas_data), sascode = sascode) +result$test$sas_log +writeLines(result$test$sas_out, con = "sas_log.txt") + +QIC(model) +VarCorr(model) diff --git a/design/reprex.R b/design/reprex.R new file mode 100644 index 0000000..0350dd1 --- /dev/null +++ b/design/reprex.R @@ -0,0 +1,383 @@ +data <- structure(list(FEV1_BIN = c( + NA, 1L, NA, 0L, NA, 1L, 1L, 1L, + NA, 1L, NA, 1L, 1L, 1L, NA, 1L, 1L, NA, 1L, 1L, NA, NA, 1L, NA, + 1L, 1L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, NA, NA, 1L, NA, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, NA, NA, 1L, 1L, NA, NA, 1L, NA, + NA, NA, 1L, 1L, NA, 1L, NA, 1L, 0L, NA, NA, 1L, 1L, 1L, NA, 1L, + 1L, 1L, 1L, 1L, NA, 1L, 1L, NA, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 0L, 1L, 1L, NA, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 0L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, 1L, 1L, + 0L, NA, 1L, NA, 1L, NA, NA, 1L, 0L, 1L, NA, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, NA, 1L, NA, 1L, NA, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, NA, + 1L, NA, 1L, 1L, 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, 1L, NA, NA, 1L, + 0L, 1L, 1L, 1L, 1L, NA, NA, NA, 1L, 1L, 1L, 1L, 0L, 1L, 1L, NA, + 0L, 1L, 1L, NA, NA, 1L, 1L, NA, 1L, 1L, 1L, NA, 0L, 1L, 1L, NA, + 0L, 1L, NA, 1L, NA, 1L, NA, 1L, 1L, 1L, NA, NA, NA, NA, NA, NA, + 1L, 1L, 1L, NA, 0L, NA, NA, 1L, NA, 1L, 1L, 1L, NA, 1L, 1L, NA, + 1L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, NA, 0L, NA, NA, + NA, 1L, 1L, 1L, 1L, 0L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, 1L, 0L, 1L, NA, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, NA, NA, 1L, 1L, 1L, NA, 1L, 1L, + NA, 1L, 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, 1L, 0L, NA, 1L, 1L, 1L, + NA, NA, NA, 1L, 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, 1L, NA, 1L, 1L, + 1L, 1L, 1L, 1L, NA, NA, NA, 1L, NA, NA, 1L, 1L, 1L, 1L, 1L, 1L, + 0L, NA, 1L, NA, 0L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 0L, NA, NA, NA, + NA, NA, 1L, 1L, 1L, NA, 1L, 1L, NA, 1L, 1L, 1L, 1L, NA, 1L, 1L, + NA, 1L, 1L, NA, 1L, 1L, NA, 1L, 1L, 1L, NA, 1L, 0L, 0L, 1L, 1L, + NA, 1L, NA, NA, 1L, 1L, 1L, NA, NA, 1L, 1L, NA, 1L, 1L, 1L, 1L, + 1L, 0L, 1L, NA, 1L, NA, 1L, 1L, NA, 1L, 1L, NA, 0L, 1L, 1L, 1L, + NA, NA, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, NA, 1L, 1L, NA, + NA, NA, NA, 1L, 1L, NA, 1L, NA, 1L, 1L, 1L, NA, 0L, 1L, 1L, 1L, + 1L, 1L, 1L, NA, 1L, NA, 1L, 1L, 0L, NA, NA, NA, 1L, 1L, 1L, NA, + 1L, 1L, NA, NA, 1L, NA, NA, NA, NA, NA, NA, 1L, NA, NA, 1L, NA, + 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, NA, 1L, + 1L, NA, NA, 1L, 1L, 1L, 1L, 1L, 1L, NA, NA, NA, NA, 1L, 1L, NA, + 0L, 1L, 1L, 0L, NA, 1L, 1L, 1L, NA, 1L, 1L, 1L, NA, 1L, 1L, 1L, + 1L, 1L, 1L, 0L, 1L, NA, NA, 1L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, NA, + NA, NA, 1L, 1L, 0L, 1L, NA, 1L, NA, 1L, NA, 1L, NA, NA, NA, NA, + 0L, 1L, NA, 1L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, NA, NA, 1L, 1L, 1L, + 1L, 1L, 1L, NA, NA, 1L, 1L, NA, 1L, 1L, 1L, 1L, NA, NA, 1L, 1L, + 1L, 1L, NA, 1L, NA, 1L, NA, 1L, 0L, 1L, 1L, 1L, 1L, 1L, NA, 1L, + 1L, NA, 1L, 1L, 1L, 1L, 1L, 1L, 1L, NA, NA, 1L, NA, 1L, 1L, 1L, + 0L, 1L, NA, NA, NA, 1L, 1L, 1L, NA, 1L, NA, NA, 0L, NA, NA, 1L, + NA, 1L, NA, 1L, NA, 1L, 1L, 1L, 1L, NA, NA, NA, 0L, NA, NA, NA, + 1L, 1L, NA, 1L, 0L, 1L, 1L, NA, 0L, 1L, NA, NA, NA, 1L, 1L, 1L, + NA, 0L, NA, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, NA, 1L, 1L, 1L, NA, + 0L, 1L, NA, 1L, 1L, 1L, NA, 1L, NA, NA, 1L, NA, 1L, NA, NA, 1L, + 1L, NA, 1L, 1L, 0L, 1L, 1L, NA, 1L, NA, 1L, 1L, NA, NA, 1L, 1L, + 1L, 1L, NA, NA, NA, 1L, NA, 1L, NA, 1L, 1L, 1L, 1L, 1L, NA, 1L, + 1L, NA, NA, NA, 1L, NA, 1L, NA, 1L, 1L, 1L, NA, 1L, 1L, 1L, 1L, + 1L, NA, NA, 1L, 1L, 1L, 1L, 1L, NA, 1L, 1L, 1L, 0L, 1L, 1L, 1L, + NA, 1L, 1L, NA, 0L, 1L, NA, 1L, NA, NA, NA, 1L, 1L, 1L, NA, 1L, + NA, NA, NA, NA, 1L, 1L, NA, 1L +), ARMCD = structure(c( + 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, + 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L +), levels = c( + "PBO", + "TRT" +), class = "factor"), RACE = structure(c( + 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, + 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, + 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, + 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, + 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, + 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, + 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 3L, 3L, 3L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, + 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, + 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, + 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, + 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, + 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 2L, 2L, 2L, 2L +), levels = c( + "Asian", + "Black or African American", "White" +), class = "factor"), id = c( + 1L, + 1L, 1L, 1L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 4L, 4L, 4L, 4L, 5L, + 5L, 5L, 5L, 6L, 6L, 6L, 6L, 7L, 7L, 7L, 7L, 8L, 8L, 8L, 8L, 9L, + 9L, 9L, 9L, 10L, 10L, 10L, 10L, 11L, 11L, 11L, 11L, 12L, 12L, + 12L, 12L, 13L, 13L, 13L, 13L, 14L, 14L, 14L, 14L, 15L, 15L, 15L, + 15L, 16L, 16L, 16L, 16L, 17L, 17L, 17L, 17L, 18L, 18L, 18L, 18L, + 19L, 19L, 19L, 19L, 20L, 20L, 20L, 20L, 21L, 21L, 21L, 21L, 22L, + 22L, 22L, 22L, 23L, 23L, 23L, 23L, 24L, 24L, 24L, 24L, 25L, 25L, + 25L, 25L, 26L, 26L, 26L, 26L, 27L, 27L, 27L, 27L, 28L, 28L, 28L, + 28L, 29L, 29L, 29L, 29L, 30L, 30L, 30L, 30L, 31L, 31L, 31L, 31L, + 32L, 32L, 32L, 32L, 33L, 33L, 33L, 33L, 34L, 34L, 34L, 34L, 35L, + 35L, 35L, 35L, 36L, 36L, 36L, 36L, 37L, 37L, 37L, 37L, 38L, 38L, + 38L, 38L, 39L, 39L, 39L, 39L, 40L, 40L, 40L, 40L, 41L, 41L, 41L, + 41L, 42L, 42L, 42L, 42L, 43L, 43L, 43L, 43L, 44L, 44L, 44L, 44L, + 45L, 45L, 45L, 45L, 46L, 46L, 46L, 46L, 47L, 47L, 47L, 47L, 48L, + 48L, 48L, 48L, 49L, 49L, 49L, 49L, 50L, 50L, 50L, 50L, 51L, 51L, + 51L, 51L, 52L, 52L, 52L, 52L, 53L, 53L, 53L, 53L, 54L, 54L, 54L, + 54L, 55L, 55L, 55L, 55L, 56L, 56L, 56L, 56L, 57L, 57L, 57L, 57L, + 58L, 58L, 58L, 58L, 59L, 59L, 59L, 59L, 60L, 60L, 60L, 60L, 61L, + 61L, 61L, 61L, 62L, 62L, 62L, 62L, 63L, 63L, 63L, 63L, 64L, 64L, + 64L, 64L, 65L, 65L, 65L, 65L, 66L, 66L, 66L, 66L, 67L, 67L, 67L, + 67L, 68L, 68L, 68L, 68L, 69L, 69L, 69L, 69L, 70L, 70L, 70L, 70L, + 71L, 71L, 71L, 71L, 72L, 72L, 72L, 72L, 73L, 73L, 73L, 73L, 74L, + 74L, 74L, 74L, 75L, 75L, 75L, 75L, 76L, 76L, 76L, 76L, 77L, 77L, + 77L, 77L, 78L, 78L, 78L, 78L, 79L, 79L, 79L, 79L, 80L, 80L, 80L, + 80L, 81L, 81L, 81L, 81L, 82L, 82L, 82L, 82L, 83L, 83L, 83L, 83L, + 84L, 84L, 84L, 84L, 85L, 85L, 85L, 85L, 86L, 86L, 86L, 86L, 87L, + 87L, 87L, 87L, 88L, 88L, 88L, 88L, 89L, 89L, 89L, 89L, 90L, 90L, + 90L, 90L, 91L, 91L, 91L, 91L, 92L, 92L, 92L, 92L, 93L, 93L, 93L, + 93L, 94L, 94L, 94L, 94L, 95L, 95L, 95L, 95L, 96L, 96L, 96L, 96L, + 97L, 97L, 97L, 97L, 98L, 98L, 98L, 98L, 99L, 99L, 99L, 99L, 100L, + 100L, 100L, 100L, 101L, 101L, 101L, 101L, 102L, 102L, 102L, 102L, + 103L, 103L, 103L, 103L, 104L, 104L, 104L, 104L, 105L, 105L, 105L, + 105L, 106L, 106L, 106L, 106L, 107L, 107L, 107L, 107L, 108L, 108L, + 108L, 108L, 109L, 109L, 109L, 109L, 110L, 110L, 110L, 110L, 111L, + 111L, 111L, 111L, 112L, 112L, 112L, 112L, 113L, 113L, 113L, 113L, + 114L, 114L, 114L, 114L, 115L, 115L, 115L, 115L, 116L, 116L, 116L, + 116L, 117L, 117L, 117L, 117L, 118L, 118L, 118L, 118L, 119L, 119L, + 119L, 119L, 120L, 120L, 120L, 120L, 121L, 121L, 121L, 121L, 122L, + 122L, 122L, 122L, 123L, 123L, 123L, 123L, 124L, 124L, 124L, 124L, + 125L, 125L, 125L, 125L, 126L, 126L, 126L, 126L, 127L, 127L, 127L, + 127L, 128L, 128L, 128L, 128L, 129L, 129L, 129L, 129L, 130L, 130L, + 130L, 130L, 131L, 131L, 131L, 131L, 132L, 132L, 132L, 132L, 133L, + 133L, 133L, 133L, 134L, 134L, 134L, 134L, 135L, 135L, 135L, 135L, + 136L, 136L, 136L, 136L, 137L, 137L, 137L, 137L, 138L, 138L, 138L, + 138L, 139L, 139L, 139L, 139L, 140L, 140L, 140L, 140L, 141L, 141L, + 141L, 141L, 142L, 142L, 142L, 142L, 143L, 143L, 143L, 143L, 144L, + 144L, 144L, 144L, 145L, 145L, 145L, 145L, 146L, 146L, 146L, 146L, + 147L, 147L, 147L, 147L, 148L, 148L, 148L, 148L, 149L, 149L, 149L, + 149L, 150L, 150L, 150L, 150L, 151L, 151L, 151L, 151L, 152L, 152L, + 152L, 152L, 153L, 153L, 153L, 153L, 154L, 154L, 154L, 154L, 155L, + 155L, 155L, 155L, 156L, 156L, 156L, 156L, 157L, 157L, 157L, 157L, + 158L, 158L, 158L, 158L, 159L, 159L, 159L, 159L, 160L, 160L, 160L, + 160L, 161L, 161L, 161L, 161L, 162L, 162L, 162L, 162L, 163L, 163L, + 163L, 163L, 164L, 164L, 164L, 164L, 165L, 165L, 165L, 165L, 166L, + 166L, 166L, 166L, 167L, 167L, 167L, 167L, 168L, 168L, 168L, 168L, + 169L, 169L, 169L, 169L, 170L, 170L, 170L, 170L, 171L, 171L, 171L, + 171L, 172L, 172L, 172L, 172L, 173L, 173L, 173L, 173L, 174L, 174L, + 174L, 174L, 175L, 175L, 175L, 175L, 176L, 176L, 176L, 176L, 177L, + 177L, 177L, 177L, 178L, 178L, 178L, 178L, 179L, 179L, 179L, 179L, + 180L, 180L, 180L, 180L, 181L, 181L, 181L, 181L, 182L, 182L, 182L, + 182L, 183L, 183L, 183L, 183L, 184L, 184L, 184L, 184L, 185L, 185L, + 185L, 185L, 186L, 186L, 186L, 186L, 187L, 187L, 187L, 187L, 188L, + 188L, 188L, 188L, 189L, 189L, 189L, 189L, 190L, 190L, 190L, 190L, + 191L, 191L, 191L, 191L, 192L, 192L, 192L, 192L, 193L, 193L, 193L, + 193L, 194L, 194L, 194L, 194L, 195L, 195L, 195L, 195L, 196L, 196L, + 196L, 196L, 197L, 197L, 197L, 197L, 198L, 198L, 198L, 198L, 199L, + 199L, 199L, 199L, 200L, 200L, 200L, 200L +), waves = c( + 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, + 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L, 1L, 2L, 3L, 4L +)), row.names = c( + NA, + -800L +), class = c("tbl_df", "tbl", "data.frame")) + +head(data) + +# geeasy ---- +library(geeasy) +model <- geeasy::geelm( + formula = FEV1_BIN ~ ARMCD + RACE, + id = id, + waves = waves, + data = data, + family = binomial(), + corstr = "unstructured", + control = geeasy::geelm.control(scale.fix = TRUE) +) + +# proc genmod data=ana.dat descending; +# class RACE(ref = 'Asian') USUBJID ARMCD(ref = 'PBO') AVISIT; +# model FEV1_BIN = ARMCD RACE / dist=bin link=logit ; +# repeated subject=USUBJID / within=AVISIT type=UN CORRW; +# run; + +# Working Correlation Matrix +# +# Col1 Col2 Col3 Col4 +# +# Row1 1.0000 -0.0868 -0.1859 -0.1480 +# Row2 -0.0868 1.0000 0.0788 0.0077 +# Row3 -0.1859 0.0788 1.0000 0.0698 +# Row4 -0.1480 0.0077 0.0698 1.0000 + +# We obtain different correlation parameter estimates: +model$geese$alpha +# [1] -0.056458907 -0.125737003 0.067815234 -0.083599271 -0.057773977 0.009216794 + +# GEE Fit Criteria +# +# QIC 303.9454 +# QICu 303.6369 + +# We obtain different QIC values: +QIC(model) +# QIC QICu Quasi Lik CIC params QICC +# 302.767472 303.226407 -147.613203 3.770533 4.000000 303.950268 + +# Analysis Of GEE Parameter Estimates +# Empirical Standard Error Estimates +# +# Standard 95% Confidence +# Parameter Estimate Error Limits Z Pr > |Z| +# +# Intercept 1.7193 0.2775 1.1755 2.2632 6.20 <.0001 +# ARMCD TRT 0.6781 0.3299 0.0315 1.3247 2.06 0.0398 +# ARMCD PBO 0.0000 0.0000 0.0000 0.0000 . . +# RACE Black or African American 0.1492 0.3246 -0.4869 0.7853 0.46 0.6457 +# RACE White 1.3111 0.5543 0.2246 2.3975 2.37 0.0180 +# RACE Asian 0.0000 0.0000 0.0000 0.0000 . . + +# We obtain different estimates: +coef(model) +# (Intercept) ARMCDTRT RACEBlack or African American RACEWhite +# 1.7582943 0.6784657 0.1705619 1.3864354 + +# And this fails, not sure why? +summary(model) +# seems the wts vector is too long (original size of the data instead of na omitted) +# Passing in na.omit(data) above works. + +# SAS ---- +sascode <- list( + "test" = " + proc genmod data=ana.dat descending; + class RACE(ref = 'Asian') id ARMCD(ref = 'PBO') waves; + model FEV1_BIN = ARMCD RACE / dist=bin link=logit ; + repeated subject=id / within=waves type=UN CORRW; + run; + " +) + +sas_data <- data +result <- r2stream::bee_sas(dat = list("dat" = sas_data), sascode = sascode) +result$test$sas_log +writeLines(result$test$sas_out, con = "sas_log.txt") + +# geeM ---- +library(geeM) +complete_data <- na.omit(data) +geem_model <- geeM::geem( + formula = FEV1_BIN ~ ARMCD + RACE, + id = id, + waves = complete_data$waves, + data = complete_data, + family = binomial(), + corstr = "unstructured", + scale.fix = TRUE +) +# takes very long... diff --git a/design/sas_log.txt b/design/sas_log.txt new file mode 100644 index 0000000..7be97b3 --- /dev/null +++ b/design/sas_log.txt @@ -0,0 +1,105 @@ + The SAS System Friday, October 21, 2022 11:09:00 AM 1 + + The GENMOD Procedure + + Model Information + + Data Set ANA.DAT + Distribution Binomial + Link Function Logit + Dependent Variable FEV1_BIN + + + Number of Observations Read 800 + Number of Observations Used 537 + Number of Events 492 + Number of Trials 537 + Missing Values 263 + + + Class Level Information + + Class Levels Values + + RACE 3 Black or African American White Asian + ID 197 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 + 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 + 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 55 + 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 + 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 + ... + ARMCD 2 TRT PBO + WAVES 4 1 2 3 4 + + + Response Profile + + Ordered Total + Value FEV1_BIN Frequency + + 1 1 492 + 2 0 45 + +PROC GENMOD is modeling the probability that FEV1_BIN='1'. + + + Parameter Information + + Parameter Effect RACE ARMCD + + Prm1 Intercept + Prm2 ARMCD TRT + Prm3 ARMCD PBO + Prm4 RACE Black or African American + Prm5 RACE White + Prm6 RACE Asian + + + Algorithm converged. + The SAS System Friday, October 21, 2022 11:09:00 AM 2 + + The GENMOD Procedure + + GEE Model Information + + Correlation Structure Unstructured + Within-Subject Effect WAVES (4 levels) + Subject Effect ID (200 levels) + Number of Clusters 200 + Clusters With Missing Values 161 + Correlation Matrix Dimension 4 + Maximum Cluster Size 4 + Minimum Cluster Size 0 + + + Algorithm converged. + + + Working Correlation Matrix + + Col1 Col2 Col3 Col4 + + Row1 1.0000 -0.0868 -0.1859 -0.1480 + Row2 -0.0868 1.0000 0.0788 0.0077 + Row3 -0.1859 0.0788 1.0000 0.0698 + Row4 -0.1480 0.0077 0.0698 1.0000 + + + GEE Fit Criteria + + QIC 303.9454 + QICu 303.6369 + + + Analysis Of GEE Parameter Estimates + Empirical Standard Error Estimates + + Standard 95% Confidence + Parameter Estimate Error Limits Z Pr > |Z| + + Intercept 1.7193 0.2775 1.1755 2.2632 6.20 <.0001 + ARMCD TRT 0.6781 0.3299 0.0315 1.3247 2.06 0.0398 + ARMCD PBO 0.0000 0.0000 0.0000 0.0000 . . + RACE Black or African American 0.1492 0.3246 -0.4869 0.7853 0.46 0.6457 + RACE White 1.3111 0.5543 0.2246 2.3975 2.37 0.0180 + RACE Asian 0.0000 0.0000 0.0000 0.0000 . . diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd index 541903f..97f5292 100644 --- a/man/gee_methods.Rd +++ b/man/gee_methods.Rd @@ -3,9 +3,12 @@ \name{gee_methods} \alias{gee_methods} \alias{VarCorr.tern_gee} +\alias{QIC.tern_gee} \title{Method for GEE Models} \usage{ \method{VarCorr}{tern_gee}(x, sigma = 1, ...) + +\method{QIC}{tern_gee}(object, ...) } \description{ Additional methods which can simplify working with the GEE result object. diff --git a/tests/testthat/test-tabulate_gee.R b/tests/testthat/test-tabulate_gee.R index 3f1e17c..be3d91e 100644 --- a/tests/testthat/test-tabulate_gee.R +++ b/tests/testthat/test-tabulate_gee.R @@ -28,3 +28,5 @@ test_that("summarize_gee_logistic works", { ) # to do: add expectation }) + + From 7eb8190fe6ce708d56fc3053506d1dc6af808ab4 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 10:00:05 +0000 Subject: [PATCH 19/45] [skip actions] Restyle files --- tests/testthat/test-tabulate_gee.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testthat/test-tabulate_gee.R b/tests/testthat/test-tabulate_gee.R index be3d91e..3f1e17c 100644 --- a/tests/testthat/test-tabulate_gee.R +++ b/tests/testthat/test-tabulate_gee.R @@ -28,5 +28,3 @@ test_that("summarize_gee_logistic works", { ) # to do: add expectation }) - - From 828b243265070c0d4c4c99f680e2b23d82d7d472 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Fri, 21 Oct 2022 10:09:50 +0000 Subject: [PATCH 20/45] [skip actions] Roxygen Man Pages Auto Update --- man/fit_gee.Rd | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/man/fit_gee.Rd b/man/fit_gee.Rd index 383cda1..d313af0 100644 --- a/man/fit_gee.Rd +++ b/man/fit_gee.Rd @@ -8,8 +8,7 @@ fit_gee( vars = vars_gee(), data, regression = c("logistic"), - cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive"), - control = geeasy::geelm.control() + cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive") ) } \arguments{ @@ -20,8 +19,6 @@ fit_gee( \item{regression}{(\code{string})\cr choice of regression model.} \item{cor_struct}{(\code{string})\cr assumed correlation structure.} - -\item{control}{(\code{list})\cr see \code{\link[=control_gee]{control_gee()}}.} } \value{ Object of class \code{tern_gee} as well as specific to the kind of regression From 421ff8e6fd47fc2b81453950ca1f47e9b62e13ac Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 25 Oct 2022 12:15:07 -0400 Subject: [PATCH 21/45] Merge branch 'main' into 2_prototype --- .github/workflows/check.yaml | 2 -- DESCRIPTION | 20 +++++++++----------- NAMESPACE | 1 + NEWS.md | 5 +++-- R/hello.R | 7 +++++++ man/dummy_function.Rd | 11 +++++++++++ staged_dependencies.yaml | 9 ++++++--- tern.gee.Rproj | 6 +++--- tests/testthat/test-dummy.R | 6 ++++++ 9 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 R/hello.R create mode 100644 man/dummy_function.Rd create mode 100644 tests/testthat/test-dummy.R diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index d5c6655..ec90c83 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -24,8 +24,6 @@ jobs: uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main secrets: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} - with: - additional-r-cmd-check-params: --as-cran coverage: name: Coverage 📔 uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main diff --git a/DESCRIPTION b/DESCRIPTION index ba7e719..f812356 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,9 @@ Type: Package Package: tern.gee -Title: Tables and Graphs for Generalized Estimating Equations (GEE) Model Fits -Version: 0.0.9001 -Date: 2022-10-03 +Title: Tables and Graphs for Generalized Estimating Equations (GEE) Model + Fits +Version: 0.0.0.9002 +Date: 2022-10-25 Authors@R: c( person("Daniel", "Sabanés Bové", , "daniel.sabanes_bove@roche.com", role = c("aut", "cre")), person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")) @@ -13,15 +14,10 @@ URL: https://github.com/insightsengineering/tern.gee/ BugReports: https://github.com/insightsengineering/tern.gee/issues Depends: R (>= 4.0), - tern (>= 0.7.9), - nestcolor (>= 0.0.1) + tern (>= 0.7.9) Imports: - checkmate, - geeasy, - geepack, - nlme, - rtables, - stats + formatters, + rtables Suggests: knitr, rmarkdown, @@ -29,6 +25,8 @@ Suggests: vdiffr VignetteBuilder: knitr +Remotes: + insightsengineering/tern@*release Config/testthat/edition: 3 Encoding: UTF-8 Language: en-US diff --git a/NAMESPACE b/NAMESPACE index f02cbac..949e9bb 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,6 +6,7 @@ S3method(as.rtable,tern_gee) S3method(lsmeans,tern_gee_logistic) export(a_lsmeans_logistic) export(as.rtable) +export(dummy_function) export(fit_gee) export(lsmeans) export(s_lsmeans_logistic) diff --git a/NEWS.md b/NEWS.md index 824388f..ef69cfa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,4 @@ -# tern.gee 0.0.9001 +# tern.gee 0.0.0.9002 +### New features -* First release of the `tern.gee` package. +* New package diff --git a/R/hello.R b/R/hello.R new file mode 100644 index 0000000..2d24a14 --- /dev/null +++ b/R/hello.R @@ -0,0 +1,7 @@ +#' A dummy function +#' @export +dummy_function <- function() { + rtables::basic_table() + formatters::divider_height() + as.rtable(data.frame(x = 1:10)) +} diff --git a/man/dummy_function.Rd b/man/dummy_function.Rd new file mode 100644 index 0000000..1c11bf2 --- /dev/null +++ b/man/dummy_function.Rd @@ -0,0 +1,11 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/hello.R +\name{dummy_function} +\alias{dummy_function} +\title{A dummy function} +\usage{ +dummy_function() +} +\description{ +A dummy function +} diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index d9d786c..fb79fca 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -4,10 +4,13 @@ current_repo: repo: insightsengineering/tern.gee host: https://github.com upstream_repos: - - repo: insightsengineering/nestcolor + insightsengineering/formatters: + repo: insightsengineering/formatters host: https://github.com - - repo: Roche/rtables + Roche/rtables: + repo: Roche/rtables host: https://github.com - - repo: insightsengineering/tern + insightsengineering/tern: + repo: insightsengineering/tern host: https://github.com downstream_repos: diff --git a/tern.gee.Rproj b/tern.gee.Rproj index cba1b6b..85d0bd5 100644 --- a/tern.gee.Rproj +++ b/tern.gee.Rproj @@ -1,7 +1,7 @@ Version: 1.0 -RestoreWorkspace: No -SaveWorkspace: No +RestoreWorkspace: Default +SaveWorkspace: Default AlwaysSaveHistory: Default EnableCodeIndexing: Yes @@ -14,8 +14,8 @@ LaTeX: pdfLaTeX AutoAppendNewline: Yes StripTrailingWhitespace: Yes +LineEndingConversion: Posix BuildType: Package -PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source PackageRoxygenize: rd,collate,namespace diff --git a/tests/testthat/test-dummy.R b/tests/testthat/test-dummy.R new file mode 100644 index 0000000..d6bbc7a --- /dev/null +++ b/tests/testthat/test-dummy.R @@ -0,0 +1,6 @@ +test_that("dummy", { + # Compare + result <- "Hello, Tim" + expected <- "Hello, Tim" + expect_identical(result, expected) +}) From 51a28bd8eed92efb8762b9aa7b77738853460bba Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 26 Oct 2022 19:12:49 -0400 Subject: [PATCH 22/45] Remove dummy function/test --- .github/workflows/check.yaml | 2 ++ DESCRIPTION | 6 ++++++ NAMESPACE | 1 - R/hello.R | 7 ------- man/dummy_function.Rd | 11 ----------- tern.gee.Rproj | 5 ++--- tests/testthat/test-dummy.R | 6 ------ 7 files changed, 10 insertions(+), 28 deletions(-) delete mode 100644 R/hello.R delete mode 100644 man/dummy_function.Rd delete mode 100644 tests/testthat/test-dummy.R diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index ec90c83..d5c6655 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -24,6 +24,8 @@ jobs: uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main secrets: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} + with: + additional-r-cmd-check-params: --as-cran coverage: name: Coverage 📔 uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main diff --git a/DESCRIPTION b/DESCRIPTION index f812356..0291b09 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -16,6 +16,12 @@ Depends: R (>= 4.0), tern (>= 0.7.9) Imports: + checkmate, + geeasy, + geepack, + nlme, + rtables, + stats, formatters, rtables Suggests: diff --git a/NAMESPACE b/NAMESPACE index 949e9bb..f02cbac 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -6,7 +6,6 @@ S3method(as.rtable,tern_gee) S3method(lsmeans,tern_gee_logistic) export(a_lsmeans_logistic) export(as.rtable) -export(dummy_function) export(fit_gee) export(lsmeans) export(s_lsmeans_logistic) diff --git a/R/hello.R b/R/hello.R deleted file mode 100644 index 2d24a14..0000000 --- a/R/hello.R +++ /dev/null @@ -1,7 +0,0 @@ -#' A dummy function -#' @export -dummy_function <- function() { - rtables::basic_table() - formatters::divider_height() - as.rtable(data.frame(x = 1:10)) -} diff --git a/man/dummy_function.Rd b/man/dummy_function.Rd deleted file mode 100644 index 1c11bf2..0000000 --- a/man/dummy_function.Rd +++ /dev/null @@ -1,11 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/hello.R -\name{dummy_function} -\alias{dummy_function} -\title{A dummy function} -\usage{ -dummy_function() -} -\description{ -A dummy function -} diff --git a/tern.gee.Rproj b/tern.gee.Rproj index 85d0bd5..ded5673 100644 --- a/tern.gee.Rproj +++ b/tern.gee.Rproj @@ -1,7 +1,7 @@ Version: 1.0 -RestoreWorkspace: Default -SaveWorkspace: Default +RestoreWorkspace: No +SaveWorkspace: No AlwaysSaveHistory: Default EnableCodeIndexing: Yes @@ -14,7 +14,6 @@ LaTeX: pdfLaTeX AutoAppendNewline: Yes StripTrailingWhitespace: Yes -LineEndingConversion: Posix BuildType: Package PackageInstallArgs: --no-multiarch --with-keep.source diff --git a/tests/testthat/test-dummy.R b/tests/testthat/test-dummy.R deleted file mode 100644 index d6bbc7a..0000000 --- a/tests/testthat/test-dummy.R +++ /dev/null @@ -1,6 +0,0 @@ -test_that("dummy", { - # Compare - result <- "Hello, Tim" - expected <- "Hello, Tim" - expect_identical(result, expected) -}) From 393c4e9fe6c84a631bbc49b1456f8c29a87e8e5d Mon Sep 17 00:00:00 2001 From: Emily de la Rua <59304861+edelarua@users.noreply.github.com> Date: Fri, 28 Oct 2022 12:10:04 -0400 Subject: [PATCH 23/45] Update pkgdown site (#15) * Update pkgdown * Update NEWS --- NEWS.md | 6 ++++-- _pkgdown.yml | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/NEWS.md b/NEWS.md index ef69cfa..10a50d8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,6 @@ # tern.gee 0.0.0.9002 -### New features -* New package +### Miscellaneous + +* Initialize the package. +* Update pkgdown site. diff --git a/_pkgdown.yml b/_pkgdown.yml index b0f2810..b738c73 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -9,13 +9,22 @@ navbar: href: https://github.com/insightsengineering/tern.gee reference: - - title: Analysis Helper Functions - desc: these functions are useful to help definining the analysis + - title: Overview Pages contents: - - control_gee - - vars_gee + - tern.gee-package + + - title: Helper Methods + contents: + - gee_methods - title: Model Specific Functions - desc: these functions help with fitting or extracting results from specific models + desc: These functions help with fitting or extracting results from specific models. contents: + - as.rtable.tern_gee - fit_gee + - lsmeans + - tabulate_gee + + - title: Example Data + contents: + - fev_data From 07cc99037ab04502d5bcade4d74e4d2b2f2bc76a Mon Sep 17 00:00:00 2001 From: Emily de la Rua <59304861+edelarua@users.noreply.github.com> Date: Tue, 1 Nov 2022 20:02:31 -0400 Subject: [PATCH 24/45] 10 Add unit tests (#17) * Add tests for build functions * Add tests * Update NEWS * Fix labels --- NEWS.md | 1 + tests/testthat/test-fit_gee.R | 40 +++++++++- tests/testthat/test-tabulate_gee.R | 116 ++++++++++++++++++++++++++--- 3 files changed, 144 insertions(+), 13 deletions(-) diff --git a/NEWS.md b/NEWS.md index 10a50d8..f79c122 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,3 +4,4 @@ * Initialize the package. * Update pkgdown site. +* Add unit tests. diff --git a/tests/testthat/test-fit_gee.R b/tests/testthat/test-fit_gee.R index 2e59034..970887c 100644 --- a/tests/testthat/test-fit_gee.R +++ b/tests/testthat/test-fit_gee.R @@ -1,16 +1,48 @@ # build_formula ---- +test_that("build_formula builds the correct formula", { + vars <- list(response = "AVAL", id = "USUBJID", arm = "ARMCD", visit = "AVISIT", covariates = c("RACE", "SEX")) + result <- as.character(build_formula(vars)) + expected <- as.character(AVAL ~ ARMCD + RACE + SEX) + expect_identical(result, expected) +}) + test_that("build_formula works without covariates", { result <- expect_silent(build_formula(vars_gee())) expected <- AVAL ~ ARM expect_equal(result, expected, ignore_attr = TRUE) }) +# build_family ---- + +test_that("build_family returns the correct class", { + result <- build_family("logistic") + expected <- "tern_gee_logistic" + expect_identical(result$class, expected) + expect_class(result$object, "family") +}) + +test_that("build_family gives error message if incorrect regression type is given", { + expect_error(build_family("BLAHHH"), "regression type BLAHHH not supported") +}) + +# build_cor_details ---- + +test_that("build_cor_details returns the correct correlation details", { + result <- as.character(build_cor_details("unstructured", fev_vars, fev_data)) + expected <- c("unstructured", "1") + expect_identical(result, expected) +}) + +test_that("build_cor_details gives error message if incorrect correlation structure is given", { + expect_error(build_cor_details("BLAHHH", fev_vars, fev_data), "correlation structure BLAHHH not available") +}) + # fit_gee ---- ## logistic ---- -test_that("unstructured", { +test_that("fit_gee works as expected for unstructured correlation structure", { result <- expect_silent(fit_gee( vars = fev_vars, data = fev_data, @@ -20,7 +52,7 @@ test_that("unstructured", { expect_class(result, c("tern_gee_logistic", "tern_gee", "geelm")) }) -test_that("auto-regressive", { +test_that("fit_gee works as expected for AR1 correlation structure", { result <- expect_silent(fit_gee( vars = fev_vars, data = fev_data, @@ -30,7 +62,7 @@ test_that("auto-regressive", { expect_class(result, c("tern_gee_logistic", "tern_gee", "geelm")) }) -test_that("compound symmetry", { +test_that("fit_gee works as expected for compound symmetry correlation structure", { result <- expect_silent(fit_gee( vars = fev_vars, data = fev_data, @@ -40,7 +72,7 @@ test_that("compound symmetry", { expect_class(result, c("tern_gee_logistic", "tern_gee", "geelm")) }) -test_that("toeplitz", { +test_that("fit_gee works as expected for Toeplitz correlation structure", { result <- expect_silent(fit_gee( vars = fev_vars, data = fev_data, diff --git a/tests/testthat/test-tabulate_gee.R b/tests/testthat/test-tabulate_gee.R index 3f1e17c..1996e06 100644 --- a/tests/testthat/test-tabulate_gee.R +++ b/tests/testthat/test-tabulate_gee.R @@ -1,3 +1,7 @@ +dat_adsl <- fev_data %>% + dplyr::select(USUBJID, ARMCD) %>% + dplyr::distinct() + model <- fit_gee( vars = fev_vars, data = fev_data, @@ -5,19 +9,103 @@ model <- fit_gee( cor_struct = "unstructured" ) -test_that("h_gee_coef works", { - result <- expect_silent(h_gee_coef(model)) +# to do: finish test once h_gee_coef is working correctly +test_that("h_gee_coef works as expected", { + # result <- expect_silent(h_gee_coef(model)) + # result2 <- as.rtable(model, type = "coef", format = "xx.xxxx") + # expect_identical(result, result2) + # + # result_matrix <- to_string_matrix(result) + # expected_matrix <- structure( + # c( + # ), + # dim = c() + # ) + # expect_identical(result_matrix, expected_matrix) }) -test_that("h_gee_cov works", { +test_that("h_gee_cov works as expected", { result <- expect_silent(h_gee_cov(model)) - # to do: add expectation + result2 <- as.rtable(model, type = "cov", format = "xx.xxxx") + expect_identical(result, result2) + + result_matrix <- to_string_matrix(result) + expected_matrix <- structure( + c( + "", "VIS1", "VIS2", "VIS3", "VIS4", + "VIS1", "1.0000", "-0.0565", "-0.1257", "0.0678", + "VIS2", "-0.0565", "1.0000", "-0.0836", "-0.0578", + "VIS3", "-0.1257", "-0.0836", "1.0000", "0.0092", + "VIS4", "0.0678", "-0.0578", "0.0092", "1.0000" + ), + dim = c(5L, 5L) + ) + expect_identical(result_matrix, expected_matrix) +}) + +test_that("s_lsmeans_logistic works as expected when not in reference column", { + result <- s_lsmeans_logistic(lsmeans(model)[2, ], FALSE) + expected <- list( + n = 380L, + adj_prop_se = c(0.94694895, 0.01347376), + adj_prop_ci = formatters::with_label(c(0.9134350, 0.9679432), label = "95% CI"), + odds_ratio_est = 1.970852, + odds_ratio_ci = formatters::with_label(c(1.031652, 3.765083), label = "95% CI"), + log_odds_ratio_est = 0.6784657, + log_odds_ratio_ci = formatters::with_label(c(0.03116157, 1.32576989), label = "95% CI") + ) + expect_equal(result, expected, tolerance = 1e-2) +}) + +test_that("s_mmrm_lsmeans works as expected when in reference column", { + result <- s_lsmeans_logistic(lsmeans(model)[1, ], TRUE) + expected <- list( + n = 420L, + adj_prop_se = c(0.9005656, 0.0198212), + adj_prop_ci = formatters::with_label(c(0.8544189, 0.9332277), label = "95% CI"), + odds_ratio_est = character(0), + odds_ratio_ci = formatters::with_label(character(0), label = "95% CI"), + log_odds_ratio_est = character(0), + log_odds_ratio_ci = formatters::with_label(character(0), label = "95% CI") + ) + expect_equal(result, expected, tolerance = 1e-2) +}) + +test_that("summarize_gee_logistic works as expected with covariates in the model", { + result <- basic_table() %>% + split_cols_by("ARMCD", ref_group = model$ref_level) %>% + add_colcounts() %>% + summarize_gee_logistic() %>% + build_table( + df = lsmeans(model), + alt_counts_df = dat_adsl + ) + + result_matrix <- to_string_matrix(result) + expected_matrix <- structure( + c( + "", "", "n", "Adjusted Mean Proportion (SE)", "95% CI", "Odds Ratio", "95% CI", "Log Odds Ratio", "95% CI", + "PBO", "(N=105)", "420", "0.90 (0.02)", "(0.85, 0.93)", "", "", "", "", + "TRT", "(N=95)", "380", "0.95 (0.01)", "(0.91, 0.97)", "1.97", "(1.03, 3.77)", "0.68", "(0.03, 1.33)" + ), + dim = c(9L, 3L) + ) + expect_identical(result_matrix, expected_matrix) }) -test_that("summarize_gee_logistic works", { - dat_adsl <- fev_data %>% - dplyr::select(USUBJID, ARMCD) %>% - dplyr::distinct() +test_that("summarize_gee_logistic works as expected with no covariates in the model", { + fev_vars_nocov <- vars_gee( + response = "FEV1_BINARY", + arm = "ARMCD", + id = "USUBJID", + visit = "AVISIT" + ) + model <- fit_gee( + vars = fev_vars_nocov, + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" + ) result <- basic_table() %>% split_cols_by("ARMCD", ref_group = model$ref_level) %>% add_colcounts() %>% @@ -26,5 +114,15 @@ test_that("summarize_gee_logistic works", { df = lsmeans(model), alt_counts_df = dat_adsl ) - # to do: add expectation + + result_matrix <- to_string_matrix(result) + expected_matrix <- structure( + c( + "", "", "n", "Adjusted Mean Proportion (SE)", "95% CI", "Odds Ratio", "95% CI", "Log Odds Ratio", "95% CI", + "PBO", "(N=105)", "420", "0.88 (0.02)", "(0.84, 0.92)", "", "", "", "", + "TRT", "(N=95)", "380", "0.94 (0.01)", "(0.91, 0.97)", "2.18", "(1.16, 4.10)", "0.78", "(0.15, 1.41)" + ), + dim = c(9L, 3L) + ) + expect_identical(result_matrix, expected_matrix) }) From ad229827ffdd6206664f993a084c254643d3a35b Mon Sep 17 00:00:00 2001 From: Emily de la Rua <59304861+edelarua@users.noreply.github.com> Date: Wed, 2 Nov 2022 07:35:33 -0400 Subject: [PATCH 25/45] 9 Update documentation, add examples (#16) * Add examples * Update documentation * Update data * Update grammar Signed-off-by: Emily de la Rua <59304861+edelarua@users.noreply.github.com> --- NEWS.md | 4 ++-- R/fit_gee.R | 9 ++++++++- R/gee_methods.R | 2 +- R/lsmeans.R | 11 ++++++++++- R/tabulate_gee.R | 40 +++++++++++++++++++++++++++++++++------- man/fit_gee.Rd | 8 ++++++++ man/gee_methods.Rd | 2 +- man/lsmeans.Rd | 9 +++++++++ man/tabulate_gee.Rd | 32 ++++++++++++++++++++++++++------ 9 files changed, 98 insertions(+), 19 deletions(-) diff --git a/NEWS.md b/NEWS.md index f79c122..6287e4f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,5 +3,5 @@ ### Miscellaneous * Initialize the package. -* Update pkgdown site. -* Add unit tests. +* Update pkgdown site, add unit tests. +* Add examples for `fit_gee`, `lsmeans`, `s_lsmeans_logistic`, `summarize_gee_logistic`. diff --git a/R/fit_gee.R b/R/fit_gee.R index a93f2d2..aa15535 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -108,6 +108,14 @@ order_data <- function(data, vars) { #' @return Object of class `tern_gee` as well as specific to the kind of regression #' which was used. #' @export +#' +#' @examples +#' df <- fev_data +#' df$AVAL <- as.integer(fev_data$FEV1 > 30) +#' +#' fit_gee(vars = vars_gee(arm = "ARMCD"), data = df) +#' +#' fit_gee(vars = vars_gee(arm = "ARMCD"), data = df, cor_struct = "compound symmetry") fit_gee <- function(vars = vars_gee(), data, regression = c("logistic"), @@ -124,7 +132,6 @@ fit_gee <- function(vars = vars_gee(), cor_struct <- match.arg(cor_struct) cor_details <- build_cor_details(cor_struct, vars, data) - browser() fit <- geeasy::geelm( formula = formula, id = .id, diff --git a/R/gee_methods.R b/R/gee_methods.R index a350e92..132c2a5 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -1,4 +1,4 @@ -#' Method for GEE Models +#' Methods for GEE Models #' #' Additional methods which can simplify working with the GEE result object. #' @name gee_methods diff --git a/R/lsmeans.R b/R/lsmeans.R index 6d43e84..ac74a94 100644 --- a/R/lsmeans.R +++ b/R/lsmeans.R @@ -4,10 +4,19 @@ #' @param conf_level (`proportion`)\cr confidence level #' @param weights (`string`)\cr type of weights to be used for the least square means, #' see [emmeans::emmeans()] for details. +#' #' @return A `data.frame` with least-square means and contrasts. Additional #' classes allow to dispatch downstream methods correctly, too. -#' #' @export +#' +#' @examples +#' df <- fev_data +#' df$AVAL <- rbinom(n = nrow(df), size = 1, prob = 0.5) +#' fit <- fit_gee(vars = vars_gee(arm = "ARMCD"), data = df) +#' +#' lsmeans(fit) +#' +#' lsmeans(fit, conf_level = 0.90, weights = "equal") lsmeans <- function(object, conf_level = 0.95, weights = "proportional", diff --git a/R/tabulate_gee.R b/R/tabulate_gee.R index 73f1546..c62ff9b 100644 --- a/R/tabulate_gee.R +++ b/R/tabulate_gee.R @@ -1,7 +1,6 @@ #' Tabulation of a GEE Model #' -#' These functions can be used to produce tables from a fitted GEE produced with -#' [fit_gee()]. +#' Functions to produce tables from a fitted GEE produced with [fit_gee()]. #' #' @name tabulate_gee NULL @@ -11,8 +10,7 @@ NULL tern::as.rtable #' @exportS3Method -#' @describeIn tabulate_gee extracts the coefficient table or covariance matrix -#' estimate from a `tern_gee` object. +#' @describeIn tabulate_gee Extracts the coefficient table or covariance matrix estimate from a `tern_gee` object. as.rtable.tern_gee <- function(x, # nolint type = c("coef", "cov"), ...) { @@ -56,11 +54,28 @@ h_gee_cov <- function(x, format = "xx.xxxx") { # lsmeans_logistic ---- -#' @describeIn tabulate_gee Statistics function which is extracting estimates from a +#' @describeIn tabulate_gee Statistics function which extracts estimates from a #' [lsmeans()] data frame based on a logistic GEE model. -#' @param df (`data.frame`)\cr data set being a result from [lsmeans()]. +#' +#' @param df (`data.frame`)\cr data set resulting from [lsmeans()]. #' @param .in_ref_col (`logical`)\cr `TRUE` when working with the reference level, `FALSE` otherwise. +#' #' @export +#' +#' @examples +#' library(dplyr) +#' +#' df <- fev_data %>% +#' mutate(AVAL = as.integer(fev_data$FEV1 > 30)) +#' df_counts <- df %>% +#' select(USUBJID, ARMCD) %>% +#' unique() +#' +#' lsmeans_df <- lsmeans(fit_gee(vars = vars_gee(arm = "ARMCD"), data = df)) +#' +#' s_lsmeans_logistic(lsmeans_df[1,], .in_ref_col = TRUE) +#' +#' s_lsmeans_logistic(lsmeans_df[2,], .in_ref_col = FALSE) s_lsmeans_logistic <- function(df, .in_ref_col) { if_not_ref <- function(x) `if`(.in_ref_col, character(), x) list( @@ -84,8 +99,8 @@ s_lsmeans_logistic <- function(df, .in_ref_col) { #' @describeIn tabulate_gee Formatted Analysis function which can be further customized by calling #' [rtables::make_afun()] on it. It is used as `afun` in [rtables::analyze()]. -#' @export #' +#' @export a_lsmeans_logistic <- make_afun( s_lsmeans_logistic, .labels = c( @@ -116,6 +131,7 @@ a_lsmeans_logistic <- make_afun( #' @describeIn tabulate_gee Analyze function for tabulating least-squares means estimates #' from logistic GEE least square mean results. +#' #' @param lyt (`layout`)\cr input layout where analyses will be added to. #' @param table_names (`character`)\cr this can be customized in case that the same `vars` #' are analyzed multiple times, to avoid warnings from `rtables`. @@ -123,7 +139,17 @@ a_lsmeans_logistic <- make_afun( #' @param .formats (named `character` or `list`)\cr formats for the statistics. #' @param .indent_mods (named `integer`)\cr indent modifiers for the labels. #' @param .labels (named `character`)\cr labels for the statistics (without indent). +#' #' @export +#' +#' @examples +#' basic_table() %>% +#' split_cols_by("ARMCD") %>% +#' add_colcounts() %>% +#' summarize_gee_logistic( +#' .in_ref_col = FALSE +#' ) %>% +#' build_table(lsmeans_df, alt_counts_df = df_counts) summarize_gee_logistic <- function(lyt, ..., table_names = "lsmeans_logistic_summary", diff --git a/man/fit_gee.Rd b/man/fit_gee.Rd index d313af0..1afd664 100644 --- a/man/fit_gee.Rd +++ b/man/fit_gee.Rd @@ -37,3 +37,11 @@ between two time points depends on the distance between the time indices. \item \code{auto-regressive}: Auto-regressive order 1 correlation matrix. } } +\examples{ +df <- fev_data +df$AVAL <- as.integer(fev_data$FEV1 > 30) + +fit_gee(vars = vars_gee(arm = "ARMCD"), data = df) + +fit_gee(vars = vars_gee(arm = "ARMCD"), data = df, cor_struct = "compound symmetry") +} diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd index 97f5292..504f811 100644 --- a/man/gee_methods.Rd +++ b/man/gee_methods.Rd @@ -4,7 +4,7 @@ \alias{gee_methods} \alias{VarCorr.tern_gee} \alias{QIC.tern_gee} -\title{Method for GEE Models} +\title{Methods for GEE Models} \usage{ \method{VarCorr}{tern_gee}(x, sigma = 1, ...) diff --git a/man/lsmeans.Rd b/man/lsmeans.Rd index 0c60b88..42acace 100644 --- a/man/lsmeans.Rd +++ b/man/lsmeans.Rd @@ -24,3 +24,12 @@ classes allow to dispatch downstream methods correctly, too. \description{ Extract Least Square Means from a GEE Model } +\examples{ +df <- fev_data +df$AVAL <- rbinom(n = nrow(df), size = 1, prob = 0.5) +fit <- fit_gee(vars = vars_gee(arm = "ARMCD"), data = df) + +lsmeans(fit) + +lsmeans(fit, conf_level = 0.90, weights = "equal") +} diff --git a/man/tabulate_gee.Rd b/man/tabulate_gee.Rd index 888c695..0c611fd 100644 --- a/man/tabulate_gee.Rd +++ b/man/tabulate_gee.Rd @@ -25,7 +25,7 @@ summarize_gee_logistic( ) } \arguments{ -\item{df}{(\code{data.frame})\cr data set being a result from \code{\link[=lsmeans]{lsmeans()}}.} +\item{df}{(\code{data.frame})\cr data set resulting from \code{\link[=lsmeans]{lsmeans()}}.} \item{.in_ref_col}{(\code{logical})\cr \code{TRUE} when working with the reference level, \code{FALSE} otherwise.} @@ -43,15 +43,13 @@ are analyzed multiple times, to avoid warnings from \code{rtables}.} \item{.labels}{(named \code{character})\cr labels for the statistics (without indent).} } \description{ -These functions can be used to produce tables from a fitted GEE produced with -\code{\link[=fit_gee]{fit_gee()}}. +Functions to produce tables from a fitted GEE produced with \code{\link[=fit_gee]{fit_gee()}}. } \section{Functions}{ \itemize{ -\item \code{as.rtable(tern_gee)}: extracts the coefficient table or covariance matrix -estimate from a \code{tern_gee} object. +\item \code{as.rtable(tern_gee)}: Extracts the coefficient table or covariance matrix estimate from a \code{tern_gee} object. -\item \code{s_lsmeans_logistic()}: Statistics function which is extracting estimates from a +\item \code{s_lsmeans_logistic()}: Statistics function which extracts estimates from a \code{\link[=lsmeans]{lsmeans()}} data frame based on a logistic GEE model. \item \code{a_lsmeans_logistic()}: Formatted Analysis function which can be further customized by calling @@ -61,3 +59,25 @@ estimate from a \code{tern_gee} object. from logistic GEE least square mean results. }} +\examples{ +library(dplyr) + +df <- fev_data \%>\% + mutate(AVAL = as.integer(fev_data$FEV1 > 30)) +df_counts <- df \%>\% + select(USUBJID, ARMCD) \%>\% + unique() + +lsmeans_df <- lsmeans(fit_gee(vars = vars_gee(arm = "ARMCD"), data = df)) + +s_lsmeans_logistic(lsmeans_df[1,], .in_ref_col = TRUE) + +s_lsmeans_logistic(lsmeans_df[2,], .in_ref_col = FALSE) +basic_table() \%>\% + split_cols_by("ARMCD") \%>\% + add_colcounts() \%>\% + summarize_gee_logistic( + .in_ref_col = FALSE + ) \%>\% + build_table(lsmeans_df, alt_counts_df = df_counts) +} From b3a344fd20f06f4784cd5f7364a6ea1af500e75e Mon Sep 17 00:00:00 2001 From: Emily de la Rua <59304861+edelarua@users.noreply.github.com> Date: Wed, 9 Nov 2022 11:07:43 -0500 Subject: [PATCH 26/45] Update README (#18) Closes #13 Signed-off-by: Emily de la Rua <59304861+edelarua@users.noreply.github.com> --- NEWS.md | 2 +- README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index 6287e4f..2c1363e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,5 +3,5 @@ ### Miscellaneous * Initialize the package. -* Update pkgdown site, add unit tests. +* Update pkgdown site and README, add unit tests. * Add examples for `fit_gee`, `lsmeans`, `s_lsmeans_logistic`, `summarize_gee_logistic`. diff --git a/README.md b/README.md index 518b084..1bd6ef4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,77 @@ # tern.gee -Create TLGs using Generalized Estimating Equations (GEE) +[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) + +## Overview + +`tern.gee` provides an interface for generalized estimating equations (GEE) within the +[`tern`](https://insightsengineering.github.io/tern) framework +to produce commonly used tables (using [`rtables`](https://roche.github.io/rtables)) and graphs. +It builds on the R-package `geepack` for the actual GEE calculations. + +## When to use this package + +If you would like to use the [`tern`](https://insightsengineering.github.io/tern) framework for +tabulation and graphs, then this package is ideal for your GEE fits. +However if you use another reporting framework then it will be better to directly use +`geepack` and perform the tabulation and plots differently. + +## Main Features + +* Fitting of GEE models to continuous longitudinal data collected over several time points + (called visits) and optionally treatment arms. +* Tabulation of least square means per visit and treatment arm. +* Tabulation of the covariance matrix estimate. + +## Installation + +### GitHub + +It is recommended that you [create and use a Github PAT](https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token) to install the latest version of this package. Once you have the PAT, run the following: + +```r +Sys.setenv(GITHUB_PAT = "your_access_token_here") +if (!require("remotes")) install.packages("remotes") +remotes::install_github("insightsengineering/tern.gee@*release") +``` + +### `NEST` distribution + +A stable release of all `NEST` packages is also available [here](https://github.com/insightsengineering/depository#readme). + +## Getting started + +You can get started by trying out the example: + +```r +library(tern.gee) + +fev_data$FEV1_BINARY <- as.integer(fev_data$FEV1 > 30) + +fit <- fit_gee( + vars = list( + response = "FEV1_BINARY", + covariates = c("RACE", "SEX"), + arm = "ARMCD", + id = "USUBJID", + visit = "AVISIT" + ), + data = fev_data +) +``` + +This specifies a GEE with the `FEV1_BINARY` outcome and the `RACE` and `SEX` covariates for +subjects identified by `USUBJID` and treatment arm `ARMCD` observed over time points identified +by `AVISIT` in the `fev_data` data set. By deafult, logistic regression is used and an unstructured +covariance matrix is assumed. The least square means assume equal weights for factor combinations. + +## Stargazers + +### Current + +[![Stargazers repo roster for @insightsengineering/tern.gee](https://reporoster.com/stars/insightsengineering/tern.gee)](https://github.com/insightsengineering/tern.gee/stargazers) +[![Forkers repo roster for @insightsengineering/tern.gee](https://reporoster.com/forks/insightsengineering/tern.gee)](https://github.com/insightsengineering/tern.gee/network/members) + +### Over time + +[![Stargazers over time](https://starchart.cc/insightsengineering/tern.gee.svg)](https://starchart.cc/insightsengineering/tern.gee) From 51d6dd88771349fe861d232e1c76ce697ed2af90 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 9 Nov 2022 11:47:31 -0500 Subject: [PATCH 27/45] Update project status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1bd6ef4..97f683d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # tern.gee -[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) +[![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip) ## Overview From fba5cd1afc6b1a7a11e38ad4d9a975c4d8605646 Mon Sep 17 00:00:00 2001 From: shajoezhu Date: Fri, 11 Nov 2022 12:16:38 +0000 Subject: [PATCH 28/45] close #22 --- NAMESPACE | 1 + R/fit_gee.R | 1 + 2 files changed, 2 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index f02cbac..a093f1a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,6 +10,7 @@ export(fit_gee) export(lsmeans) export(s_lsmeans_logistic) export(summarize_gee_logistic) +export(vars_gee) import(checkmate) import(rtables) importFrom(gee,gee) diff --git a/R/fit_gee.R b/R/fit_gee.R index aa15535..f0bfb8a 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -1,3 +1,4 @@ +#' @export vars_gee <- function(response = "AVAL", covariates = c(), id = "USUBJID", From afffbe770f97bc6a6451667adf9a97c49ec78f6c Mon Sep 17 00:00:00 2001 From: shajoezhu Date: Fri, 11 Nov 2022 13:08:50 +0000 Subject: [PATCH 29/45] include tmc downstream --- staged_dependencies.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index fb79fca..7d88465 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -14,3 +14,6 @@ upstream_repos: repo: insightsengineering/tern host: https://github.com downstream_repos: + insightsengineering/teal.modules.clinical: + repo: insightsengineering/teal.modules.clinical + host: https://github.com From 774e97f80f6873c136425a890d7b5f36196e9c2f Mon Sep 17 00:00:00 2001 From: Emily de la Rua <59304861+edelarua@users.noreply.github.com> Date: Tue, 15 Nov 2022 02:45:10 -0500 Subject: [PATCH 30/45] 21 Update README with example (#23) Closes #21 --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 97f683d..3fd3868 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,15 @@ A stable release of all `NEST` packages is also available [here](https://github. You can get started by trying out the example: ```r +library(dplyr) library(tern.gee) fev_data$FEV1_BINARY <- as.integer(fev_data$FEV1 > 30) +fev_counts <- fev_data %>% + select(USUBJID, ARMCD) %>% + unique() -fit <- fit_gee( +gee_fit <- fit_gee( vars = list( response = "FEV1_BINARY", covariates = c("RACE", "SEX"), @@ -58,6 +62,16 @@ fit <- fit_gee( ), data = fev_data ) + +lsmeans_df <- lsmeans(gee_fit, data = fev_data) + +basic_table() %>% + split_cols_by("ARMCD") %>% + add_colcounts() %>% + summarize_gee_logistic( + .in_ref_col = FALSE + ) %>% + build_table(lsmeans_df, alt_counts_df = fev_counts) ``` This specifies a GEE with the `FEV1_BINARY` outcome and the `RACE` and `SEX` covariates for From 3db2fa9b83e667b2d692f6260220c1e33031e7fd Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 07:43:49 -0500 Subject: [PATCH 31/45] Add Joe and Emily as authors --- DESCRIPTION | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 0291b09..4ff7f33 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,6 +6,8 @@ Version: 0.0.0.9002 Date: 2022-10-25 Authors@R: c( person("Daniel", "Sabanés Bové", , "daniel.sabanes_bove@roche.com", role = c("aut", "cre")), + person("Joe", "Zhu", , "joe.zhu@roche.com", role = c("aut")), + person("Emily", "de la Rua", , "emily.de_la_rua@contractors.roche.com", role = "aut"), person("F. Hoffmann-La Roche AG", role = c("cph", "fnd")) ) Description: Create tables and graphs for GEE model fits. From f8ec9953c6252b27d385e8568ca4452a38e206fa Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 12:58:53 +0000 Subject: [PATCH 32/45] [skip actions] Restyle files --- R/tabulate_gee.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/tabulate_gee.R b/R/tabulate_gee.R index c62ff9b..68b780e 100644 --- a/R/tabulate_gee.R +++ b/R/tabulate_gee.R @@ -73,9 +73,9 @@ h_gee_cov <- function(x, format = "xx.xxxx") { #' #' lsmeans_df <- lsmeans(fit_gee(vars = vars_gee(arm = "ARMCD"), data = df)) #' -#' s_lsmeans_logistic(lsmeans_df[1,], .in_ref_col = TRUE) +#' s_lsmeans_logistic(lsmeans_df[1, ], .in_ref_col = TRUE) #' -#' s_lsmeans_logistic(lsmeans_df[2,], .in_ref_col = FALSE) +#' s_lsmeans_logistic(lsmeans_df[2, ], .in_ref_col = FALSE) s_lsmeans_logistic <- function(df, .in_ref_col) { if_not_ref <- function(x) `if`(.in_ref_col, character(), x) list( From a705d70df07fc05359eb5fe4e58a2dabf8c771aa Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 08:03:53 -0500 Subject: [PATCH 33/45] Fix spelling, fix lint --- .lintr | 5 +++-- README.md | 2 +- _pkgdown.yml | 4 +++- inst/WORDLIST | 13 ++++++++++++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.lintr b/.lintr index c2b2094..df85707 100644 --- a/.lintr +++ b/.lintr @@ -1,5 +1,6 @@ -linters: with_defaults( +linters: linters_with_defaults( line_length_linter = line_length_linter(120), cyclocomp_linter = NULL, - object_usage_linter = NULL + object_usage_linter = NULL, + commented_code_linter = NULL ) diff --git a/README.md b/README.md index 39f43b2..a152359 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ basic_table() %>% This specifies a GEE with the `FEV1_BINARY` outcome and the `RACE` and `SEX` covariates for subjects identified by `USUBJID` and treatment arm `ARMCD` observed over time points identified -by `AVISIT` in the `fev_data` data set. By deafult, logistic regression is used and an unstructured +by `AVISIT` in the `fev_data` data set. By default, logistic regression is used and an unstructured covariance matrix is assumed. The least square means assume equal weights for factor combinations. ## Stargazers diff --git a/_pkgdown.yml b/_pkgdown.yml index b738c73..731fb60 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,3 +1,4 @@ +--- url: https://insightsengineering.github.io/tern.gee template: @@ -18,7 +19,8 @@ reference: - gee_methods - title: Model Specific Functions - desc: These functions help with fitting or extracting results from specific models. + desc: These functions help with fitting or extracting results from + specific models. contents: - as.rtable.tern_gee - fit_gee diff --git a/inst/WORDLIST b/inst/WORDLIST index 4408fdb..6d5af51 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1 +1,12 @@ -TLGs +Bové +COPD +FEV +Forkers +Github +Hoffmann +README +Sabanés +WIP +funder +pkgdown +repo From ee445b523d273810ab36a6cb5d9fb5580fae9121 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 08:11:16 -0500 Subject: [PATCH 34/45] Fix lint --- R/gee_methods.R | 4 ++-- README.md | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/R/gee_methods.R b/R/gee_methods.R index 132c2a5..8ccd69d 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -7,7 +7,7 @@ NULL #' @rdname gee_methods #' @importFrom nlme VarCorr #' @exportS3Method -VarCorr.tern_gee <- function(x, sigma = 1, ...) { +VarCorr.tern_gee <- function(x, sigma = 1, ...) { # nolint dim_mat <- length(x$visit_levels) tmp <- id_mat <- diag(dim_mat) corest <- x$geese$alpha @@ -40,6 +40,6 @@ VarCorr.tern_gee <- function(x, sigma = 1, ...) { #' @rdname gee_methods #' @importFrom geepack QIC #' @exportS3Method -QIC.tern_gee <- function(object, ...) { +QIC.tern_gee <- function(object, ...) { # nolint object$qic } diff --git a/README.md b/README.md index a152359..cf58c38 100644 --- a/README.md +++ b/README.md @@ -77,10 +77,7 @@ basic_table() %>% build_table(lsmeans_df, alt_counts_df = fev_counts) ``` -This specifies a GEE with the `FEV1_BINARY` outcome and the `RACE` and `SEX` covariates for -subjects identified by `USUBJID` and treatment arm `ARMCD` observed over time points identified -by `AVISIT` in the `fev_data` data set. By default, logistic regression is used and an unstructured -covariance matrix is assumed. The least square means assume equal weights for factor combinations. +This specifies a GEE with the `FEV1_BINARY` outcome and the `RACE` and `SEX` covariates for subjects identified by `USUBJID` and treatment arm `ARMCD` observed over time points identified by `AVISIT` in the `fev_data` data set. By default, logistic regression is used and an unstructured covariance matrix is assumed. The least square means assume equal weights for factor combinations. ## Stargazers From 0587a3f0f848363075beb4c4ace5ca5037c1d521 Mon Sep 17 00:00:00 2001 From: "27856297+dependabot-preview[bot]@users.noreply.github.com" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 15 Nov 2022 13:24:55 +0000 Subject: [PATCH 35/45] [skip actions] Roxygen Man Pages Auto Update --- man/tabulate_gee.Rd | 4 ++-- man/tern.gee-package.Rd | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/man/tabulate_gee.Rd b/man/tabulate_gee.Rd index 0c611fd..f000d49 100644 --- a/man/tabulate_gee.Rd +++ b/man/tabulate_gee.Rd @@ -70,9 +70,9 @@ df_counts <- df \%>\% lsmeans_df <- lsmeans(fit_gee(vars = vars_gee(arm = "ARMCD"), data = df)) -s_lsmeans_logistic(lsmeans_df[1,], .in_ref_col = TRUE) +s_lsmeans_logistic(lsmeans_df[1, ], .in_ref_col = TRUE) -s_lsmeans_logistic(lsmeans_df[2,], .in_ref_col = FALSE) +s_lsmeans_logistic(lsmeans_df[2, ], .in_ref_col = FALSE) basic_table() \%>\% split_cols_by("ARMCD") \%>\% add_colcounts() \%>\% diff --git a/man/tern.gee-package.Rd b/man/tern.gee-package.Rd index adf28b9..eaa70f8 100644 --- a/man/tern.gee-package.Rd +++ b/man/tern.gee-package.Rd @@ -19,6 +19,12 @@ Useful links: \author{ \strong{Maintainer}: Daniel Sabanés Bové \email{daniel.sabanes_bove@roche.com} +Authors: +\itemize{ + \item Joe Zhu \email{joe.zhu@roche.com} + \item Emily de la Rua \email{emily.de_la_rua@contractors.roche.com} +} + Other contributors: \itemize{ \item F. Hoffmann-La Roche AG [copyright holder, funder] From cc6e6a47d37852295a1e7c951ecb6b557e9b7de3 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 17:15:34 -0500 Subject: [PATCH 36/45] Add nestcolor dependency, remove unused imports --- NAMESPACE | 1 - R/package.R | 1 - inst/plumber/hello/plumber.R | 4 ---- staged_dependencies.yaml | 3 +++ 4 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 inst/plumber/hello/plumber.R diff --git a/NAMESPACE b/NAMESPACE index a093f1a..a7ee76d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,7 +13,6 @@ export(summarize_gee_logistic) export(vars_gee) import(checkmate) import(rtables) -importFrom(gee,gee) importFrom(geepack,QIC) importFrom(nlme,VarCorr) importFrom(rtables,add_colcounts) diff --git a/R/package.R b/R/package.R index 399bd80..ead2a57 100644 --- a/R/package.R +++ b/R/package.R @@ -6,7 +6,6 @@ #' @import checkmate #' @import rtables -#' @importFrom gee gee #' @importFrom rtables add_colcounts #' @importFrom stats acf #' @importFrom tern f_conf_level diff --git a/inst/plumber/hello/plumber.R b/inst/plumber/hello/plumber.R deleted file mode 100644 index e98ebdb..0000000 --- a/inst/plumber/hello/plumber.R +++ /dev/null @@ -1,4 +0,0 @@ -#* Personal greeting as a Plumber API -#* @param name Your name (character string; e.g. "john doe"). -#* @get /echo -tern.gee::hello diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index 7d88465..ffe5fdc 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -13,6 +13,9 @@ upstream_repos: insightsengineering/tern: repo: insightsengineering/tern host: https://github.com + insightsengineering/nestcolor: + repo: insightsengineering/nestcolor + host: https://github.com downstream_repos: insightsengineering/teal.modules.clinical: repo: insightsengineering/teal.modules.clinical From 52c7ca64bff917722779ce1b22507c82b79ad30e Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 17:18:52 -0500 Subject: [PATCH 37/45] Fix spelling --- inst/WORDLIST | 2 ++ 1 file changed, 2 insertions(+) diff --git a/inst/WORDLIST b/inst/WORDLIST index 6d5af51..af7f2f9 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -5,8 +5,10 @@ Forkers Github Hoffmann README +Rua Sabanés WIP +de funder pkgdown repo From b87db54b9bd7a7ae8ebfb9ff900c5c43817b45e5 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 17:25:10 -0500 Subject: [PATCH 38/45] Update DESCRIPTION --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index 05fa7eb..220f612 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,6 +28,7 @@ Imports: rtables Suggests: knitr, + nestcolor (>= 0.1.0), rmarkdown, testthat (>= 3.1), vdiffr From a6445ce0697e80f94c000e68b4f4da6259024af3 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Tue, 15 Nov 2022 17:55:58 -0500 Subject: [PATCH 39/45] Update description --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 220f612..b0026b7 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -28,7 +28,7 @@ Imports: rtables Suggests: knitr, - nestcolor (>= 0.1.0), + nestcolor, rmarkdown, testthat (>= 3.1), vdiffr From 90aac45879380d988345d0495c410752dabb0bd0 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 16 Nov 2022 08:26:42 -0500 Subject: [PATCH 40/45] Fix warnings/notes, remove nestcolor dependency --- DESCRIPTION | 5 ++--- NAMESPACE | 2 ++ R/fit_gee.R | 20 ++++++++++++++++++ R/gee_methods.R | 2 ++ R/lsmeans.R | 1 + R/package.R | 3 ++- R/tabulate_gee.R | 2 ++ man/gee_methods.Rd | 14 +++++++++++++ man/lsmeans.Rd | 2 ++ man/tabulate_gee.Rd | 6 ++++++ man/vars_gee.Rd | 42 ++++++++++++++++++++++++++++++++++++++ staged_dependencies.yaml | 3 --- vignettes/introduction.Rmd | 13 ++++++++++++ 13 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 man/vars_gee.Rd diff --git a/DESCRIPTION b/DESCRIPTION index b0026b7..506e7f4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,16 +19,15 @@ Depends: tern (>= 0.7.9) Imports: checkmate, + dplyr, geeasy, geepack, nlme, rtables, stats, - formatters, - rtables + formatters Suggests: knitr, - nestcolor, rmarkdown, testthat (>= 3.1), vdiffr diff --git a/NAMESPACE b/NAMESPACE index a7ee76d..01df84a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,9 +13,11 @@ export(summarize_gee_logistic) export(vars_gee) import(checkmate) import(rtables) +importFrom(emmeans,emmeans) importFrom(geepack,QIC) importFrom(nlme,VarCorr) importFrom(rtables,add_colcounts) importFrom(stats,acf) +importFrom(stats,as.formula) importFrom(tern,as.rtable) importFrom(tern,f_conf_level) diff --git a/R/fit_gee.R b/R/fit_gee.R index f0bfb8a..567b819 100644 --- a/R/fit_gee.R +++ b/R/fit_gee.R @@ -1,4 +1,24 @@ +#' Set Variables to Use in GEE Model +#' +#' @param response (`character`)\cr name of response variable. +#' @param covariates (`character`)\cr vector of names of variables to use as covariates. +#' @param id (`character`)\cr name of variable to use to identify unique IDs. +#' @param arm (`character`)\cr name of arm variable. +#' @param visit (`character`)\cr name of visit variable. +#' +#' @return A list of variables that can be used as the `vars` argument in [fit_gee()]. #' @export +#' +#' @examples +#' vars_gee() +#' +#' vars_gee( +#' response = "CHG", +#' covariates = c("SEX", "RACE"), +#' id = "SUBJID", +#' arm = "ARMCD", +#' visit = "AVISITN" +#' ) vars_gee <- function(response = "AVAL", covariates = c(), id = "USUBJID", diff --git a/R/gee_methods.R b/R/gee_methods.R index 8ccd69d..8f9f261 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -6,6 +6,7 @@ NULL #' @rdname gee_methods #' @importFrom nlme VarCorr +#' @inheritParams nlme::VarCorr #' @exportS3Method VarCorr.tern_gee <- function(x, sigma = 1, ...) { # nolint dim_mat <- length(x$visit_levels) @@ -39,6 +40,7 @@ VarCorr.tern_gee <- function(x, sigma = 1, ...) { # nolint #' @rdname gee_methods #' @importFrom geepack QIC +#' @inheritParams geepack::QIC #' @exportS3Method QIC.tern_gee <- function(object, ...) { # nolint object$qic diff --git a/R/lsmeans.R b/R/lsmeans.R index ac74a94..0422efc 100644 --- a/R/lsmeans.R +++ b/R/lsmeans.R @@ -4,6 +4,7 @@ #' @param conf_level (`proportion`)\cr confidence level #' @param weights (`string`)\cr type of weights to be used for the least square means, #' see [emmeans::emmeans()] for details. +#' @param ... additional arguments for methods #' #' @return A `data.frame` with least-square means and contrasts. Additional #' classes allow to dispatch downstream methods correctly, too. diff --git a/R/package.R b/R/package.R index ead2a57..d3f34ea 100644 --- a/R/package.R +++ b/R/package.R @@ -7,6 +7,7 @@ #' @import checkmate #' @import rtables #' @importFrom rtables add_colcounts -#' @importFrom stats acf +#' @importFrom stats acf as.formula #' @importFrom tern f_conf_level +#' @importFrom emmeans emmeans NULL diff --git a/R/tabulate_gee.R b/R/tabulate_gee.R index 68b780e..fab8903 100644 --- a/R/tabulate_gee.R +++ b/R/tabulate_gee.R @@ -11,6 +11,8 @@ tern::as.rtable #' @exportS3Method #' @describeIn tabulate_gee Extracts the coefficient table or covariance matrix estimate from a `tern_gee` object. +#' @inheritParams tern::as.rtable +#' @param type (`character`)\cr type of table to extract from `tern_gee` object. as.rtable.tern_gee <- function(x, # nolint type = c("coef", "cov"), ...) { diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd index 504f811..0cda41f 100644 --- a/man/gee_methods.Rd +++ b/man/gee_methods.Rd @@ -10,6 +10,20 @@ \method{QIC}{tern_gee}(object, ...) } +\arguments{ +\item{x}{a fitted model object, usually an object inheriting from + class \code{"\link[nlme]{lme}"}.} + +\item{sigma}{an optional numeric value used as a multiplier for the + standard deviations. The default is \code{x$sigma} or \code{1} + depending on \code{\link{class}(x)}.} + +\item{...}{further optional arguments passed to other methods (none + for the methods documented here).} + +\item{object}{a fitted GEE model from the geepack +package. Currently only works on geeglm objects.} +} \description{ Additional methods which can simplify working with the GEE result object. } diff --git a/man/lsmeans.Rd b/man/lsmeans.Rd index 42acace..210a3df 100644 --- a/man/lsmeans.Rd +++ b/man/lsmeans.Rd @@ -16,6 +16,8 @@ lsmeans(object, conf_level = 0.95, weights = "proportional", ...) \item{weights}{(\code{string})\cr type of weights to be used for the least square means, see \code{\link[emmeans:emmeans]{emmeans::emmeans()}} for details.} + +\item{...}{additional arguments for methods} } \value{ A \code{data.frame} with least-square means and contrasts. Additional diff --git a/man/tabulate_gee.Rd b/man/tabulate_gee.Rd index f000d49..e1b6725 100644 --- a/man/tabulate_gee.Rd +++ b/man/tabulate_gee.Rd @@ -25,6 +25,12 @@ summarize_gee_logistic( ) } \arguments{ +\item{x}{the object which should be converted to an \code{rtable}.} + +\item{type}{(\code{character})\cr type of table to extract from \code{tern_gee} object.} + +\item{...}{additional arguments for methods.} + \item{df}{(\code{data.frame})\cr data set resulting from \code{\link[=lsmeans]{lsmeans()}}.} \item{.in_ref_col}{(\code{logical})\cr \code{TRUE} when working with the reference level, \code{FALSE} otherwise.} diff --git a/man/vars_gee.Rd b/man/vars_gee.Rd new file mode 100644 index 0000000..14ff783 --- /dev/null +++ b/man/vars_gee.Rd @@ -0,0 +1,42 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/fit_gee.R +\name{vars_gee} +\alias{vars_gee} +\title{Set Variables to Use in GEE Model} +\usage{ +vars_gee( + response = "AVAL", + covariates = c(), + id = "USUBJID", + arm = "ARM", + visit = "AVISIT" +) +} +\arguments{ +\item{response}{(\code{character})\cr name of response variable.} + +\item{covariates}{(\code{character})\cr vector of names of variables to use as covariates.} + +\item{id}{(\code{character})\cr name of variable to use to identify unique IDs.} + +\item{arm}{(\code{character})\cr name of arm variable.} + +\item{visit}{(\code{character})\cr name of visit variable.} +} +\value{ +A list of variables that can be used as the \code{vars} argument in \code{\link[=fit_gee]{fit_gee()}}. +} +\description{ +Set Variables to Use in GEE Model +} +\examples{ +vars_gee() + +vars_gee( + response = "CHG", + covariates = c("SEX", "RACE"), + id = "SUBJID", + arm = "ARMCD", + visit = "AVISITN" +) +} diff --git a/staged_dependencies.yaml b/staged_dependencies.yaml index ffe5fdc..7d88465 100644 --- a/staged_dependencies.yaml +++ b/staged_dependencies.yaml @@ -13,9 +13,6 @@ upstream_repos: insightsengineering/tern: repo: insightsengineering/tern host: https://github.com - insightsengineering/nestcolor: - repo: insightsengineering/nestcolor - host: https://github.com downstream_repos: insightsengineering/teal.modules.clinical: repo: insightsengineering/teal.modules.clinical diff --git a/vignettes/introduction.Rmd b/vignettes/introduction.Rmd index 8ea852a..4f53612 100644 --- a/vignettes/introduction.Rmd +++ b/vignettes/introduction.Rmd @@ -1,6 +1,19 @@ --- title: "Introduction to `tern.gee`" date: "`r Sys.Date()`" +output: + rmarkdown::html_document: + theme: "spacelab" + highlight: "kate" + toc: true + toc_float: true +vignette: > + %\VignetteIndexEntry{Introduction} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +editor_options: + markdown: + wrap: 72 --- Placeholder for introduction vignette. From 4263b9835545b7d3c8b2334ac3bfa73094b20d99 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 16 Nov 2022 11:16:44 -0500 Subject: [PATCH 41/45] Fix error --- DESCRIPTION | 1 + 1 file changed, 1 insertion(+) diff --git a/DESCRIPTION b/DESCRIPTION index 506e7f4..7651c76 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Depends: Imports: checkmate, dplyr, + emmeans, geeasy, geepack, nlme, From c2018e1697283bb533d855d9ad48f8070e8d495a Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 16 Nov 2022 11:24:15 -0500 Subject: [PATCH 42/45] Remove plumber --- .pre-commit-config.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index df8cac2..051bf3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,6 @@ repos: args: [--style_pkg=styler, --style_fun=tidyverse_style] - id: roxygenize additional_dependencies: - - plumber - shiny # codemeta must be above use-tidy-description when both are used # - id: codemeta-description-updated From 6439824da881bea2ac23603b84231495476696f6 Mon Sep 17 00:00:00 2001 From: arkadiuszbeer <86738093+arkadiuszbeer@users.noreply.github.com> Date: Wed, 16 Nov 2022 17:39:16 +0100 Subject: [PATCH 43/45] removing as cran + adding emmeans deps --- .github/workflows/check.yaml | 2 -- DESCRIPTION | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 8a4cdb5..a49aae7 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -24,8 +24,6 @@ jobs: uses: insightsengineering/r.pkg.template/.github/workflows/build-check-install.yaml@main secrets: REPO_GITHUB_TOKEN: ${{ secrets.REPO_GITHUB_TOKEN }} - with: - additional-r-cmd-check-params: --as-cran coverage: name: Coverage 📔 uses: insightsengineering/r.pkg.template/.github/workflows/test-coverage.yaml@main diff --git a/DESCRIPTION b/DESCRIPTION index 506e7f4..c7b9623 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -25,7 +25,8 @@ Imports: nlme, rtables, stats, - formatters + formatters, + emmeans (>= 1.4.5) Suggests: knitr, rmarkdown, From d61ba74ef94eadcd8f6f7961f171e5b7adc7a6bd Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 16 Nov 2022 11:44:41 -0500 Subject: [PATCH 44/45] Update docs --- R/gee_methods.R | 6 ++++++ _pkgdown.yml | 1 + man/gee_methods.Rd | 6 ++---- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/R/gee_methods.R b/R/gee_methods.R index 8f9f261..6fb1b5d 100644 --- a/R/gee_methods.R +++ b/R/gee_methods.R @@ -6,7 +6,10 @@ NULL #' @rdname gee_methods #' @importFrom nlme VarCorr +#' +#' @param x (`tern_gee`)\cr result of [fit_gee()]. #' @inheritParams nlme::VarCorr +#' #' @exportS3Method VarCorr.tern_gee <- function(x, sigma = 1, ...) { # nolint dim_mat <- length(x$visit_levels) @@ -40,7 +43,10 @@ VarCorr.tern_gee <- function(x, sigma = 1, ...) { # nolint #' @rdname gee_methods #' @importFrom geepack QIC +#' +#' @param object (`tern_gee`)\cr result of [fit_gee()]. #' @inheritParams geepack::QIC +#' #' @exportS3Method QIC.tern_gee <- function(object, ...) { # nolint object$qic diff --git a/_pkgdown.yml b/_pkgdown.yml index 731fb60..465b9b9 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -26,6 +26,7 @@ reference: - fit_gee - lsmeans - tabulate_gee + - vars_gee - title: Example Data contents: diff --git a/man/gee_methods.Rd b/man/gee_methods.Rd index 0cda41f..88fb1fb 100644 --- a/man/gee_methods.Rd +++ b/man/gee_methods.Rd @@ -11,8 +11,7 @@ \method{QIC}{tern_gee}(object, ...) } \arguments{ -\item{x}{a fitted model object, usually an object inheriting from - class \code{"\link[nlme]{lme}"}.} +\item{x}{(\code{tern_gee})\cr result of \code{\link[=fit_gee]{fit_gee()}}.} \item{sigma}{an optional numeric value used as a multiplier for the standard deviations. The default is \code{x$sigma} or \code{1} @@ -21,8 +20,7 @@ \item{...}{further optional arguments passed to other methods (none for the methods documented here).} -\item{object}{a fitted GEE model from the geepack -package. Currently only works on geeglm objects.} +\item{object}{(\code{tern_gee})\cr result of \code{\link[=fit_gee]{fit_gee()}}.} } \description{ Additional methods which can simplify working with the GEE result object. From 52dbb2aaadccfec7049f8babe17c787631e0c4b8 Mon Sep 17 00:00:00 2001 From: Emily de la Rua Date: Wed, 16 Nov 2022 11:45:24 -0500 Subject: [PATCH 45/45] Remove duplicate import --- DESCRIPTION | 1 - 1 file changed, 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index c109493..c7b9623 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,7 +20,6 @@ Depends: Imports: checkmate, dplyr, - emmeans, geeasy, geepack, nlme,