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/.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 diff --git a/DESCRIPTION b/DESCRIPTION index 46a6d7c..c7b9623 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,6 +6,8 @@ Version: 0.0.0.9004 Date: 2022-11-09 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. @@ -16,8 +18,15 @@ Depends: R (>= 4.0), tern (>= 0.7.9) Imports: + checkmate, + dplyr, + geeasy, + geepack, + nlme, + rtables, + stats, formatters, - rtables + emmeans (>= 1.4.5) Suggests: knitr, rmarkdown, diff --git a/NAMESPACE b/NAMESPACE index 5402a28..01df84a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,3 +1,23 @@ # Generated by roxygen2: do not edit by hand -export(dummy_function) +S3method(QIC,tern_gee) +S3method(VarCorr,tern_gee) +S3method(as.rtable,tern_gee) +S3method(lsmeans,tern_gee_logistic) +export(a_lsmeans_logistic) +export(as.rtable) +export(fit_gee) +export(lsmeans) +export(s_lsmeans_logistic) +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/NEWS.md b/NEWS.md index 72f5fd0..eba5d7f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,7 @@ # tern.gee 0.0.0.9004 -### New features -* New package +### Miscellaneous + +* Initialize the package. +* Update pkgdown site and README, add unit tests. +* Add examples for `fit_gee`, `lsmeans`, `s_lsmeans_logistic`, `summarize_gee_logistic`. 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..567b819 --- /dev/null +++ b/R/fit_gee.R @@ -0,0 +1,178 @@ +#' 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", + arm = "ARM", + visit = "AVISIT") { + list( + response = response, + covariates = covariates, + id = id, + arm = arm, + visit = visit + ) +} + +#' @keywords internal +build_formula <- function(vars) { + assert_list(vars) + arm_part <- if (is.null(vars$arm)) NULL else vars$arm + rhs_formula <- paste( + c(arm_part, vars$covariates), + 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, + control = geeasy::geelm.control(scale.fix = TRUE) + ) +} + +#' @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" = "m-dependent", + "compound symmetry" = "exchangeable", + "auto-regressive" = "ar1", + stop(paste("correlation structure", cor_str, "not available")) + ) + + result_mv <- switch(cor_str, + "unstructured" = 1, + "toeplitz" = nlevels(data[[vars$visit]]) - 1, + "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. +#' +#' @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 +#' +#' @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"), + cor_struct = c("unstructured", "toeplitz", "compound symmetry", "auto-regressive")) { + formula <- build_formula(vars) + + regression <- match.arg(regression) + family <- build_family(regression) + + data <- order_data(data, vars) + data[[".id"]] <- data[[vars$id]] + data[[".waves"]] <- as.integer(data[[vars$visit]]) + + cor_struct <- match.arg(cor_struct) + cor_details <- build_cor_details(cor_struct, vars, data) + + fit <- geeasy::geelm( + formula = formula, + id = .id, + waves = .waves, + data = data, + family = family$object, + corstr = cor_details$str, + Mv = cor_details$mv, + control = family$control + ) + + fit$qic <- geepack::QIC(fit) + 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( + 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..6fb1b5d --- /dev/null +++ b/R/gee_methods.R @@ -0,0 +1,53 @@ +#' Methods for GEE Models +#' +#' Additional methods which can simplify working with the GEE result object. +#' @name gee_methods +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) + tmp <- id_mat <- diag(dim_mat) + corest <- x$geese$alpha + + # 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 +} + +#' @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/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/R/lsmeans.R b/R/lsmeans.R new file mode 100644 index 0000000..0422efc --- /dev/null +++ b/R/lsmeans.R @@ -0,0 +1,75 @@ +#' 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. +#' @param ... additional arguments for methods +#' +#' @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", + ...) { + 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/package.R b/R/package.R new file mode 100644 index 0000000..d3f34ea --- /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 rtables add_colcounts +#' @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 new file mode 100644 index 0000000..fab8903 --- /dev/null +++ b/R/tabulate_gee.R @@ -0,0 +1,176 @@ +#' Tabulation of a GEE Model +#' +#' Functions 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. +#' @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"), + ...) { + 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) + 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_string, upper_string)], + 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 extracts estimates from a +#' [lsmeans()] data frame based on a logistic GEE model. +#' +#' @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( + 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 +#' +#' @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", + .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/README.md b/README.md index 400acd6..cf58c38 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,90 @@ [![Code Coverage](https://raw.githubusercontent.com/insightsengineering/tern.gee/_xml_coverage_reports/data/main/badge.svg)](https://raw.githubusercontent.com/insightsengineering/tern.gee/_xml_coverage_reports/data/main/coverage.xml) +[![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) -Create TLGs using Generalized Estimating Equations (GEE) +## 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(dplyr) +library(tern.gee) + +fev_data$FEV1_BINARY <- as.integer(fev_data$FEV1 > 30) +fev_counts <- fev_data %>% + select(USUBJID, ARMCD) %>% + unique() + +gee_fit <- fit_gee( + vars = list( + response = "FEV1_BINARY", + covariates = c("RACE", "SEX"), + arm = "ARMCD", + id = "USUBJID", + visit = "AVISIT" + ), + 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 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 + +### 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) diff --git a/_pkgdown.yml b/_pkgdown.yml index ce33f8f..465b9b9 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,3 +1,4 @@ +--- url: https://insightsengineering.github.io/tern.gee template: @@ -7,3 +8,26 @@ navbar: right: - icon: fa-github href: https://github.com/insightsengineering/tern.gee + +reference: + - title: Overview Pages + contents: + - 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. + contents: + - as.rtable.tern_gee + - fit_gee + - lsmeans + - tabulate_gee + - vars_gee + + - title: Example Data + contents: + - fev_data diff --git a/data/fev_data.rda b/data/fev_data.rda new file mode 100644 index 0000000..585676a Binary files /dev/null and b/data/fev_data.rda differ 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/inst/WORDLIST b/inst/WORDLIST index 4408fdb..af7f2f9 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -1 +1,14 @@ -TLGs +Bové +COPD +FEV +Forkers +Github +Hoffmann +README +Rua +Sabanés +WIP +de +funder +pkgdown +repo 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/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/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..1afd664 --- /dev/null +++ b/man/fit_gee.Rd @@ -0,0 +1,47 @@ +% 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") +) +} +\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.} +} +\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. +} +} +\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 new file mode 100644 index 0000000..88fb1fb --- /dev/null +++ b/man/gee_methods.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/gee_methods.R +\name{gee_methods} +\alias{gee_methods} +\alias{VarCorr.tern_gee} +\alias{QIC.tern_gee} +\title{Methods for GEE Models} +\usage{ +\method{VarCorr}{tern_gee}(x, sigma = 1, ...) + +\method{QIC}{tern_gee}(object, ...) +} +\arguments{ +\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} + depending on \code{\link{class}(x)}.} + +\item{...}{further optional arguments passed to other methods (none + for the methods documented here).} + +\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. +} diff --git a/man/lsmeans.Rd b/man/lsmeans.Rd new file mode 100644 index 0000000..210a3df --- /dev/null +++ b/man/lsmeans.Rd @@ -0,0 +1,37 @@ +% 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.} + +\item{...}{additional arguments for methods} +} +\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 +} +\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/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..e1b6725 --- /dev/null +++ b/man/tabulate_gee.Rd @@ -0,0 +1,89 @@ +% 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{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.} + +\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{ +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{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 +\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. + +}} +\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) +} diff --git a/man/tern.gee-package.Rd b/man/tern.gee-package.Rd new file mode 100644 index 0000000..eaa70f8 --- /dev/null +++ b/man/tern.gee-package.Rd @@ -0,0 +1,33 @@ +% 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} + +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] +} + +} 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 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 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.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/helper-setup.R b/tests/testthat/helper-setup.R new file mode 100644 index 0000000..1639fac --- /dev/null +++ b/tests/testthat/helper-setup.R @@ -0,0 +1,10 @@ +# 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/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) -}) diff --git a/tests/testthat/test-fit_gee.R b/tests/testthat/test-fit_gee.R new file mode 100644 index 0000000..970887c --- /dev/null +++ b/tests/testthat/test-fit_gee.R @@ -0,0 +1,83 @@ +# 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("fit_gee works as expected for unstructured correlation structure", { + 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", "geelm")) +}) + +test_that("fit_gee works as expected for AR1 correlation structure", { + result <- expect_silent(fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "auto-regressive" + )) + expect_class(result, c("tern_gee_logistic", "tern_gee", "geelm")) +}) + +test_that("fit_gee works as expected for compound symmetry correlation structure", { + 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", "geelm")) +}) + +test_that("fit_gee works as expected for Toeplitz correlation structure", { + result <- expect_silent(fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "toeplitz" + )) + expect_class(result, c("tern_gee_logistic", "tern_gee", "geelm")) +}) diff --git a/tests/testthat/test-gee_methods.R b/tests/testthat/test-gee_methods.R new file mode 100644 index 0000000..780d6b5 --- /dev/null +++ b/tests/testthat/test-gee_methods.R @@ -0,0 +1,59 @@ +model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" +) + +test_that("vcov works as expected", { + result <- expect_silent(vcov(model)) + expect_matrix(result, nrows = 4, ncols = 4) +}) + +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)) +}) diff --git a/tests/testthat/test-lsmeans.R b/tests/testthat/test-lsmeans.R new file mode 100644 index 0000000..dd8661a --- /dev/null +++ b/tests/testthat/test-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/test-tabulate_gee.R b/tests/testthat/test-tabulate_gee.R new file mode 100644 index 0000000..1996e06 --- /dev/null +++ b/tests/testthat/test-tabulate_gee.R @@ -0,0 +1,128 @@ +dat_adsl <- fev_data %>% + dplyr::select(USUBJID, ARMCD) %>% + dplyr::distinct() + +model <- fit_gee( + vars = fev_vars, + data = fev_data, + regression = "logistic", + cor_struct = "unstructured" +) + +# 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 as expected", { + result <- expect_silent(h_gee_cov(model)) + 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 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() %>% + 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.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) +}) 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..4f53612 --- /dev/null +++ b/vignettes/introduction.Rmd @@ -0,0 +1,23 @@ +--- +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. + +```{r} +library(tern.gee) +```