Shift Tables avec flextable

# remotes::install_github("davidgohel/flextable")
library(flextable)
library(tidyverse)
library(safetyData)

use_df_printer()

set_flextable_defaults(
  theme_fun = theme_booktabs,
  big.mark = " ", 
  font.color = "#666666",
  border.color = "#666666",
  padding = 3,
)

Introduction

Les Shift tables sont des tableaux utilisés dans l’analyse des essais cliniques.

Ils montrent la progression du changement par rapport à une valeur de référence, la progression se faisant souvent dans le temps ; le nombre de sujets est affiché pour chaque niveau (par exemple, faible, normal ou élevé) et une valeur de référence, souvent appelée baseline et ce à chaque visites ou intervalles de temps sélectionnés.

Les deux étapes de la création de ces tableaux sont les suivantes :

  • Réaliser les calculs en utilisant la fonction flextable::shift_table(). Elle calcule les comptages mais aussi les agrège selon différentes dimensions afin d’afficher les sous-totaux.
  • Créez un flextable avec la fonction as_flextable().

Nous avons utilisé l’article de (Luo 2017) pour nous aider à comprendre les tables de décalage.

Données d’exemple

Nous allons illustrer avec un jeu de données nommé sdtm_lb contenant des résultats des tests de laboratoire et disponible dans le package “safetyData”. Il contient :

One record per analyte per planned time point number per time point reference per visit per subject

adlb <- safetyData::sdtm_lb %>% as_tibble() %>% 
  filter(LBTEST %in% c("Albumin", "Alkaline Phosphatase"),
         grepl("(WEEK|SCREENING)", VISIT))
adlb

Génération de la “shift table”

On appelle la fonction shift_table():

SHIFT_TABLE <- shift_table(
  x = adlb, cn_visit = "VISIT",
  cn_grade = "LBNRIND",
  cn_usubjid = "USUBJID",
  cn_lab_cat = "LBTEST",
  cn_is_baseline = "LBBLFL",
  baseline_identifier = "Y",
  grade_levels = c("LOW", "NORMAL", "HIGH"))
SHIFT_TABLE

Le data.frame produit contient des attributs que vous pouvez utiliser pour les post-traitements, c’est-à-dire transformer les grades et les visites comme colonnes de type factor.

SHIFT_TABLE_VISIT <- attr(SHIFT_TABLE, "VISIT_N")

visit_as_factor <- attr(SHIFT_TABLE, "FUN_VISIT")
range_as_factor <- attr(SHIFT_TABLE, "FUN_GRADE")

# post treatments ----
SHIFT_TABLE <- SHIFT_TABLE |> mutate(
  VISIT = visit_as_factor(VISIT),
  BASELINE = range_as_factor(BASELINE),
  LBNRIND = range_as_factor(LBNRIND))

SHIFT_TABLE_VISIT <- SHIFT_TABLE_VISIT |>
  mutate(VISIT = visit_as_factor(VISIT))

SHIFT_TABLE

Afin d’avoir un tableau court lors de l’illustration, nous allons filtrer les données avec seulement quelques visites.

SHIFT_TABLE <- SHIFT_TABLE |> 
  filter(VISIT %in% c("WEEK 4", "WEEK 12", "WEEK 16", "WEEK 26"))

Tabulatation avec “tabulator”

Maintenant que les données sont prêtes, nous devons créer un objet avec la fonction tabulator() qui sera ensuite passé à la fonction as_flextable().

my_format <- function(z) {
  formatC(z * 100, digits = 1, format = "f",
          flag = "0", width = 4)
}

tab <- tabulator(
  x = SHIFT_TABLE,
  hidden_data = SHIFT_TABLE_VISIT,
  row_compose = list(
    VISIT = as_paragraph(VISIT, "\n(N=", N_VISIT, ")")
  ),
  rows = c("LBTEST", "VISIT", "BASELINE"),
  columns = c("LBNRIND"),
  `n` = as_paragraph(N),
  `%` = as_paragraph(as_chunk(PCT, formatter = my_format))
)

Production du flextable

ft <- as_flextable(
  x = tab, separate_with = "VISIT",
  label_rows = c(LBTEST = "Lab Test", VISIT = "Visit",
                 BASELINE = "Reference Range Indicator")) |>
  width(j = 3, width = 0.9)

ft

Automatisation Word

Ce type de tableau est souvent trop grand pour être affiché sur une seule page dans un document Word. Nous allons utiliser une approche programmatique pour générer un document Word contenant un sous-tableau par page avec quelques marqueurs de pagination et titres.

Tout d’abord, chargeons le package ‘officer’ et définissons une fonction de post-traitement qui ajoutera le numéro de page (comme un champ Word) dans la ligne supérieure du tableau.

library(officer)

set_flextable_defaults(
  post_process_docx = function(x) {
    x <- add_header_lines(x, "Page N°") |> 
      append_chunks(i = 1, part = "header", j = 1,
                    as_word_field(x = "Page")) |> 
      align(part = "header", align = "right", i = 1) |>
      hline_top(part = "header", border = fp_border_default(width = 0)) 
    x
  }
)

La fonction qui crée le flextable pour chaque sous-ensemble de données est la suivante :

small_shift_ft <- function(x) {
  tab <- tabulator(
    x = x,
    rows = c("VISIT", "BASELINE"),
    columns = c("LBNRIND"),
    `n` = as_paragraph(N),
    `%` = as_paragraph(as_chunk(PCT, formatter = my_format))
  )
  ft <- as_flextable(
    x = tab, separate_with = "VISIT",
    label_rows = c(VISIT = "Visit", BASELINE = "Reference Range Indicator"))
  ft
}

Ensuite, divisez ou imbriquez les sous tables. Nous allons utiliser tidyr::nest().

Le modèle Word utilisé peut être téléchargé ici : template.docx. Nous y avons ajouté notre logo et les numéros de pages en bas de chaque page.

subdata <- nest(SHIFT_TABLE, data = all_of(c("VISIT", "BASELINE", "LBNRIND", "N", "PCT")))

doc <- read_docx(path = "template.docx") |> 
  body_add_par("Table of content", style = "Title") |>
  body_add_toc()

for (i in seq_len(nrow(subdata))) {
  
  ft <- small_shift_ft(subdata[[i, "data"]])
  doc <- body_add_break(doc) |>
    body_add_par(subdata[[i, "LBTEST"]], style = "heading 1") |>
    body_add_flextable(ft)

}

print(doc, target = "illustration.docx")

Le document Word produit peut être téléchargé ici : illustration.docx. Les miniatures ci-dessous montrent le document attendu.

miniatures du document Word produit

Luo, Haiqiang. 2017. “One Step to Produce Shift Table by Using Proc Report.” PharmaSUG China 2017, 7. https://www.lexjansen.com/pharmasug-cn/2017/CC/PharmaSUG-China-2017-CC04.pdf.