{flowchart}: A Tidy R Package for Data Flowchart Generation

Pau Satorra

Biostatistics Support and Research Unit, IGTP

João Carmezim

Biostatistics Support and Research Unit, IGTP

Natàlia Pallarès

Biostatistics Support and Research Unit, IGTP

Kenneth A. Taylor

Komodo Health, University of South Florida

Cristian Tebé

Biostatistics Support and Research Unit, IGTP

April 24, 2025

Index

1. Introduction

2. The tidyverse

3. The {flowchart} package

4. Hands-on examples

5. Conclusions

Introduction

Flowcharts

  • In any study, a participant flowchart serves as a visual representation of the steps and decisions in the study workflow.

  • Usually different decisions are made from the initial cohort of eligible or screened subjects until a final number of these subjects are considered to be included in the analyses.

  • It is essential that the steps and numbers are clearly defined and that the process is transparent in order to ensure the reproducibility of the study and the quality of the reporting.

Flowcharts

  • Participant flowcharts evolved from the broader concept of flowcharts introduced by industrial engineers.

  • Frank and Lillian Gilbreth introduced the idea of flowcharts in 1921 as “Flow Process Charts” to the American Society of Mechanical Engineers:

Flowcharts

Flowcharts in clinical research

  • In clinical research, the CONSORT, STROBE and ICH guidelines strongly recommend the use of flowcharts.

  • The CONSORT guideline provides a template for the elaboration of a flowchart in the context of a randomized trial of two groups:

Flowcharts in clinical research

Flowchart creation

  • The creation of these flowcharts is a joint task between the data management team and the statisticians.

  • It is time-consuming and labor-intensive, as every screened or recruited subject must be included, without exception.

  • Usually this process must be repeated many times until the database is closed for analysis.

flowchart packages

  • There are several R packages dedicated to building flowcharts: {Gmisc}, {DiagrammeR}, {consort}, {ggflowchart}.

  • Complex programming and manual parameterization are often involved.

  • Some are designed for building other kind of diagrams.

  • They are not adapted to the tidyverse.

{Gmisc} package

library(Gmisc, quietly = TRUE)
library(glue)
library(htmlTable)
library(grid)
library(magrittr)

org_cohort <- boxGrob(glue("Stockholm population",
                           "n = {pop}",
                           pop = txtInt(1632798),
                           .sep = "\n"))
eligible <- boxGrob(glue("Eligible",
                          "n = {pop}",
                           pop = txtInt(10032),
                           .sep = "\n"))
included <- boxGrob(glue("Randomized",
                         "n = {incl}",
                         incl = txtInt(122),
                         .sep = "\n"))
grp_a <- boxGrob(glue("Treatment A",
                      "n = {recr}",
                      recr = txtInt(43),
                      .sep = "\n"))

grp_b <- boxGrob(glue("Treatment B",
                      "n = {recr}",
                      recr = txtInt(122 - 43 - 30),
                      .sep = "\n"))

excluded <- boxGrob(glue("Excluded (n = {tot}):",
                         " - not interested: {uninterested}",
                         " - contra-indicated: {contra}",
                         tot = 30,
                         uninterested = 12,
                         contra = 30 - 12,
                         .sep = "\n"),
                    just = "left")

grid.newpage()
vert <- spreadVertical(org_cohort,
                       eligible = eligible,
                       included = included,
                       grps = grp_a)
grps <- alignVertical(reference = vert$grps,
                      grp_a, grp_b) %>%
  spreadHorizontal()
vert$grps <- NULL

excluded <- moveBox(excluded,
                    x = .8,
                    y = coords(vert$included)$top + distance(vert$eligible, vert$included, half = TRUE, center = FALSE))

for (i in 1:(length(vert) - 1)) {
  connectGrob(vert[[i]], vert[[i + 1]], type = "vert") %>%
    print
}
connectGrob(vert$included, grps[[1]], type = "N")
connectGrob(vert$included, grps[[2]], type = "N")

connectGrob(vert$eligible, excluded, type = "L")

# Print boxes
vert
grps
excluded

{Gmisc} package

The tidyverse

The tidyverse

A set of R packages ideal for data management. They will make your life a lot easier.

The tidyverse

  • The philosophy of tidyverse is to concatenate basic functions applied to a tibble (dataframe) to accomplish complex manipulations integrated into a tidy workflow.

  • The tidyverse workflow is based on the usage of the pipe operator, which can be the native pipe (|>) or the magrittr pipe (%>%).

Pipe operator

  • The pipe operator allows to concatenate multiple functions applied to the same object:
#Round π to 6 decimals
round(pi, 6)
[1] 3.141593

Pipe operator

  • The pipe operator allows to concatenate multiple functions applied to the same object:
#Equivalent using pipes
pi |> round(6)
[1] 3.141593

Pipe operator

  • The pipe operator allows to concatenate multiple functions applied to the same object:
#Exponential of the square root of π and then round to 6 decimals
round(exp(sqrt(pi)), 6)
[1] 5.885277

Pipe operator

  • The pipe operator allows to concatenate multiple functions applied to the same object:
#Equivalent using pipes
pi |> 
  sqrt() |> 
  exp() |> 
  round(6)
[1] 5.885277

The tidyverse

  • This is an example of what a tidyverse workflow looks like compared to base R:
filter_iris <- subset(iris, Species == "setosa")

sel_filter_iris <- filter_iris[, c("Sepal.Length", "Sepal.Width")]

sel_filter_iris$Sepal.Size <- ifelse(sel_filter_iris$Sepal.Length > mean(sel_filter_iris$Sepal.Length) & sel_filter_iris$Sepal.Width > mean(sel_filter_iris$Sepal.Width), 2, 1)

sel_filter_iris$Sepal.Size <- factor(sel_filter_iris$Sepal.Size, levels = 1:2, labels = c("Small", "Big"))

small_iris <- sel_filter_iris[sel_filter_iris$Sepal.Size == "Small",]

small_sepal_area <- mean(small_iris[,"Sepal.Length"] * small_iris[,"Sepal.Width"])

big_iris <- sel_filter_iris[sel_filter_iris$Sepal.Size == "Big",]

big_sepal_area <- mean(big_iris[,"Sepal.Length"] * big_iris[,"Sepal.Width"])

data.frame(
  "Sepal.Size" = c("Small", "Big"),
  "Sepal.Area" = c(small_sepal_area, big_sepal_area)
)
  Sepal.Size Sepal.Area
1      Small   15.65636
2        Big   20.36647

The tidyverse

  • This is an example of what a tidyverse workflow looks like compared to base R:
library(dplyr)

iris |> 
  filter(Species == "setosa") |> 
  select(Sepal.Length, Sepal.Width) |> 
  mutate(
    Sepal.Size = case_when(
      Sepal.Length > mean(Sepal.Length) & Sepal.Width > mean(Sepal.Width) ~ 2,
      .default = 1
    ),
    Sepal.Size = factor(Sepal.Size, levels = 1:2, labels = c("Small", "Big")) 
  ) |> 
  group_by(Sepal.Size) |> 
  summarise(
    Sepal.Area = mean(Sepal.Length*Sepal.Width)
  )
# A tibble: 2 × 2
  Sepal.Size Sepal.Area
  <fct>           <dbl>
1 Small            15.7
2 Big              20.4

The {flowchart} package

{flowchart}

  • It adapts the {Gmisc} package to the tidyverse and makes it more automatizable.

  • From the study database we have to define a set of simple operations combined with the pipe operator (|> or %>%).

  • It automatically adapts to the study database allowing reproducibility.

  • These functions are highly customizable allowing manual parameters to be entered if necessary.

{flowchart}

Alexander Calder (1898-1976)

{flowchart}

  • Published in CRAN since Feburary 2024. Current version: 0.8.0.

flowchart CRAN page

Overview of the package


  • Create a flowchart:

    • as_fc()

    • fc_draw()

    • fc_split()

    • fc_filter()

  • Combine flowcharts:

    • fc_merge()

    • fc_stack()

  • Customize flowcharts:

    • fc_modify()
  • Export flowcharts:

    • fc_export()

safo dataset

  • It’s the built-in dataset of the package.

  • Randomly generated dataset from the SAFO clinical trial1.

ID did not meet inclusion criteria met exclusion criteria declined to participate treatment received intention to treat (ITT) per protocol (PP)
1 Yes No NA NA NA NA
2 No No Yes NA NA NA
3 No No No cloxacillin plus fosfomycin Yes Yes
4 No Yes NA NA NA NA
5 No No No cloxacillin plus fosfomycin Yes Yes
6 No Yes NA NA NA NA

How to create a flowchart

as_fc()

  • Allows to initialize a dataset in the class fc created for this package.

  • Creates a flowchart with an initial box showing the total number of rows of the dataset.

library(flowchart)

safo_fc <- safo |> 
  as_fc()

as_fc()

  • Allows to initialize a dataset in the class fc created for this package.

  • Creates a flowchart with an initial box showing the total number of rows of the dataset.

library(flowchart)

safo_fc <- safo |> 
  as_fc()

str(safo_fc, max.level = 1)
List of 2
 $ data: tibble [925 × 21] (S3: tbl_df/tbl/data.frame)
 $ fc  : tibble [1 × 19] (S3: tbl_df/tbl/data.frame)
 - attr(*, "class")= chr "fc"

as_fc()

safo_fc$fc

id x y n N perc text type group just text_color text_fs text_fface text_ffamily text_padding bg_fill border_color width height
1 0.5 0.5 925 925 100 Initial dataframe 925 init NA center black 8 1 NA 1 white black NA NA

fc_draw()

  • Allows to draw a previously created fc object:
safo |> 
  as_fc()

fc_draw()

  • Allows to draw a previously created fc object:
safo |> 
  as_fc() |> 
  fc_draw()

fc_draw()

  • Allows to draw a previously created fc object:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |> 
  fc_draw()
  • We can use the label argument to modify the box label.

fc_filter()

  • We can filter an existing flowchart specifying the logic in which the filter is to be applied:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |> 
  fc_draw()

fc_filter()

  • We can filter an existing flowchart specifying the logic in which the filter is to be applied:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group)) |> 
  fc_draw()

fc_filter()

  • We can filter an existing flowchart specifying the logic in which the filter is to be applied:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized") |> 
  fc_draw()

  • We can change again the label.

fc_filter()

  • We can filter an existing flowchart specifying the logic in which the filter is to be applied:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw()

  • We can change again the label.

  • We can use show_exc=TRUE to show the excluded rows.

fc_split()

  • We can split an existing flowchart according to the different values of a column:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw()

fc_split()

  • We can split an existing flowchart according to the different values of a column:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_split(group) |> 
  fc_draw()

Customize a flowchart

Modify function arguments

  • Some arguments common to as_fc(), fc_filter() and fc_split(), to customise the appearance of the boxes created at each step:
label= modify the label.
text_pattern= modify the pattern of the text (e.g. {label}\n {n} ({perc}%).
round_digits= number of digits to round percentages (default is 2)
just= modify the justification for the text.
text_color= modify the color of the text.
text_fs= modify the font size of the text.
text_fface= modify the font face of the text.
text_ffamily= modify the font family of the text.
text_padding= modify the text padding inside the box.
bg_fill= modify the background color of the box.
border_color= modify the border color of the box.

Modify function arguments

  • There are a number of other arguments that you can check in the vignette:

fc_modify()

  • We can modify the parameters of the created flowchart using the fc_modify() function:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw()

fc_modify()

  • We can modify the parameters of the created flowchart using the fc_modify() function:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_view("fc")
id x y n N perc text type group just text_color text_fs text_fface text_ffamily text_padding bg_fill border_color width height
1 0.50 0.6666667 925 925 100 Patients assessed for eligibility 925 init NA center black 8 1 NA 1 white black NA NA
2 0.50 0.3333333 215 925 23.24 Randomized 215 (23.24%) filter NA center black 8 1 NA 1 white black NA NA
3 0.65 0.5000000 710 925 76.76 Excluded 710 (76.76%) exclude NA center black 6 1 NA 1 white black NA NA

fc_modify()

  • We can modify the parameters of the created flowchart using the fc_modify() function:
safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_modify(
    ~ . |> 
      mutate(
        text = ifelse(id == 3, str_glue("- {sum(safo$inclusion_crit == 'Yes')} not met the inclusion criteria\n- {sum(safo$exclusion_crit == 'Yes')} met the exclusion criteria"), text),
        x = ifelse(id == 3, 0.75, x)
      )
  ) |> 
  fc_draw()

fc_modify()

Combine flowcharts

fc_merge()

  • We can combine different flowcharts horizontally:
fc1 <- safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(itt == "Yes", label = "Intention to treat (ITT)")

fc_draw(fc1)

fc2 <- safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(pp == "Yes", label = "Per protocol (PP)")

fc_draw(fc2)

fc_merge()

  • We can combine different flowcharts horizontally:
list(fc1, fc2) |>
  fc_merge()

fc_merge()

  • We can combine different flowcharts horizontally:
list(fc1, fc2) |>
  fc_merge() |>
  fc_draw()

Export flowcharts

fc_export()

  • We can export the drawn flowchart to some of the most popular graphic devices.

  • These include both bitmap (png, jpeg, tiff, bmp) and vector (svg, pdf) formats.

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw() 

fc_export()

  • We can export the drawn flowchart to some of the most popular graphic devices.

  • These include both bitmap (png, jpeg, tiff, bmp) and vector (svg, pdf) formats.

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw() |> 
  fc_export("flowchart.png")

fc_export()

  • We can export the drawn flowchart to some of the most popular graphic devices.

  • These include both bitmap (png, jpeg, tiff, bmp) and vector (svg, pdf) formats.

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw() |> 
  fc_export("flowchart.png", width = 3000, height = 4000, res = 700)
  • We can customize the size and resolution of the image to save.

Hands-on examples

Example 1

  • We will try to build a flowchart for the complete participant flow of the SAFO study trial:

Example 1

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_draw()

Example 1

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_draw()

Example 1

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_split(group) |> 
  fc_draw()

Example 1

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_split(group) |> 
  fc_filter(itt == "Yes", label = "Included in ITT") |> 
  fc_draw()

Example 1

safo |> 
  as_fc(label = "Patients assessed for eligibility") |>
  fc_filter(!is.na(group), label = "Randomized", show_exc = TRUE) |> 
  fc_split(group) |> 
  fc_filter(itt == "Yes", label = "Included in ITT") |> 
  fc_filter(pp == "Yes", label = "Included in PP") |> 
  fc_draw()

Example 2

  • Now let’s try to customize it in order to exactly reproduce the original flowchart of the SAFO study published in Nature Medicine:

Example 2

  • First, we have to build the text in the exclude boxes:
#For the first exclude box:
label_exc <- paste(
  c(str_glue("{sum(safo$inclusion_crit == 'Yes' | safo$exclusion_crit == 'Yes' | safo$decline_part == 'Yes', na.rm = T)} excluded:"),
    map_chr(c("inclusion_crit", "decline_part", "exclusion_crit"), ~str_glue("{sum(safo[[.x]] == 'Yes', na.rm = TRUE)} {attr(safo[[.x]], 'label')}")),
    map_chr(4:15, ~str_glue(" -  {sum(safo[[.x]] == 'Yes')} {attr(safo[[.x]], 'label')}"))),
  collapse = "\n")

label_exc <- gsub("exclusion criteria", "exclusion criteria:", label_exc)
  
cat(label_exc)

Example 2

710 excluded:
136 did not meet inclusion criteria
134 declined to participate
440 met exclusion criteria:
 -  133 chronic heart failure
 -  70 clinical status with expected death in <24h
 -  68 polymicrobial bacteremia
 -  56 conditions expected to affect adhrence to the protocol
 -  53 suspicion of prosthetic valve endocarditis
 -  33 severe liver cirrhosis
 -  27 acute SARS-CoV-2 infection
 -  28 beta-lactam or fosfomycin hypersensitivity
 -  10 participation in another clinical trial
 -  5 pregnancy or breastfeeding
 -  4 previous participation in the SAFO trial
 -  3 myasthenia gravis

Example 2

  • First, we have to build the text in the exclude boxes:
#For the PP exclude box (cloxacillin plus fosfomycin):
safo1 <- safo |> 
    filter(group == "cloxacillin plus fosfomycin", !is.na(reason_pp)) |> 
    mutate(reason_pp = droplevels(reason_pp))

label_exc1 <- paste(
    c(str_glue("{nrow(safo1)} excluded:"),
      map_chr(levels(safo1$reason_pp), ~str_glue(" -  {sum(safo1$reason_pp == .x)} {.x}"))),
    collapse = "\n")

label_exc1 <- str_replace_all(label_exc1, c("nosocomial" = "nosocomial\n", "treatment" = "treatment\n"))

cat(label_exc1)

Example 2

3 excluded:
 -  2 fosfomycin-resistant strain in index blood cultures
 -  1 protocol violation

Example 2

  • First, we have to build the text in the exclude boxes:
safo2 <- safo |> 
  filter(group == "cloxacillin alone", !is.na(reason_pp)) |> 
  mutate(reason_pp = droplevels(reason_pp))

label_exc2 <- paste(
  c(str_glue("{nrow(safo2)} excluded:"),
    map_chr(levels(safo2$reason_pp), ~str_glue(" -  {sum(safo2$reason_pp == .x)} {.x}"))),
  collapse = "\n")

label_exc2 <- str_replace_all(label_exc2, c("resistant" = "resistant\n", "blood" = "blood\n"))

cat(label_exc2)

Example 2

4 excluded:
 -  1 nosocomial SARS-CoV-2 infection
 -  1 treatment discontinuation (phlebitis)
 -  1 withdrew consent
 -  1 protocol violation

Example 2

safo |> 
  as_fc(label = "patients assessed for eligibility", text_pattern = "{N} {label}") |>
  fc_draw()

Example 2

Example 2

safo |> 
  as_fc(label = "patients assessed for eligibility", text_pattern = "{N} {label}") |>
  fc_filter(!is.na(group), label = "randomized", text_pattern = "{n} {label}", show_exc = TRUE, just_exc = "left", text_pattern_exc = "{label}", label_exc = label_exc, text_fs_exc = 7, offset_exc = 0.15) |> 
  fc_draw()

Example 2

Example 2

safo |> 
  as_fc(label = "patients assessed for eligibility", text_pattern = "{N} {label}") |>
  fc_filter(!is.na(group), label = "randomized", text_pattern = "{n} {label}", show_exc = TRUE, just_exc = "left", text_pattern_exc = "{label}", label_exc = label_exc, text_fs_exc = 7, offset_exc = 0.15) |> 
  fc_split(group, text_pattern = "{n} assigned\n {label}") |> 
  fc_draw()

Example 2

Example 2

safo |> 
  as_fc(label = "patients assessed for eligibility", text_pattern = "{N} {label}") |>
  fc_filter(!is.na(group), label = "randomized", text_pattern = "{n} {label}", show_exc = TRUE, just_exc = "left", text_pattern_exc = "{label}", label_exc = label_exc, text_fs_exc = 7, offset_exc = 0.15) |> 
  fc_split(group, text_pattern = "{n} assigned\n {label}") |> 
  fc_filter(itt == "Yes", label = "included in intention-to-treat\n population", show_exc = TRUE, text_pattern = "{n} {label}", label_exc = "patient did not receive allocated\n treatment (withdrew consent)", text_pattern_exc = "{n} {label}", text_fs_exc = 7) |> 
  fc_draw()

Example 2

Example 2

safo |> 
  as_fc(label = "patients assessed for eligibility", text_pattern = "{N} {label}") |>
  fc_filter(!is.na(group), label = "randomized", text_pattern = "{n} {label}", show_exc = TRUE, just_exc = "left", text_pattern_exc = "{label}", label_exc = label_exc, text_fs_exc = 7, offset_exc = 0.15) |> 
  fc_split(group, text_pattern = "{n} assigned\n {label}") |> 
  fc_filter(itt == "Yes", label = "included in intention-to-treat\n population", show_exc = TRUE, text_pattern = "{n} {label}", label_exc = "patient did not receive allocated\n treatment (withdrew consent)", text_pattern_exc = "{n} {label}", text_fs_exc = 7) |> 
  fc_filter(pp == "Yes", label = "included in per-protocol\n population", show_exc = TRUE, just_exc = "left", text_pattern = "{n} {label}", text_fs_exc = 7) |> 
  fc_draw()

Example 2

Example 2

  • Now let’s use fc_modify() to change the existing texts for the ones we created:
safo |> 
  as_fc(label = "patients assessed for eligibility", text_pattern = "{N} {label}") |> 
  fc_filter(!is.na(group), label = "randomized", text_pattern = "{n} {label}", show_exc = TRUE, just_exc = "left", text_pattern_exc = "{label}", label_exc = label_exc, text_fs_exc = 7, offset_exc = 0.15) |>
  fc_split(group, text_pattern = "{n} asssigned\n {label}") |> 
  fc_filter(itt == "Yes", label = "included in intention-to-treat\n population", show_exc = TRUE, text_pattern = "{n} {label}", label_exc = "patient did not receive allocated\n treatment (withdrew consent)", text_pattern_exc = "{n} {label}", text_fs_exc = 7) |>
  fc_filter(pp == "Yes", label = "included in per-protocol\n population", show_exc = TRUE, just_exc = "left", text_pattern = "{n} {label}", text_fs_exc = 7, offset_exc = c(0, -0.01)) |> 
  fc_modify(
    ~.x |> 
      filter(n != 0) |> 
      mutate(
        text = case_when(id == 11 ~ label_exc1, id == 13 ~ label_exc2, TRUE ~ text)
      )
  ) |> 
  fc_draw()

Example 2

Example 3

  • Now, we will create a flowchart without any dataset using the N= argument:

Example 3

as_fc(N = 300, label = "Available patients") |> 
  fc_draw()

Example 3

as_fc(N = 300, label = "Available patients") |>
  fc_filter(N = 240, label = "Randomized patients", show_exc = TRUE) |> 
  fc_draw()

Example 3

as_fc(N = 300, label = "Available patients") |>
  fc_filter(N = 240, label = "Randomized patients", show_exc = TRUE) |> 
  fc_split(N = c(100, 80, 60), label = c("Group A", "Group B", "Group C")) |>
  fc_draw()

Example 3

as_fc(N = 300, label = "Available patients") |>
  fc_filter(N = 240, label = "Randomized patients", show_exc = TRUE) |> 
  fc_split(N = c(100, 80, 60), label = c("Group A", "Group B", "Group C")) |>
  fc_filter(N = c(80, 75, 50), label = "Finished the study") |> 
  fc_draw()

Conclusions

Conclusions

  • A clear and detailed reporting of the flow of participants in health research studies is required and recommended.

  • With this package, flowchart programming in R is made easier and accessible within the tidyverse workflow.

  • Flowchart reproducibility is assured.

  • As a limitation, we have not considered all possible scenarios and study designs, although is highly customizable.

  • As future developments:

    • Define style themes using a function fc_theme().

    • Shiny application.

Conclusions


“Readers should not have to infer what was probably done; they should be told explicitly.”

Douglas G. Altman

More information

Contact

IGTP Biostatistics Support and Research Unit:

Pau Satorra

João Carmezim

Natàlia Pallarès

Cristian Tebé

Lucia Blanc


github.com/bruigtp


Thank you!