diff --git a/.nojekyll b/.nojekyll
index f1812ca..a737b10 100644
--- a/.nojekyll
+++ b/.nojekyll
@@ -1 +1 @@
-2da7e1a8
\ No newline at end of file
+a49949c2
\ No newline at end of file
diff --git a/R-for-Clinical-Study-Reports-and-Submission.pdf b/R-for-Clinical-Study-Reports-and-Submission.pdf
index 05ac080..00e3cd8 100644
Binary files a/R-for-Clinical-Study-Reports-and-Submission.pdf and b/R-for-Clinical-Study-Reports-and-Submission.pdf differ
diff --git a/search.json b/search.json
index e992298..7d9f1d9 100644
--- a/search.json
+++ b/search.json
@@ -117,7 +117,7 @@
"href": "tlf-overview.html#tools",
"title": "1 Overview",
"section": "1.3 Tools",
- "text": "1.3 Tools\nTo exemplify the generation of TLFs in RTF format, we rely on the functionality provided by two R packages:\n\ntidyverse: preparation of datasets in a format suitable for reporting purposes. The tidyverse package offers a comprehensive suite of tools and functions for data manipulation and transformation, ensuring that the data is structured appropriately.\nr2rtf: creation RTF files. The r2rtf package offers functions specifically designed for generating RTF files, allowing us to produce TLFs in the desired format.\n\n\n\n\n\n\n\nNote\n\n\n\nThere are indeed several other R packages available that can assist in creating TLFs in ASCII, RTF, and Word formats such as rtables, huxtable, pharmaRTF, gt, officer, and flextable. However, in this particular context, we will concentrate on demonstrating the concept using the r2rtf package. It is highly recommended for readers to explore and experiment with various R packages to identify the most suitable tools that align with their specific needs and objectives.\n\n\n\n1.3.1 tidyverse\nThe tidyverse is a comprehensive collection of R packages that aim to simplify the workflow of manipulating, visualizing, and analyzing data in R. These packages adhere to the principles outlined in the the tidy tools manifesto and offer user-friendly interfaces for interactive data analysis.\nThe creators of the tidyverse, Posit, have provided exceptional cheatsheets and tutorials that serve as valuable resources for learning and mastering the functionalities of these packages.\nFurthermore, there are several books available that serve as introductions to the tidyverse. For example:\n\nThe tidyverse cookbook\nR for Data Science\n\n\n\n\n\n\n\nNote\n\n\n\nIn this book, we assume that the reader already has experience in using the tidyverse. This prior knowledge and familiarity with the tidyverse tools enable a more efficient and focused exploration of the concepts presented throughout the book.\n\n\n\n\n1.3.2 r2rtf\nr2rtf is an R package specifically designed to create production-ready tables and figures in RTF format.\n\nProvide simple “verb” functions that correspond to each component of a table, to help you translate a data frame to a table in an RTF file.\nEnable pipes (|>).\nFocus on the table format only. Data manipulation and analysis tasks can be handled by other R packages like the tidyverse.\n\nBefore generating an RTF table using r2rtf, there are a few steps to follow:\n\nDetermine the desired layout of the table.\nBreak down the layout into smaller tasks, which can be programmed.\nExecute the program to generate the table.\n\nWe provide a brief introduction of r2rtf and show how to transfer data frames into table, listing, and figures (TLFs).\nWe provide a concise introduction to r2rtf and demonstrate how to convert data frames into TLFs. For more comprehensive examples and additional features, we encourage readers to explore the r2rtf package website.\nTo illustrate the basic usage of the r2rtf package, we will work with the “r2rtf_adae” dataset, available within the r2rtf package. This dataset contains information on adverse events (AEs) from a clinical trial, which will serve as a practical example for generating RTF tables using r2rtf.\nTo begin, let’s load the required packages:\n\nlibrary(tidyverse) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this example, we will focus on three variables from the r2rtf_adae dataset:\n\nUSUBJID: unique subject identifier.\nTRTA: actual treatment group.\nAEDECOD: dictionary-derived derm.\n\n\n\n\n\n\n\nNote\n\n\n\nAdditional information about these variables can be found on the help page of the dataset, which can be accessed by using the command ?r2rtf_adae in R.\n\n\n\nr2rtf_adae |>\n select(USUBJID, TRTA, AEDECOD) |>\n head(4)\n#> USUBJID TRTA AEDECOD\n#> 1 01-701-1015 Placebo APPLICATION SITE ERYTHEMA\n#> 2 01-701-1015 Placebo APPLICATION SITE PRURITUS\n#> 3 01-701-1015 Placebo DIARRHOEA\n#> 4 01-701-1023 Placebo ERYTHEMA\n\nTo manipulate the data and create a data frame containing the necessary information for the RTF table, we can use the dplyr and tidyr packages within the tidyverse.\n\ntbl <- r2rtf_adae %>%\n count(TRTA, AEDECOD) %>%\n pivot_wider(names_from = TRTA, values_from = n, values_fill = 0)\n\ntbl %>% head(4)\n#> # A tibble: 4 × 4\n#> AEDECOD Placebo `Xanomeline High Dose` `Xanomeline Low Dose`\n#> <chr> <int> <int> <int>\n#> 1 ABDOMINAL PAIN 1 2 3\n#> 2 AGITATION 2 1 2\n#> 3 ALOPECIA 1 0 0\n#> 4 ANXIETY 2 0 4\n\nHaving prepared the dataset tbl, we can now proceed with constructing the final RTF table using the r2rtf package. The r2rtf package has various functions, each designed for a specific type of table layout. Some commonly used verbs include:\n\nrtf_page(): RTF page information\nrtf_title(): RTF title information\nrtf_colheader(): RTF column header information\nrtf_body(): RTF table body information\nrtf_footnote(): RTF footnote information\nrtf_source(): RTF data source information\n\nFunctions provided by the r2rtf package are designed to work seamlessly with the pipe operator (|>). This allows for a more concise and readable code structure, enhancing the efficiency of table creation in RTF format. A full list of functions in the r2rtf package can be found in the package reference page.\nHere is a minimal example that demonstrates how to combine functions using pipes to create an RTF table.\n\nrtf_body() is used to define table body layout.\nrtf_encode() transfers table layout information into RTF syntax.\nwrite_rtf() save RTF encoding into a file with file extension .rtf.\n\n\nhead(tbl) |>\n rtf_body() |>\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae1.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we may want to add more column space to the first column. We can achieve the goal by updating the col_rel_width argument in the rtf_body() function.\nIn the example below, the col_rel_width argument expects a vector with the same length as the number of columns in the table tbl. This vector defines the relative width of each column within a predetermined total column width. Here, the relative width is defined as 3:2:2:2 that allow us to allocate more space to specific columns.\n\n\n\n\n\n\nNote\n\n\n\nOnly the ratio of the col_rel_width values is considered. Therefore, using col_rel_width = c(6, 4, 4, 4) or col_rel_width = c(1.5, 1, 1, 1) would yield equivalent results, as they maintain the same ratio.\n\n\n\nhead(tbl) |>\n rtf_body(col_rel_width = c(3, 2, 2, 2)) |>\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae2.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we encountered a misalignment issue with the column header. To address this, we can use the rtf_colheader() function to adjust column header width and provide more informative column headers.\nWithin the rtf_colheader() function, the colheader argument is used to specify the content of the column header. The columns are separated using the | symbol. In the following example, we define the column header as \"Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose\", representing the four columns in the table:\n\nhead(tbl) |>\n rtf_colheader(\n colheader = \"Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose\",\n col_rel_width = c(3, 2, 2, 2)\n ) |>\n rtf_body(col_rel_width = c(3, 2, 2, 2)) |>\n rtf_encode() %>%\n write_rtf(\"tlf/intro-ae3.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn rtf_body() and rtf_colheader(), the text_justification argument is used to align text within the generated RTF table. The default value is \"c\", representing center justification. However, you can customize the text justification by column using a character vector with a length equal to the number of displayed columns. Here is a table displaying the possible inputs for the text_justification argument:\n\n\n\n\n\ntype\nname\n\n\n\n\nl\nleft\n\n\nc\ncenter\n\n\nr\nright\n\n\nd\ndecimal\n\n\nj\njustified\n\n\n\n\n\nBelow is an example to make the first column left-aligned and the rest columns center-aligned.\n\nhead(tbl) |>\n rtf_body(text_justification = c(\"l\", \"c\", \"c\", \"c\")) |>\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae5.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe border_left, border_right, border_top, and border_bottom arguments in the rtf_body() and rtf_colheader() functions are used to control the cell borders in the RTF table. If we want to remove the top border of \"Adverse Events\" in the header, we can change the default value \"single\" to \"\" in the border_top argument. Below is an example to demonstrate the possibility of adding multiple column headers with proper border lines.\n\n\n\n\n\n\nNote\n\n\n\nthe r2rtf package supports 26 different border types, each offering unique border styles. For more details and examples regarding these border types, you can refer to the r2rtf package website.\n\n\n\nhead(tbl) %>%\n rtf_colheader(\n colheader = \" | Treatment\",\n col_rel_width = c(3, 6)\n ) |>\n rtf_colheader(\n colheader = \"Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose\",\n border_top = c(\"\", \"single\", \"single\", \"single\"),\n col_rel_width = c(3, 2, 2, 2)\n ) |>\n rtf_body(col_rel_width = c(3, 2, 2, 2)) %>%\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae7.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe r2rtf R package website provides additional examples that demonstrate how to customize various aspects of the generated RTF tables. These examples cover topics such as customizing the title, subtitle, footnote, data source, and handling special characters within the table content.\nIn the upcoming chapters of this book, we will introduce and explore these features as they become relevant to the specific use cases and scenarios discussed. By following along with the chapters, readers will gradually learn how to leverage these features to customize and enhance their RTF tables in real examples.",
+ "text": "1.3 Tools\nTo exemplify the generation of TLFs in RTF format, we rely on the functionality provided by two R packages:\n\ntidyverse: preparation of datasets in a format suitable for reporting purposes. The tidyverse package offers a comprehensive suite of tools and functions for data manipulation and transformation, ensuring that the data is structured appropriately.\nr2rtf: creation RTF files. The r2rtf package offers functions specifically designed for generating RTF files, allowing us to produce TLFs in the desired format.\n\n\n\n\n\n\n\nNote\n\n\n\nThere are indeed several other R packages available that can assist in creating TLFs in ASCII, RTF, and Word formats such as rtables, huxtable, pharmaRTF, gt, officer, and flextable. However, in this particular context, we will concentrate on demonstrating the concept using the r2rtf package. It is highly recommended for readers to explore and experiment with various R packages to identify the most suitable tools that align with their specific needs and objectives.\n\n\n\n1.3.1 tidyverse\nThe tidyverse is a comprehensive collection of R packages that aim to simplify the workflow of manipulating, visualizing, and analyzing data in R. These packages adhere to the principles outlined in the the tidy tools manifesto and offer user-friendly interfaces for interactive data analysis.\nThe creators of the tidyverse, Posit, have provided exceptional cheatsheets and tutorials that serve as valuable resources for learning and mastering the functionalities of these packages.\nFurthermore, there are several books available that serve as introductions to the tidyverse. For example:\n\nThe tidyverse cookbook\nR for Data Science\n\n\n\n\n\n\n\nNote\n\n\n\nIn this book, we assume that the reader already has experience in using the tidyverse. This prior knowledge and familiarity with the tidyverse tools enable a more efficient and focused exploration of the concepts presented throughout the book.\n\n\n\n\n1.3.2 r2rtf\nr2rtf is an R package specifically designed to create production-ready tables and figures in RTF format.\n\nProvide simple “verb” functions that correspond to each component of a table, to help you translate a data frame to a table in an RTF file.\nEnable pipes (|>).\nFocus on the table format only. Data manipulation and analysis tasks can be handled by other R packages like the tidyverse.\n\nBefore generating an RTF table using r2rtf, there are a few steps to follow:\n\nDetermine the desired layout of the table.\nBreak down the layout into smaller tasks, which can be programmed.\nExecute the program to generate the table.\n\nWe provide a brief introduction of r2rtf and show how to transfer data frames into table, listing, and figures (TLFs).\nWe provide a concise introduction to r2rtf and demonstrate how to convert data frames into TLFs. For more comprehensive examples and additional features, we encourage readers to explore the r2rtf package website.\nTo illustrate the basic usage of the r2rtf package, we will work with the “r2rtf_adae” dataset, available within the r2rtf package. This dataset contains information on adverse events (AEs) from a clinical trial, which will serve as a practical example for generating RTF tables using r2rtf.\nTo begin, let’s load the required packages:\n\nlibrary(tidyverse) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this example, we will focus on three variables from the r2rtf_adae dataset:\n\nUSUBJID: unique subject identifier.\nTRTA: actual treatment group.\nAEDECOD: dictionary-derived derm.\n\n\n\n\n\n\n\nNote\n\n\n\nAdditional information about these variables can be found on the help page of the dataset, which can be accessed by using the command ?r2rtf_adae in R.\n\n\n\nr2rtf_adae |> select(USUBJID, TRTA, AEDECOD)\n#> USUBJID TRTA AEDECOD\n#> 1 01-701-1015 Placebo APPLICATION SITE ERYTHEMA\n#> 2 01-701-1015 Placebo APPLICATION SITE PRURITUS\n#> 3 01-701-1015 Placebo DIARRHOEA\n#> 4 01-701-1023 Placebo ERYTHEMA\n#> # ℹ 1187 more rows\n\nTo manipulate the data and create a data frame containing the necessary information for the RTF table, we can use the dplyr and tidyr packages within the tidyverse.\n\ntbl <- r2rtf_adae %>%\n count(TRTA, AEDECOD) %>%\n pivot_wider(names_from = TRTA, values_from = n, values_fill = 0)\n\ntbl\n#> # A tibble: 242 × 4\n#> AEDECOD Placebo `Xanomeline High Dose` `Xanomeline Low Dose`\n#> <chr> <int> <int> <int>\n#> 1 ABDOMINAL PAIN 1 2 3\n#> 2 AGITATION 2 1 2\n#> 3 ALOPECIA 1 0 0\n#> 4 ANXIETY 2 0 4\n#> # ℹ 238 more rows\n\nHaving prepared the dataset tbl, we can now proceed with constructing the final RTF table using the r2rtf package. The r2rtf package has various functions, each designed for a specific type of table layout. Some commonly used verbs include:\n\nrtf_page(): RTF page information\nrtf_title(): RTF title information\nrtf_colheader(): RTF column header information\nrtf_body(): RTF table body information\nrtf_footnote(): RTF footnote information\nrtf_source(): RTF data source information\n\nFunctions provided by the r2rtf package are designed to work seamlessly with the pipe operator (|>). This allows for a more concise and readable code structure, enhancing the efficiency of table creation in RTF format. A full list of functions in the r2rtf package can be found in the package reference page.\nHere is a minimal example that demonstrates how to combine functions using pipes to create an RTF table.\n\nrtf_body() is used to define table body layout.\nrtf_encode() transfers table layout information into RTF syntax.\nwrite_rtf() save RTF encoding into a file with file extension .rtf.\n\n\ntbl |>\n head() |>\n rtf_body() |>\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae1.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we may want to add more column space to the first column. We can achieve the goal by updating the col_rel_width argument in the rtf_body() function.\nIn the example below, the col_rel_width argument expects a vector with the same length as the number of columns in the table tbl. This vector defines the relative width of each column within a predetermined total column width. Here, the relative width is defined as 3:2:2:2 that allow us to allocate more space to specific columns.\n\n\n\n\n\n\nNote\n\n\n\nOnly the ratio of the col_rel_width values is considered. Therefore, using col_rel_width = c(6, 4, 4, 4) or col_rel_width = c(1.5, 1, 1, 1) would yield equivalent results, as they maintain the same ratio.\n\n\n\ntbl |>\n head() |>\n rtf_body(col_rel_width = c(3, 2, 2, 2)) |>\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae2.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn the previous example, we encountered a misalignment issue with the column header. To address this, we can use the rtf_colheader() function to adjust column header width and provide more informative column headers.\nWithin the rtf_colheader() function, the colheader argument is used to specify the content of the column header. The columns are separated using the | symbol. In the following example, we define the column header as \"Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose\", representing the four columns in the table:\n\ntbl |>\n head() |>\n rtf_colheader(\n colheader = \"Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose\",\n col_rel_width = c(3, 2, 2, 2)\n ) |>\n rtf_body(col_rel_width = c(3, 2, 2, 2)) |>\n rtf_encode() %>%\n write_rtf(\"tlf/intro-ae3.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn rtf_body() and rtf_colheader(), the text_justification argument is used to align text within the generated RTF table. The default value is \"c\", representing center justification. However, you can customize the text justification by column using a character vector with a length equal to the number of displayed columns. Here is a table displaying the possible inputs for the text_justification argument:\n\n\n\n\n\ntype\nname\n\n\n\n\nl\nleft\n\n\nc\ncenter\n\n\nr\nright\n\n\nd\ndecimal\n\n\nj\njustified\n\n\n\n\n\nBelow is an example to make the first column left-aligned and the rest columns center-aligned.\n\ntbl |>\n head() |>\n rtf_body(text_justification = c(\"l\", \"c\", \"c\", \"c\")) |>\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae5.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe border_left, border_right, border_top, and border_bottom arguments in the rtf_body() and rtf_colheader() functions are used to control the cell borders in the RTF table. If we want to remove the top border of \"Adverse Events\" in the header, we can change the default value \"single\" to \"\" in the border_top argument. Below is an example to demonstrate the possibility of adding multiple column headers with proper border lines.\n\n\n\n\n\n\nNote\n\n\n\nthe r2rtf package supports 26 different border types, each offering unique border styles. For more details and examples regarding these border types, you can refer to the r2rtf package website.\n\n\n\ntbl |>\n head() |>\n rtf_colheader(\n colheader = \" | Treatment\",\n col_rel_width = c(3, 6)\n ) |>\n rtf_colheader(\n colheader = \"Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose\",\n border_top = c(\"\", \"single\", \"single\", \"single\"),\n col_rel_width = c(3, 2, 2, 2)\n ) |>\n rtf_body(col_rel_width = c(3, 2, 2, 2)) %>%\n rtf_encode() |>\n write_rtf(\"tlf/intro-ae7.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe r2rtf R package website provides additional examples that demonstrate how to customize various aspects of the generated RTF tables. These examples cover topics such as customizing the title, subtitle, footnote, data source, and handling special characters within the table content.\nIn the upcoming chapters of this book, we will introduce and explore these features as they become relevant to the specific use cases and scenarios discussed. By following along with the chapters, readers will gradually learn how to leverage these features to customize and enhance their RTF tables in real examples.",
"crumbs": [
"Delivering TLFs in CSR",
"1 Overview"
@@ -128,7 +128,7 @@
"href": "tlf-disposition.html",
"title": "2 Disposition",
"section": "",
- "text": "Following ICH E3 guidance, a summary table needs to be provided to include all participants who entered the study in Section 10.1, Disposition of Participants.\nThe disposition of participants table reports the numbers of participants who were randomized, and who entered and completed each phase of the study. In addition, the reasons for all post-randomization discontinuations, grouped by treatment and by major reason (lost to follow-up, adverse event, poor compliance, etc.) are reported.\n\nlibrary(haven) # Read SAS data\nlibrary(dplyr) # Manipulate data\nlibrary(tidyr) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this chapter, we show how to create a typical disposition table.\n\n\n\n\n\n\n\n\n\nThe first step is to read in the relevant datasets into R. For a disposition table, all the required information is saved in a Subject-level Analysis Dataset (ADSL). This dataset is provided in sas7bdat format, which is a SAS data format currently used in many clinical trial analysis and reporting. The haven package is able to read the dataset, while maintaining its attributes (e.g., variable labels).\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\n\nThe following variables are used in the preparation of a simplified disposition of participants table:\n\nUSUBJID: unique subject identifier\nTRT01P: planned treatment\nTRT01PN: planned treatment numeric encoding\nDISCONFL: discontinued from study flag\nDCREASCD: discontinued from study reason coded\n\n\nadsl %>%\n select(USUBJID, TRT01P, TRT01PN, DISCONFL, DCREASCD) %>%\n head(4)\n#> # A tibble: 4 × 5\n#> USUBJID TRT01P TRT01PN DISCONFL DCREASCD \n#> <chr> <chr> <dbl> <chr> <chr> \n#> 1 01-701-1015 Placebo 0 \"\" Completed \n#> 2 01-701-1023 Placebo 0 \"Y\" Adverse Event \n#> 3 01-701-1028 Xanomeline High Dose 81 \"\" Completed \n#> 4 01-701-1033 Xanomeline Low Dose 54 \"Y\" Sponsor Decision\n\nIn the code below, we calculate the number of participants in the analysis population by treatment arms.\n\nn_rand <- adsl %>%\n group_by(TRT01PN) %>%\n summarize(n = n()) %>%\n pivot_wider(\n names_from = TRT01PN,\n names_prefix = \"n_\",\n values_from = n\n ) %>%\n mutate(row = \"Participants in population\")\n\nn_rand\n#> # A tibble: 1 × 4\n#> n_0 n_54 n_81 row \n#> <int> <int> <int> <chr> \n#> 1 86 84 84 Participants in population\n\n\nn_disc <- adsl %>%\n group_by(TRT01PN) %>%\n summarize(\n n = sum(DISCONFL == \"Y\"),\n pct = formatC(n / n() * 100,\n digits = 1, format = \"f\", width = 5\n )\n ) %>%\n pivot_wider(\n names_from = TRT01PN,\n values_from = c(n, pct)\n ) %>%\n mutate(row = \"Discontinued\")\n\nn_disc\n#> # A tibble: 1 × 7\n#> n_0 n_54 n_81 pct_0 pct_54 pct_81 row \n#> <int> <int> <int> <chr> <chr> <chr> <chr> \n#> 1 28 59 57 \" 32.6\" \" 70.2\" \" 67.9\" Discontinued\n\nIn the code below, we calculate the number and percentage of participants who completed/discontinued the study for different reasons by treatment arms.\n\nn_reason <- adsl %>%\n group_by(TRT01PN) %>%\n mutate(n_total = n()) %>%\n group_by(TRT01PN, DCREASCD) %>%\n summarize(\n n = n(),\n pct = formatC(n / unique(n_total) * 100,\n digits = 1, format = \"f\", width = 5\n )\n ) %>%\n pivot_wider(\n id_cols = DCREASCD,\n names_from = TRT01PN,\n values_from = c(n, pct),\n values_fill = list(n = 0, pct = \" 0.0\")\n ) %>%\n rename(row = DCREASCD)\n\nn_reason\n#> # A tibble: 10 × 7\n#> row n_0 n_54 n_81 pct_0 pct_54 pct_81 \n#> <chr> <int> <int> <int> <chr> <chr> <chr> \n#> 1 Adverse Event 8 44 40 \" 9.3\" \" 52.4\" \" 47.6\"\n#> 2 Completed 58 25 27 \" 67.4\" \" 29.8\" \" 32.1\"\n#> 3 Death 2 1 0 \" 2.3\" \" 1.2\" \" 0.0\"\n#> 4 I/E Not Met 1 0 2 \" 1.2\" \" 0.0\" \" 2.4\"\n#> 5 Lack of Efficacy 3 0 1 \" 3.5\" \" 0.0\" \" 1.2\"\n#> 6 Lost to Follow-up 1 1 0 \" 1.2\" \" 1.2\" \" 0.0\"\n#> 7 Physician Decision 1 0 2 \" 1.2\" \" 0.0\" \" 2.4\"\n#> 8 Protocol Violation 1 1 1 \" 1.2\" \" 1.2\" \" 1.2\"\n#> 9 Sponsor Decision 2 2 3 \" 2.3\" \" 2.4\" \" 3.6\"\n#> 10 Withdrew Consent 9 10 8 \" 10.5\" \" 11.9\" \" 9.5\"\n\nIn the code below, we calculate the number and percentage of participants who complete the study by treatment arms. We split n_reason because we want to customize the row order of the table.\n\nn_complete <- n_reason %>%\n filter(row == \"Completed\")\n\nn_complete\n#> # A tibble: 1 × 7\n#> row n_0 n_54 n_81 pct_0 pct_54 pct_81 \n#> <chr> <int> <int> <int> <chr> <chr> <chr> \n#> 1 Completed 58 25 27 \" 67.4\" \" 29.8\" \" 32.1\"\n\nIn the code below, we calculate the numbers and percentages of participants who discontinued the study for different reasons by treatment arms. For display purpose, paste0(\" \", row) is used to add leading spaces to produce indentation in the final report.\n\nn_reason <- n_reason %>%\n filter(row != \"Completed\") %>%\n mutate(row = paste0(\" \", row))\n\nn_reason\n#> # A tibble: 9 × 7\n#> row n_0 n_54 n_81 pct_0 pct_54 pct_81 \n#> <chr> <int> <int> <int> <chr> <chr> <chr> \n#> 1 \" Adverse Event\" 8 44 40 \" 9.3\" \" 52.4\" \" 47.6\"\n#> 2 \" Death\" 2 1 0 \" 2.3\" \" 1.2\" \" 0.0\"\n#> 3 \" I/E Not Met\" 1 0 2 \" 1.2\" \" 0.0\" \" 2.4\"\n#> 4 \" Lack of Efficacy\" 3 0 1 \" 3.5\" \" 0.0\" \" 1.2\"\n#> 5 \" Lost to Follow-up\" 1 1 0 \" 1.2\" \" 1.2\" \" 0.0\"\n#> 6 \" Physician Decision\" 1 0 2 \" 1.2\" \" 0.0\" \" 2.4\"\n#> 7 \" Protocol Violation\" 1 1 1 \" 1.2\" \" 1.2\" \" 1.2\"\n#> 8 \" Sponsor Decision\" 2 2 3 \" 2.3\" \" 2.4\" \" 3.6\"\n#> 9 \" Withdrew Consent\" 9 10 8 \" 10.5\" \" 11.9\" \" 9.5\"\n\nNow we combine individual rows into one table for reporting purpose. tbl_disp is used as input for r2rtf to create final report.\n\ntbl_disp <- bind_rows(n_rand, n_complete, n_disc, n_reason) %>%\n select(row, ends_with(c(\"_0\", \"_54\", \"_81\")))\n\ntbl_disp\n#> # A tibble: 12 × 7\n#> row n_0 pct_0 n_54 pct_54 n_81 pct_81 \n#> <chr> <int> <chr> <int> <chr> <int> <chr> \n#> 1 \"Participants in population\" 86 <NA> 84 <NA> 84 <NA> \n#> 2 \"Completed\" 58 \" 67.4\" 25 \" 29.8\" 27 \" 32.1\"\n#> 3 \"Discontinued\" 28 \" 32.6\" 59 \" 70.2\" 57 \" 67.9\"\n#> 4 \" Adverse Event\" 8 \" 9.3\" 44 \" 52.4\" 40 \" 47.6\"\n#> 5 \" Death\" 2 \" 2.3\" 1 \" 1.2\" 0 \" 0.0\"\n#> 6 \" I/E Not Met\" 1 \" 1.2\" 0 \" 0.0\" 2 \" 2.4\"\n#> 7 \" Lack of Efficacy\" 3 \" 3.5\" 0 \" 0.0\" 1 \" 1.2\"\n#> 8 \" Lost to Follow-up\" 1 \" 1.2\" 1 \" 1.2\" 0 \" 0.0\"\n#> 9 \" Physician Decision\" 1 \" 1.2\" 0 \" 0.0\" 2 \" 2.4\"\n#> 10 \" Protocol Violation\" 1 \" 1.2\" 1 \" 1.2\" 1 \" 1.2\"\n#> 11 \" Sponsor Decision\" 2 \" 2.3\" 2 \" 2.4\" 3 \" 3.6\"\n#> 12 \" Withdrew Consent\" 9 \" 10.5\" 10 \" 11.9\" 8 \" 9.5\"\n\nIn the below code, formatting of the final table is defined. Items that were not discussed in the previous sections, are highlighted below.\nThe rtf_title defines table title. We can provide a vector for the title argument. Each value is a separate line. The format can also be controlled by providing a vector input in text format.\n\ntbl_disp %>%\n # Table title\n rtf_title(\"Disposition of Participants\") %>%\n # First row of column header\n rtf_colheader(\" | Placebo | Xanomeline Low Dose| Xanomeline High Dose\",\n col_rel_width = c(3, rep(2, 3))\n ) %>%\n # Second row of column header\n rtf_colheader(\" | n | (%) | n | (%) | n | (%)\",\n col_rel_width = c(3, rep(c(0.7, 1.3), 3)),\n border_top = c(\"\", rep(\"single\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n # Table body\n rtf_body(\n col_rel_width = c(3, rep(c(0.7, 1.3), 3)),\n text_justification = c(\"l\", rep(\"c\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n # Encoding RTF syntax\n rtf_encode() %>%\n # Save to a file\n write_rtf(\"tlf/tbl_disp.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe procedure to generate a disposition table can be summarized as follows:\n\nStep 1: Read subject level data (i.e., adsl) into R.\nStep 2: Count participants in the analysis population and name the dataset n_rand.\nStep 3: Calculate the number and percentage of participants who discontinued the study by treatment arm, and name the dataset n_disc.\nStep 4: Calculate the numbers and percentages of participants who discontinued the study for different reasons by treatment arm, and name the dataset n_reason.\nStep 5: Calculate the number and percentage of participants who completed the study by treatment arm, and name the dataset n_complete.\nStep 6: Bind n_rand, n_disc, n_reason, and n_complete by row.\nStep 7: Write the final table to RTF",
+ "text": "Following ICH E3 guidance, a summary table needs to be provided to include all participants who entered the study in Section 10.1, Disposition of Participants.\nThe disposition of participants table reports the numbers of participants who were randomized, and who entered and completed each phase of the study. In addition, the reasons for all post-randomization discontinuations, grouped by treatment and by major reason (lost to follow-up, adverse event, poor compliance, etc.) are reported.\n\nlibrary(haven) # Read SAS data\nlibrary(dplyr) # Manipulate data\nlibrary(tidyr) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this chapter, we show how to create a typical disposition table.\n\n\n\n\n\n\n\n\n\nThe first step is to read in the relevant datasets into R. For a disposition table, all the required information is saved in a Subject-level Analysis Dataset (ADSL). This dataset is provided in sas7bdat format, which is a SAS data format currently used in many clinical trial analysis and reporting. The haven package is able to read the dataset, while maintaining its attributes (e.g., variable labels).\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\n\nThe following variables are used in the preparation of a simplified disposition of participants table:\n\nUSUBJID: unique subject identifier\nTRT01P: planned treatment\nTRT01PN: planned treatment numeric encoding\nDISCONFL: discontinued from study flag\nDCREASCD: discontinued from study reason coded\n\n\nadsl %>% select(USUBJID, TRT01P, TRT01PN, DISCONFL, DCREASCD)\n#> # A tibble: 254 × 5\n#> USUBJID TRT01P TRT01PN DISCONFL DCREASCD \n#> <chr> <chr> <dbl> <chr> <chr> \n#> 1 01-701-1015 Placebo 0 \"\" Completed \n#> 2 01-701-1023 Placebo 0 \"Y\" Adverse Event \n#> 3 01-701-1028 Xanomeline High Dose 81 \"\" Completed \n#> 4 01-701-1033 Xanomeline Low Dose 54 \"Y\" Sponsor Decision\n#> # ℹ 250 more rows\n\nIn the code below, we calculate the number of participants in the analysis population by treatment arms.\n\nn_rand <- adsl %>%\n group_by(TRT01PN) %>%\n summarize(n = n()) %>%\n pivot_wider(\n names_from = TRT01PN,\n names_prefix = \"n_\",\n values_from = n\n ) %>%\n mutate(row = \"Participants in population\")\n\nn_rand\n#> # A tibble: 1 × 4\n#> n_0 n_54 n_81 row \n#> <int> <int> <int> <chr> \n#> 1 86 84 84 Participants in population\n\n\nn_disc <- adsl %>%\n group_by(TRT01PN) %>%\n summarize(\n n = sum(DISCONFL == \"Y\"),\n pct = formatC(n / n() * 100,\n digits = 1, format = \"f\", width = 5\n )\n ) %>%\n pivot_wider(\n names_from = TRT01PN,\n values_from = c(n, pct)\n ) %>%\n mutate(row = \"Discontinued\")\n\nn_disc\n#> # A tibble: 1 × 7\n#> n_0 n_54 n_81 pct_0 pct_54 pct_81 row \n#> <int> <int> <int> <chr> <chr> <chr> <chr> \n#> 1 28 59 57 \" 32.6\" \" 70.2\" \" 67.9\" Discontinued\n\nIn the code below, we calculate the number and percentage of participants who completed/discontinued the study for different reasons by treatment arms.\n\nn_reason <- adsl %>%\n group_by(TRT01PN) %>%\n mutate(n_total = n()) %>%\n group_by(TRT01PN, DCREASCD) %>%\n summarize(\n n = n(),\n pct = formatC(n / unique(n_total) * 100,\n digits = 1, format = \"f\", width = 5\n )\n ) %>%\n pivot_wider(\n id_cols = DCREASCD,\n names_from = TRT01PN,\n values_from = c(n, pct),\n values_fill = list(n = 0, pct = \" 0.0\")\n ) %>%\n rename(row = DCREASCD)\n\nn_reason\n#> # A tibble: 10 × 7\n#> row n_0 n_54 n_81 pct_0 pct_54 pct_81 \n#> <chr> <int> <int> <int> <chr> <chr> <chr> \n#> 1 Adverse Event 8 44 40 \" 9.3\" \" 52.4\" \" 47.6\"\n#> 2 Completed 58 25 27 \" 67.4\" \" 29.8\" \" 32.1\"\n#> 3 Death 2 1 0 \" 2.3\" \" 1.2\" \" 0.0\"\n#> 4 I/E Not Met 1 0 2 \" 1.2\" \" 0.0\" \" 2.4\"\n#> # ℹ 6 more rows\n\nIn the code below, we calculate the number and percentage of participants who complete the study by treatment arms. We split n_reason because we want to customize the row order of the table.\n\nn_complete <- n_reason %>%\n filter(row == \"Completed\")\n\nn_complete\n#> # A tibble: 1 × 7\n#> row n_0 n_54 n_81 pct_0 pct_54 pct_81 \n#> <chr> <int> <int> <int> <chr> <chr> <chr> \n#> 1 Completed 58 25 27 \" 67.4\" \" 29.8\" \" 32.1\"\n\nIn the code below, we calculate the numbers and percentages of participants who discontinued the study for different reasons by treatment arms. For display purpose, paste0(\" \", row) is used to add leading spaces to produce indentation in the final report.\n\nn_reason <- n_reason %>%\n filter(row != \"Completed\") %>%\n mutate(row = paste0(\" \", row))\n\nn_reason\n#> # A tibble: 9 × 7\n#> row n_0 n_54 n_81 pct_0 pct_54 pct_81 \n#> <chr> <int> <int> <int> <chr> <chr> <chr> \n#> 1 \" Adverse Event\" 8 44 40 \" 9.3\" \" 52.4\" \" 47.6\"\n#> 2 \" Death\" 2 1 0 \" 2.3\" \" 1.2\" \" 0.0\"\n#> 3 \" I/E Not Met\" 1 0 2 \" 1.2\" \" 0.0\" \" 2.4\"\n#> 4 \" Lack of Efficacy\" 3 0 1 \" 3.5\" \" 0.0\" \" 1.2\"\n#> # ℹ 5 more rows\n\nNow we combine individual rows into one table for reporting purpose. tbl_disp is used as input for r2rtf to create final report.\n\ntbl_disp <- bind_rows(n_rand, n_complete, n_disc, n_reason) %>%\n select(row, ends_with(c(\"_0\", \"_54\", \"_81\")))\n\ntbl_disp\n#> # A tibble: 12 × 7\n#> row n_0 pct_0 n_54 pct_54 n_81 pct_81 \n#> <chr> <int> <chr> <int> <chr> <int> <chr> \n#> 1 \"Participants in population\" 86 <NA> 84 <NA> 84 <NA> \n#> 2 \"Completed\" 58 \" 67.4\" 25 \" 29.8\" 27 \" 32.1\"\n#> 3 \"Discontinued\" 28 \" 32.6\" 59 \" 70.2\" 57 \" 67.9\"\n#> 4 \" Adverse Event\" 8 \" 9.3\" 44 \" 52.4\" 40 \" 47.6\"\n#> # ℹ 8 more rows\n\nIn the below code, formatting of the final table is defined. Items that were not discussed in the previous sections, are highlighted below.\nThe rtf_title defines table title. We can provide a vector for the title argument. Each value is a separate line. The format can also be controlled by providing a vector input in text format.\n\ntbl_disp %>%\n # Table title\n rtf_title(\"Disposition of Participants\") %>%\n # First row of column header\n rtf_colheader(\" | Placebo | Xanomeline Low Dose| Xanomeline High Dose\",\n col_rel_width = c(3, rep(2, 3))\n ) %>%\n # Second row of column header\n rtf_colheader(\" | n | (%) | n | (%) | n | (%)\",\n col_rel_width = c(3, rep(c(0.7, 1.3), 3)),\n border_top = c(\"\", rep(\"single\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n # Table body\n rtf_body(\n col_rel_width = c(3, rep(c(0.7, 1.3), 3)),\n text_justification = c(\"l\", rep(\"c\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n # Encoding RTF syntax\n rtf_encode() %>%\n # Save to a file\n write_rtf(\"tlf/tbl_disp.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe procedure to generate a disposition table can be summarized as follows:\n\nStep 1: Read subject level data (i.e., adsl) into R.\nStep 2: Count participants in the analysis population and name the dataset n_rand.\nStep 3: Calculate the number and percentage of participants who discontinued the study by treatment arm, and name the dataset n_disc.\nStep 4: Calculate the numbers and percentages of participants who discontinued the study for different reasons by treatment arm, and name the dataset n_reason.\nStep 5: Calculate the number and percentage of participants who completed the study by treatment arm, and name the dataset n_complete.\nStep 6: Bind n_rand, n_disc, n_reason, and n_complete by row.\nStep 7: Write the final table to RTF",
"crumbs": [
"Delivering TLFs in CSR",
"2 Disposition"
@@ -172,7 +172,7 @@
"href": "tlf-baseline.html",
"title": "4 Baseline characteristics",
"section": "",
- "text": "Following ICH E3 guidance, we need to summarize critical demographic and baseline characteristics of the participants in Section 11.2, Demographic and Other Baseline Characteristics.\nIn this chapter, we illustrate how to create a simplified baseline characteristics table for a study.\n\n\n\n\n\n\n\n\n\nThere are many R packages that can efficiently summarize baseline information. The table1 R package is one of them.\n\nlibrary(table1)\nlibrary(r2rtf)\nlibrary(haven)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(stringr)\nlibrary(tools)\n\nAs in previous chapters, we first read the adsl dataset that contains all the required information for the baseline characteristics table.\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\n\nFor simplicity, we only analyze SEX, AGE and, RACE in this example using the table1 R package. More details of the table1 R package can be found in the package vignettes.\nThe table1 R package directly creates an HTML report.\n\nana <- adsl %>%\n mutate(\n SEX = factor(SEX, c(\"F\", \"M\"), c(\"Female\", \"Male\")),\n RACE = toTitleCase(tolower(RACE))\n )\n\ntbl <- table1(~ SEX + AGE + RACE | TRT01P, data = ana)\ntbl\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nPlacebo\n(N=86)\nXanomeline High Dose\n(N=84)\nXanomeline Low Dose\n(N=84)\nOverall\n(N=254)\n\n\n\n\nSEX\n\n\n\n\n\n\nFemale\n53 (61.6%)\n40 (47.6%)\n50 (59.5%)\n143 (56.3%)\n\n\nMale\n33 (38.4%)\n44 (52.4%)\n34 (40.5%)\n111 (43.7%)\n\n\nAge\n\n\n\n\n\n\nMean (SD)\n75.2 (8.59)\n74.4 (7.89)\n75.7 (8.29)\n75.1 (8.25)\n\n\nMedian [Min, Max]\n76.0 [52.0, 89.0]\n76.0 [56.0, 88.0]\n77.5 [51.0, 88.0]\n77.0 [51.0, 89.0]\n\n\nRACE\n\n\n\n\n\n\nBlack or African American\n8 (9.3%)\n9 (10.7%)\n6 (7.1%)\n23 (9.1%)\n\n\nWhite\n78 (90.7%)\n74 (88.1%)\n78 (92.9%)\n230 (90.6%)\n\n\nAmerican Indian or Alaska Native\n0 (0%)\n1 (1.2%)\n0 (0%)\n1 (0.4%)\n\n\n\n\n\n\n\n\nThe code below transfer the output into a dataframe that only contains ASCII characters recommended by regulatory agencies. tbl_base is used as input for r2rtf to create the final report.\n\ntbl_base <- tbl %>%\n as.data.frame() %>%\n as_tibble() %>%\n mutate(across(\n everything(),\n ~ str_replace_all(.x, intToUtf8(160), \" \")\n ))\n\n\nnames(tbl_base) <- str_replace_all(names(tbl_base), intToUtf8(160), \" \")\ntbl_base\n#> # A tibble: 11 × 5\n#> ` ` Placebo `Xanomeline High Dose` `Xanomeline Low Dose` Overall\n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 \"\" \"(N=86… \"(N=84)\" \"(N=84)\" \"(N=25…\n#> 2 \"SEX\" \"\" \"\" \"\" \"\" \n#> 3 \" Female\" \"53 (6… \"40 (47.6%)\" \"50 (59.5%)\" \"143 (…\n#> 4 \" Male\" \"33 (3… \"44 (52.4%)\" \"34 (40.5%)\" \"111 (…\n#> 5 \"Age\" \"\" \"\" \"\" \"\" \n#> 6 \" Mean (SD)\" \"75.2 … \"74.4 (7.89)\" \"75.7 (8.29)\" \"75.1 …\n#> 7 \" Median [Min,… \"76.0 … \"76.0 [56.0, 88.0]\" \"77.5 [51.0, 88.0]\" \"77.0 …\n#> 8 \"RACE\" \"\" \"\" \"\" \"\" \n#> 9 \" Black or Afr… \"8 (9.… \"9 (10.7%)\" \"6 (7.1%)\" \"23 (9…\n#> 10 \" White\" \"78 (9… \"74 (88.1%)\" \"78 (92.9%)\" \"230 (…\n#> 11 \" American Ind… \"0 (0%… \"1 (1.2%)\" \"0 (0%)\" \"1 (0.…\n\nWe define the format of the output. We highlight items that are not discussed in previous discussion.\ntext_indent_first and text_indent_left are used to control the indent space of text. They are helpful when you need to control the white space of a long phrase, “AMERICAN INDIAN OR ALASKA NATIVE” in the table provides an example.\n\ncolheader1 <- paste(names(tbl_base), collapse = \"|\")\ncolheader2 <- paste(tbl_base[1, ], collapse = \"|\")\nrel_width <- c(2.5, rep(1, 4))\n\ntbl_base[-1, ] %>%\n rtf_title(\n \"Baseline Characteristics of Participants\",\n \"(All Participants Randomized)\"\n ) %>%\n rtf_colheader(colheader1,\n col_rel_width = rel_width\n ) %>%\n rtf_colheader(colheader2,\n border_top = \"\",\n col_rel_width = rel_width\n ) %>%\n rtf_body(\n col_rel_width = rel_width,\n text_justification = c(\"l\", rep(\"c\", 4)),\n text_indent_first = -240,\n text_indent_left = 180\n ) %>%\n rtf_encode() %>%\n write_rtf(\"tlf/tlf_base.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn conclusion, the procedure to generate demographic and baseline characteristics table is summarized as follows:\n\nStep 1: Read the data set.\nStep 2: Use table1::table1() to get the baseline characteristics table.\nStep 3: Transfer the output from Step 2 into a data frame that only contains ASCII characters.\nStep 4: Define the format of the RTF table by using the R package r2rtf.",
+ "text": "Following ICH E3 guidance, we need to summarize critical demographic and baseline characteristics of the participants in Section 11.2, Demographic and Other Baseline Characteristics.\nIn this chapter, we illustrate how to create a simplified baseline characteristics table for a study.\n\n\n\n\n\n\n\n\n\nThere are many R packages that can efficiently summarize baseline information. The table1 R package is one of them.\n\nlibrary(table1)\nlibrary(r2rtf)\nlibrary(haven)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(stringr)\nlibrary(tools)\n\nAs in previous chapters, we first read the adsl dataset that contains all the required information for the baseline characteristics table.\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\n\nFor simplicity, we only analyze SEX, AGE and, RACE in this example using the table1 R package. More details of the table1 R package can be found in the package vignettes.\nThe table1 R package directly creates an HTML report.\n\nana <- adsl %>%\n mutate(\n SEX = factor(SEX, c(\"F\", \"M\"), c(\"Female\", \"Male\")),\n RACE = toTitleCase(tolower(RACE))\n )\n\ntbl <- table1(~ SEX + AGE + RACE | TRT01P, data = ana)\ntbl\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nPlacebo\n(N=86)\nXanomeline High Dose\n(N=84)\nXanomeline Low Dose\n(N=84)\nOverall\n(N=254)\n\n\n\n\nSEX\n\n\n\n\n\n\nFemale\n53 (61.6%)\n40 (47.6%)\n50 (59.5%)\n143 (56.3%)\n\n\nMale\n33 (38.4%)\n44 (52.4%)\n34 (40.5%)\n111 (43.7%)\n\n\nAge\n\n\n\n\n\n\nMean (SD)\n75.2 (8.59)\n74.4 (7.89)\n75.7 (8.29)\n75.1 (8.25)\n\n\nMedian [Min, Max]\n76.0 [52.0, 89.0]\n76.0 [56.0, 88.0]\n77.5 [51.0, 88.0]\n77.0 [51.0, 89.0]\n\n\nRACE\n\n\n\n\n\n\nBlack or African American\n8 (9.3%)\n9 (10.7%)\n6 (7.1%)\n23 (9.1%)\n\n\nWhite\n78 (90.7%)\n74 (88.1%)\n78 (92.9%)\n230 (90.6%)\n\n\nAmerican Indian or Alaska Native\n0 (0%)\n1 (1.2%)\n0 (0%)\n1 (0.4%)\n\n\n\n\n\n\n\n\nThe code below transfer the output into a dataframe that only contains ASCII characters recommended by regulatory agencies. tbl_base is used as input for r2rtf to create the final report.\n\ntbl_base <- tbl %>%\n as.data.frame() %>%\n as_tibble() %>%\n mutate(across(\n everything(),\n ~ str_replace_all(.x, intToUtf8(160), \" \")\n ))\n\n\nnames(tbl_base) <- str_replace_all(names(tbl_base), intToUtf8(160), \" \")\ntbl_base\n#> # A tibble: 11 × 5\n#> ` ` Placebo `Xanomeline High Dose` `Xanomeline Low Dose` Overall \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 \"\" \"(N=86)\" \"(N=84)\" \"(N=84)\" \"(N=254)\"\n#> 2 \"SEX\" \"\" \"\" \"\" \"\" \n#> 3 \" Female\" \"53 (61.6%)\" \"40 (47.6%)\" \"50 (59.5%)\" \"143 (56…\n#> 4 \" Male\" \"33 (38.4%)\" \"44 (52.4%)\" \"34 (40.5%)\" \"111 (43…\n#> # ℹ 7 more rows\n\nWe define the format of the output. We highlight items that are not discussed in previous discussion.\ntext_indent_first and text_indent_left are used to control the indent space of text. They are helpful when you need to control the white space of a long phrase, “AMERICAN INDIAN OR ALASKA NATIVE” in the table provides an example.\n\ncolheader1 <- paste(names(tbl_base), collapse = \"|\")\ncolheader2 <- paste(tbl_base[1, ], collapse = \"|\")\nrel_width <- c(2.5, rep(1, 4))\n\ntbl_base[-1, ] %>%\n rtf_title(\n \"Baseline Characteristics of Participants\",\n \"(All Participants Randomized)\"\n ) %>%\n rtf_colheader(colheader1,\n col_rel_width = rel_width\n ) %>%\n rtf_colheader(colheader2,\n border_top = \"\",\n col_rel_width = rel_width\n ) %>%\n rtf_body(\n col_rel_width = rel_width,\n text_justification = c(\"l\", rep(\"c\", 4)),\n text_indent_first = -240,\n text_indent_left = 180\n ) %>%\n rtf_encode() %>%\n write_rtf(\"tlf/tlf_base.rtf\")\n\n\n\n\n\n\n\n\n\n\nIn conclusion, the procedure to generate demographic and baseline characteristics table is summarized as follows:\n\nStep 1: Read the data set.\nStep 2: Use table1::table1() to get the baseline characteristics table.\nStep 3: Transfer the output from Step 2 into a data frame that only contains ASCII characters.\nStep 4: Define the format of the RTF table by using the R package r2rtf.",
"crumbs": [
"Delivering TLFs in CSR",
"4 Baseline characteristics"
@@ -183,7 +183,7 @@
"href": "tlf-efficacy-ancova.html",
"title": "5 Efficacy table",
"section": "",
- "text": "5.1 Analysis dataset\nTo prepare the analysis, both adsl and adlbc datasets are required.\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\nadlb <- read_sas(\"data-adam/adlbc.sas7bdat\")\nFirst, both the population and the data in scope are selected. The analysis is done on the efficacy population, identified by EFFFL == \"Y\", and all records post baseline (AVISITN >= 1) and on or before Week 24 (AVISITN <= 24). Here the variable AVISITN is the numerical analysis visit. For example, if the analysis visit is recorded as “Baseline” (i.e., AVISIT = Baseline), AVISITN = 0; if the analysis visit is recorded as “Week 24” (i.e., AVISIT = Week 24), AVISITN = 24; if the analysis visit is blank, AVISITN is also blank. We will discuss these missing values in Section 6.4.\ngluc <- adlb %>%\n left_join(adsl %>% select(USUBJID, EFFFL), by = \"USUBJID\") %>%\n # PARAMCD is parameter code and here we focus on Glucose (mg/dL)\n filter(EFFFL == \"Y\" & PARAMCD == \"GLUC\") %>%\n arrange(TRTPN) %>%\n mutate(TRTP = factor(TRTP, levels = unique(TRTP)))\n\nana <- gluc %>%\n filter(AVISITN > 0 & AVISITN <= 24) %>%\n arrange(AVISITN) %>%\n mutate(AVISIT = factor(AVISIT, levels = unique(AVISIT)))\nBelow is the first few records of the analysis dataset.\nana %>%\n select(USUBJID, TRTPN, AVISIT, AVAL, BASE, CHG) %>%\n head(4)\n#> # A tibble: 4 × 6\n#> USUBJID TRTPN AVISIT AVAL BASE CHG\n#> <chr> <dbl> <fct> <dbl> <dbl> <dbl>\n#> 1 01-701-1015 0 \" Week 2\" 4.66 4.72 -0.0555\n#> 2 01-701-1023 0 \" Week 2\" 5.77 5.33 0.444 \n#> 3 01-701-1047 0 \" Week 2\" 5.55 5.55 0 \n#> 4 01-701-1118 0 \" Week 2\" 4.88 4.05 0.833",
+ "text": "5.1 Analysis dataset\nTo prepare the analysis, both adsl and adlbc datasets are required.\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\nadlb <- read_sas(\"data-adam/adlbc.sas7bdat\")\nFirst, both the population and the data in scope are selected. The analysis is done on the efficacy population, identified by EFFFL == \"Y\", and all records post baseline (AVISITN >= 1) and on or before Week 24 (AVISITN <= 24). Here the variable AVISITN is the numerical analysis visit. For example, if the analysis visit is recorded as “Baseline” (i.e., AVISIT = Baseline), AVISITN = 0; if the analysis visit is recorded as “Week 24” (i.e., AVISIT = Week 24), AVISITN = 24; if the analysis visit is blank, AVISITN is also blank. We will discuss these missing values in Section 6.4.\ngluc <- adlb %>%\n left_join(adsl %>% select(USUBJID, EFFFL), by = \"USUBJID\") %>%\n # PARAMCD is parameter code and here we focus on Glucose (mg/dL)\n filter(EFFFL == \"Y\" & PARAMCD == \"GLUC\") %>%\n arrange(TRTPN) %>%\n mutate(TRTP = factor(TRTP, levels = unique(TRTP)))\n\nana <- gluc %>%\n filter(AVISITN > 0 & AVISITN <= 24) %>%\n arrange(AVISITN) %>%\n mutate(AVISIT = factor(AVISIT, levels = unique(AVISIT)))\nBelow is the first few records of the analysis dataset.\nana %>% select(USUBJID, TRTPN, AVISIT, AVAL, BASE, CHG)\n#> # A tibble: 1,377 × 6\n#> USUBJID TRTPN AVISIT AVAL BASE CHG\n#> <chr> <dbl> <fct> <dbl> <dbl> <dbl>\n#> 1 01-701-1015 0 \" Week 2\" 4.66 4.72 -0.0555\n#> 2 01-701-1023 0 \" Week 2\" 5.77 5.33 0.444 \n#> 3 01-701-1047 0 \" Week 2\" 5.55 5.55 0 \n#> 4 01-701-1118 0 \" Week 2\" 4.88 4.05 0.833 \n#> # ℹ 1,373 more rows",
"crumbs": [
"Delivering TLFs in CSR",
"5 Efficacy table"
@@ -227,7 +227,7 @@
"href": "tlf-efficacy-ancova.html#missing-data-imputation",
"title": "5 Efficacy table",
"section": "5.4 Missing data imputation",
- "text": "5.4 Missing data imputation\nIn clinical trials, missing data is inevitable. In this study, there are missing values in glucose data.\n\ncount(ana, AVISIT)\n#> # A tibble: 8 × 2\n#> AVISIT n\n#> <fct> <int>\n#> 1 \" Week 2\" 229\n#> 2 \" Week 4\" 211\n#> 3 \" Week 6\" 197\n#> 4 \" Week 8\" 187\n#> 5 \" Week 12\" 167\n#> 6 \" Week 16\" 147\n#> 7 \" Week 20\" 126\n#> 8 \" Week 24\" 113\n\nFor simplicity and illustration purpose, we use the last observation carried forward (LOCF) approach to handle missing data. LOCF approach is a single imputation approach that is not recommended in real application. Interested readers can find more discussion on missing data approaches in the book: The Prevention and Treatment of Missing Data in Clinical Trials.\n\nana_locf <- ana %>%\n group_by(USUBJID) %>%\n mutate(locf = AVISITN == max(AVISITN)) %>%\n filter(locf)",
+ "text": "5.4 Missing data imputation\nIn clinical trials, missing data is inevitable. In this study, there are missing values in glucose data.\n\ncount(ana, AVISIT)\n#> # A tibble: 8 × 2\n#> AVISIT n\n#> <fct> <int>\n#> 1 \" Week 2\" 229\n#> 2 \" Week 4\" 211\n#> 3 \" Week 6\" 197\n#> 4 \" Week 8\" 187\n#> # ℹ 4 more rows\n\nFor simplicity and illustration purpose, we use the last observation carried forward (LOCF) approach to handle missing data. LOCF approach is a single imputation approach that is not recommended in real application. Interested readers can find more discussion on missing data approaches in the book: The Prevention and Treatment of Missing Data in Clinical Trials.\n\nana_locf <- ana %>%\n group_by(USUBJID) %>%\n mutate(locf = AVISITN == max(AVISITN)) %>%\n filter(locf)",
"crumbs": [
"Delivering TLFs in CSR",
"5 Efficacy table"
@@ -293,7 +293,7 @@
"href": "tlf-ae-summary.html",
"title": "7 AE summary",
"section": "",
- "text": "Following ICH E3 guidance, we summarize number of participants that were included in each safety analysis in Section 12.2, Adverse Events (AEs).\n\nlibrary(haven) # Read SAS data\nlibrary(dplyr) # Manipulate data\nlibrary(tidyr) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this chapter, we illustrate how to summarize AEs information for a study.\n\n\n\n\n\n\n\n\n\nThe data used to summarize AE information is in adsl and adae datasets.\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\nadae <- read_sas(\"data-adam/adae.sas7bdat\")\n\nWe first summarize participants in population by treatment arm.\n\npop <- adsl %>%\n filter(SAFFL == \"Y\") %>%\n rename(TRTAN = TRT01AN) %>%\n count(TRTAN, name = \"tot\")\n\npop\n#> # A tibble: 3 × 2\n#> TRTAN tot\n#> <dbl> <int>\n#> 1 0 86\n#> 2 54 84\n#> 3 81 84\n\nWe transform the data to simplify the analysis of each required AE criteria of interest.\n\nWith one or more adverse events\nWith drug-related adverse events\nWith serious adverse events\nWith serious drug-related adverse events\nWho died\n\n\ntidy_ae <- adae %>%\n mutate(\n all = SAFFL == \"Y\",\n drug = AEREL %in% c(\"POSSIBLE\", \"PROBABLE\"),\n ser = AESER == \"Y\",\n drug_ser = drug & ser,\n die = AEOUT == \"FATAL\"\n ) %>%\n select(USUBJID, TRTAN, all, drug, ser, drug_ser, die) %>%\n pivot_longer(cols = c(all, drug, ser, drug_ser, die))\n\ntidy_ae %>% head(4)\n#> # A tibble: 4 × 4\n#> USUBJID TRTAN name value\n#> <chr> <dbl> <chr> <lgl>\n#> 1 01-701-1015 0 all TRUE \n#> 2 01-701-1015 0 drug TRUE \n#> 3 01-701-1015 0 ser FALSE\n#> 4 01-701-1015 0 drug_ser FALSE\n\nWe summarize the number and percentage of participants who meet each AE criteria.\n\nfmt_num <- function(x, digits, width = digits + 4) {\n formatC(\n x,\n digits = digits,\n format = \"f\",\n width = width\n )\n}\n\n\nana <- tidy_ae %>%\n filter(value == TRUE) %>%\n group_by(TRTAN, name) %>%\n summarise(n = n_distinct(USUBJID)) %>%\n left_join(pop, by = \"TRTAN\") %>%\n mutate(\n pct = fmt_num(n / tot * 100, digits = 1),\n n = fmt_num(n, digits = 0),\n pct = paste0(\"(\", pct, \")\")\n )\n\nana %>% head(4)\n#> # A tibble: 4 × 5\n#> # Groups: TRTAN [2]\n#> TRTAN name n tot pct \n#> <dbl> <chr> <chr> <int> <chr> \n#> 1 0 all \" 69\" 86 ( 80.2)\n#> 2 0 die \" 2\" 86 ( 2.3)\n#> 3 0 drug \" 44\" 86 ( 51.2)\n#> 4 54 all \" 77\" 84 ( 91.7)\n\nWe prepare reporting-ready dataset for each AE group.\n\nt_ae <- ana %>%\n pivot_wider(\n id_cols = \"name\",\n names_from = TRTAN,\n values_from = c(n, pct),\n values_fill = list(\n n = \" 0\",\n pct = \"( 0.0)\"\n )\n )\n\nt_ae <- t_ae %>%\n mutate(name = factor(\n name,\n c(\"all\", \"drug\", \"ser\", \"drug_ser\", \"die\"),\n c(\n \"With one or more adverse events\",\n \"With drug-related adverse events\",\n \"With serious adverse events\",\n \"With serious drug-related adverse events\",\n \"Who died\"\n )\n )) %>%\n arrange(name)\n\nWe prepare reporting-ready dataset for the analysis population.\n\nt_pop <- pop %>%\n mutate(\n name = \"Participants in population\",\n tot = fmt_num(tot, digits = 0)\n ) %>%\n pivot_wider(\n id_cols = name,\n names_from = TRTAN,\n names_prefix = \"n_\",\n values_from = tot\n )\n\nt_pop\n#> # A tibble: 1 × 4\n#> name n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> \n#> 1 Participants in population \" 86\" \" 84\" \" 84\"\n\nThe final report data is saved in tbl_ae_summary.\n\ntbl_ae_summary <- bind_rows(t_pop, t_ae) %>%\n select(name, ends_with(\"_0\"), ends_with(\"_54\"), ends_with(\"_81\"))\n\ntbl_ae_summary\n#> # A tibble: 6 × 7\n#> name n_0 pct_0 n_54 pct_54 n_81 pct_81\n#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> \n#> 1 Participants in population \" 8… <NA> \" 8… <NA> \" 8… <NA> \n#> 2 With one or more adverse events \" 6… ( 80… \" 7… ( 91.… \" 7… ( 94.…\n#> 3 With drug-related adverse events \" 4… ( 51… \" 7… ( 86.… \" 7… ( 83.…\n#> 4 With serious adverse events \" … ( 0… \" … ( 1.… \" … ( 2.…\n#> 5 With serious drug-related adverse events \" … ( 0… \" … ( 1.… \" … ( 1.…\n#> 6 Who died \" … ( 2… \" … ( 1.… \" … ( 0.…\n\nWe define the format of the output using code below:\n\ntbl_ae_summary %>%\n rtf_title(\n \"Analysis of Adverse Event Summary\",\n \"(Safety Analysis Population)\"\n ) %>%\n rtf_colheader(\" | Placebo | Xanomeline Low Dose| Xanomeline High Dose\",\n col_rel_width = c(3.5, rep(2, 3))\n ) %>%\n rtf_colheader(\" | n | (%) | n | (%) | n | (%)\",\n col_rel_width = c(3.5, rep(c(0.7, 1.3), 3)),\n border_top = c(\"\", rep(\"single\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n rtf_body(\n col_rel_width = c(3.5, rep(c(0.7, 1.3), 3)),\n text_justification = c(\"l\", rep(\"c\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n rtf_footnote(\"Every subject is counted a single time for each applicable row and column.\") %>%\n rtf_encode() %>%\n write_rtf(\"tlf/tlf_ae_summary.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe procedure to generate an AE summary table can be summarized as follows:\n\nStep 1: Read data (i.e., adae and adsl) into R.\nStep 2: Summarize participants in population by treatment arm, and name the dataset as t_pop.\nStep 3: Summarize participants in population by required AE criteria of interest, and name the dataset as t_ae.\nStep 4: Row-wise combine t_pop and t_ae and format it by using r2rtf.",
+ "text": "Following ICH E3 guidance, we summarize number of participants that were included in each safety analysis in Section 12.2, Adverse Events (AEs).\n\nlibrary(haven) # Read SAS data\nlibrary(dplyr) # Manipulate data\nlibrary(tidyr) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this chapter, we illustrate how to summarize AEs information for a study.\n\n\n\n\n\n\n\n\n\nThe data used to summarize AE information is in adsl and adae datasets.\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\nadae <- read_sas(\"data-adam/adae.sas7bdat\")\n\nWe first summarize participants in population by treatment arm.\n\npop <- adsl %>%\n filter(SAFFL == \"Y\") %>%\n rename(TRTAN = TRT01AN) %>%\n count(TRTAN, name = \"tot\")\n\npop\n#> # A tibble: 3 × 2\n#> TRTAN tot\n#> <dbl> <int>\n#> 1 0 86\n#> 2 54 84\n#> 3 81 84\n\nWe transform the data to simplify the analysis of each required AE criteria of interest.\n\nWith one or more adverse events\nWith drug-related adverse events\nWith serious adverse events\nWith serious drug-related adverse events\nWho died\n\n\ntidy_ae <- adae %>%\n mutate(\n all = SAFFL == \"Y\",\n drug = AEREL %in% c(\"POSSIBLE\", \"PROBABLE\"),\n ser = AESER == \"Y\",\n drug_ser = drug & ser,\n die = AEOUT == \"FATAL\"\n ) %>%\n select(USUBJID, TRTAN, all, drug, ser, drug_ser, die) %>%\n pivot_longer(cols = c(all, drug, ser, drug_ser, die))\n\ntidy_ae\n#> # A tibble: 5,955 × 4\n#> USUBJID TRTAN name value\n#> <chr> <dbl> <chr> <lgl>\n#> 1 01-701-1015 0 all TRUE \n#> 2 01-701-1015 0 drug TRUE \n#> 3 01-701-1015 0 ser FALSE\n#> 4 01-701-1015 0 drug_ser FALSE\n#> # ℹ 5,951 more rows\n\nWe summarize the number and percentage of participants who meet each AE criteria.\n\nfmt_num <- function(x, digits, width = digits + 4) {\n formatC(\n x,\n digits = digits,\n format = \"f\",\n width = width\n )\n}\n\n\nana <- tidy_ae %>%\n filter(value == TRUE) %>%\n group_by(TRTAN, name) %>%\n summarise(n = n_distinct(USUBJID)) %>%\n left_join(pop, by = \"TRTAN\") %>%\n mutate(\n pct = fmt_num(n / tot * 100, digits = 1),\n n = fmt_num(n, digits = 0),\n pct = paste0(\"(\", pct, \")\")\n )\n\nana\n#> # A tibble: 12 × 5\n#> # Groups: TRTAN [3]\n#> TRTAN name n tot pct \n#> <dbl> <chr> <chr> <int> <chr> \n#> 1 0 all \" 69\" 86 ( 80.2)\n#> 2 0 die \" 2\" 86 ( 2.3)\n#> 3 0 drug \" 44\" 86 ( 51.2)\n#> 4 54 all \" 77\" 84 ( 91.7)\n#> 5 54 die \" 1\" 84 ( 1.2)\n#> 6 54 drug \" 73\" 84 ( 86.9)\n#> 7 54 drug_ser \" 1\" 84 ( 1.2)\n#> 8 54 ser \" 1\" 84 ( 1.2)\n#> 9 81 all \" 79\" 84 ( 94.0)\n#> 10 81 drug \" 70\" 84 ( 83.3)\n#> 11 81 drug_ser \" 1\" 84 ( 1.2)\n#> 12 81 ser \" 2\" 84 ( 2.4)\n\nWe prepare reporting-ready dataset for each AE group.\n\nt_ae <- ana %>%\n pivot_wider(\n id_cols = \"name\",\n names_from = TRTAN,\n values_from = c(n, pct),\n values_fill = list(\n n = \" 0\",\n pct = \"( 0.0)\"\n )\n )\n\nt_ae <- t_ae %>%\n mutate(name = factor(\n name,\n c(\"all\", \"drug\", \"ser\", \"drug_ser\", \"die\"),\n c(\n \"With one or more adverse events\",\n \"With drug-related adverse events\",\n \"With serious adverse events\",\n \"With serious drug-related adverse events\",\n \"Who died\"\n )\n )) %>%\n arrange(name)\n\nWe prepare reporting-ready dataset for the analysis population.\n\nt_pop <- pop %>%\n mutate(\n name = \"Participants in population\",\n tot = fmt_num(tot, digits = 0)\n ) %>%\n pivot_wider(\n id_cols = name,\n names_from = TRTAN,\n names_prefix = \"n_\",\n values_from = tot\n )\n\nt_pop\n#> # A tibble: 1 × 4\n#> name n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> \n#> 1 Participants in population \" 86\" \" 84\" \" 84\"\n\nThe final report data is saved in tbl_ae_summary.\n\ntbl_ae_summary <- bind_rows(t_pop, t_ae) %>%\n select(name, ends_with(\"_0\"), ends_with(\"_54\"), ends_with(\"_81\"))\n\ntbl_ae_summary\n#> # A tibble: 6 × 7\n#> name n_0 pct_0 n_54 pct_54 n_81 pct_81 \n#> <chr> <chr> <chr> <chr> <chr> <chr> <chr> \n#> 1 Participants in population \" 86\" <NA> \" 84\" <NA> \" 84\" <NA> \n#> 2 With one or more adverse events \" 69\" ( 80.2) \" 77\" ( 91.7) \" 79\" ( 94.0)\n#> 3 With drug-related adverse events \" 44\" ( 51.2) \" 73\" ( 86.9) \" 70\" ( 83.3)\n#> 4 With serious adverse events \" 0\" ( 0.0) \" 1\" ( 1.2) \" 2\" ( 2.4)\n#> # ℹ 2 more rows\n\nWe define the format of the output using code below:\n\ntbl_ae_summary %>%\n rtf_title(\n \"Analysis of Adverse Event Summary\",\n \"(Safety Analysis Population)\"\n ) %>%\n rtf_colheader(\" | Placebo | Xanomeline Low Dose| Xanomeline High Dose\",\n col_rel_width = c(3.5, rep(2, 3))\n ) %>%\n rtf_colheader(\" | n | (%) | n | (%) | n | (%)\",\n col_rel_width = c(3.5, rep(c(0.7, 1.3), 3)),\n border_top = c(\"\", rep(\"single\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n rtf_body(\n col_rel_width = c(3.5, rep(c(0.7, 1.3), 3)),\n text_justification = c(\"l\", rep(\"c\", 6)),\n border_left = c(\"single\", rep(c(\"single\", \"\"), 3))\n ) %>%\n rtf_footnote(\"Every subject is counted a single time for each applicable row and column.\") %>%\n rtf_encode() %>%\n write_rtf(\"tlf/tlf_ae_summary.rtf\")\n\n\n\n\n\n\n\n\n\n\nThe procedure to generate an AE summary table can be summarized as follows:\n\nStep 1: Read data (i.e., adae and adsl) into R.\nStep 2: Summarize participants in population by treatment arm, and name the dataset as t_pop.\nStep 3: Summarize participants in population by required AE criteria of interest, and name the dataset as t_ae.\nStep 4: Row-wise combine t_pop and t_ae and format it by using r2rtf.",
"crumbs": [
"Delivering TLFs in CSR",
"7 AE summary"
@@ -304,7 +304,7 @@
"href": "tlf-ae-specific.html",
"title": "8 Specific AE",
"section": "",
- "text": "Following ICH E3 guidance, we need to summarize number of participants for each specific AE in Section 12.2, Adverse Events (AEs).\n\nlibrary(haven) # Read SAS data\nlibrary(dplyr) # Manipulate data\nlibrary(tidyr) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this chapter, we illustrate how to summarize simplified specific AE information for a study.\n\n\n\n\n\n\n\n\n\nThe data used to summarize AE information is in adsl and adae datasets.\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\nadae <- read_sas(\"data-adam/adae.sas7bdat\")\n\nFor illustration purpose, we only provide counts in the simplified table. The percentage of participants for each AE can be calculated as shown in Chapter 7.\nHere, we focus on the analysis script for two advanced features for a table layout.\n\ngroup content: AE can be summarized in multiple nested layers. (e.g., by system organ class (SOC, AESOC) and specific AE term (AEDECOD))\npagenization: there are many AE terms that can not be covered in one page. Column headers and SOC information need to be repeated on every page.\n\nIn the code below, we count the number of participants in each AE term by SOC and treatment arm, and we create a new variable order and set it as 0. The variable order can help with the data manipulation later.\n\nfmt_num <- function(x, digits, width = digits + 4) {\n formatC(\n x,\n digits = digits,\n format = \"f\",\n width = width\n )\n}\n\n\nana <- adae %>%\n mutate(\n AESOC = tools::toTitleCase(tolower(AESOC)),\n AEDECOD = tools::toTitleCase(tolower(AEDECOD))\n )\n\nt1 <- ana %>%\n group_by(TRTAN, AESOC) %>%\n summarise(n = fmt_num(n_distinct(USUBJID), digits = 0)) %>%\n mutate(AEDECOD = AESOC, order = 0)\n\nt1 %>% head(4)\n#> # A tibble: 4 × 5\n#> # Groups: TRTAN [1]\n#> TRTAN AESOC n AEDECOD order\n#> <dbl> <chr> <chr> <chr> <dbl>\n#> 1 0 Cardiac Disorders \" 13\" Cardiac Disorders 0\n#> 2 0 Ear and Labyrinth Disorders \" 1\" Ear and Labyrinth Disorders 0\n#> 3 0 Eye Disorders \" 4\" Eye Disorders 0\n#> 4 0 Gastrointestinal Disorders \" 17\" Gastrointestinal Disorders 0\n\nIn the code below, we count the number of subjects in each AE term by SOC, AE term, and treatment arm. Here we also create a new variable order and set it as 1.\n\nt2 <- ana %>%\n group_by(TRTAN, AESOC, AEDECOD) %>%\n summarise(n = fmt_num(n_distinct(USUBJID), digits = 0)) %>%\n mutate(order = 1)\n\nt2 %>% head(4)\n#> # A tibble: 4 × 5\n#> # Groups: TRTAN, AESOC [1]\n#> TRTAN AESOC AEDECOD n order\n#> <dbl> <chr> <chr> <chr> <dbl>\n#> 1 0 Cardiac Disorders Atrial Fibrillation \" 1\" 1\n#> 2 0 Cardiac Disorders Atrial Hypertrophy \" 1\" 1\n#> 3 0 Cardiac Disorders Atrioventricular Block First Degree \" 1\" 1\n#> 4 0 Cardiac Disorders Atrioventricular Block Second Degree \" 2\" 1\n\nWe prepare reporting data for AE information using code below:\n\nt_ae <- bind_rows(t1, t2) %>%\n pivot_wider(\n id_cols = c(AESOC, order, AEDECOD),\n names_from = TRTAN,\n names_prefix = \"n_\",\n values_from = n,\n values_fill = fmt_num(0, digits = 0)\n ) %>%\n arrange(AESOC, order, AEDECOD) %>%\n select(AESOC, AEDECOD, starts_with(\"n\"))\n\nt_ae %>% head(4)\n#> # A tibble: 4 × 5\n#> AESOC AEDECOD n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 Cardiac Disorders Cardiac Disorders \" 13\" \" 13\" \" 18\"\n#> 2 Cardiac Disorders Atrial Fibrillation \" 1\" \" 1\" \" 3\"\n#> 3 Cardiac Disorders Atrial Flutter \" 0\" \" 1\" \" 1\"\n#> 4 Cardiac Disorders Atrial Hypertrophy \" 1\" \" 0\" \" 0\"\n\nWe prepare reporting data for analysis population using code below:\n\ncount_by <- function(data, # Input data set\n grp, # Group variable\n var, # Analysis variable\n var_label = var, # Analysis variable label\n id = \"USUBJID\") { # Subject ID variable\n data <- data %>% rename(grp = !!grp, var = !!var, id = !!id)\n\n left_join(\n count(data, grp, var),\n count(data, grp, name = \"tot\"),\n by = \"grp\",\n ) %>%\n mutate(\n pct = fmt_num(100 * n / tot, digits = 1),\n n = fmt_num(n, digits = 0),\n npct = paste0(n, \" (\", pct, \")\")\n ) %>%\n pivot_wider(\n id_cols = var,\n names_from = grp,\n values_from = c(n, pct, npct),\n values_fill = list(n = \"0\", pct = fmt_num(0, digits = 0))\n ) %>%\n mutate(var_label = var_label)\n}\n\n\nt_pop <- adsl %>%\n filter(SAFFL == \"Y\") %>%\n count_by(\"TRT01AN\", \"SAFFL\",\n var_label = \"Participants in population\"\n ) %>%\n mutate(\n AESOC = \"pop\",\n AEDECOD = var_label\n ) %>%\n select(AESOC, AEDECOD, starts_with(\"n_\"))\n\nt_pop\n#> # A tibble: 1 × 5\n#> AESOC AEDECOD n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 pop Participants in population \" 86\" \" 84\" \" 84\"\n\nThe final report data is saved in tbl_ae_spec. We also add a blank row between population and AE information in the reporting table.\n\ntbl_ae_spec <- bind_rows(\n t_pop,\n data.frame(AESOC = \"pop\"),\n t_ae\n) %>%\n mutate(AEDECOD = ifelse(AEDECOD == AESOC,\n AEDECOD, paste0(\" \", AEDECOD)\n ))\n\ntbl_ae_spec %>% head(4)\n#> # A tibble: 4 × 5\n#> AESOC AEDECOD n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 pop \" Participants in population\" \" 86\" \" 84\" \" 84\"\n#> 2 pop <NA> <NA> <NA> <NA> \n#> 3 Cardiac Disorders \"Cardiac Disorders\" \" 13\" \" 13\" \" 18\"\n#> 4 Cardiac Disorders \" Atrial Fibrillation\" \" 1\" \" 1\" \" 3\"\n\nWe define the format of the output as below:\nTo obtain the nested layout, we use the page_by argument in the rtf_body function. By defining page_by=\"AESOC\", r2rtf recognizes the variable as a group indicator.\nAfter setting pageby_row = \"first_row\", the first row is displayed as group header. If a group of information is broken into multiple pages, the group header row is repeated on each page by default.\nWe can also customize the text format by providing a matrix that has the same dimension as the input dataset (i.e., tbl_ae_spec). In the code below, we illustrate how to display bold text for group headers to highlight the nested structure of the table layout.\n\nn_row <- nrow(tbl_ae_spec)\nn_col <- ncol(tbl_ae_spec)\nid <- tbl_ae_spec$AESOC == tbl_ae_spec$AEDECOD\nid <- ifelse(is.na(id), FALSE, id)\n\ntext_format <- ifelse(id, \"b\", \"\")\n\n\ntbl_ae_spec %>%\n rtf_title(\n \"Analysis of Participants With Specific Adverse Events\",\n \"(Safety Analysis Population)\"\n ) %>%\n rtf_colheader(\" | Placebo | Xanomeline Low Dose| Xanomeline High Dose\",\n col_rel_width = c(3, rep(1, 3))\n ) %>%\n rtf_colheader(\" | n | n | n \",\n border_top = \"\",\n border_bottom = \"single\",\n col_rel_width = c(3, rep(1, 3))\n ) %>%\n rtf_body(\n col_rel_width = c(1, 3, rep(1, 3)),\n text_justification = c(\"l\", \"l\", rep(\"c\", 3)),\n text_format = matrix(text_format, nrow = n_row, ncol = n_col),\n page_by = \"AESOC\",\n pageby_row = \"first_row\"\n ) %>%\n rtf_footnote(\"Every subject is counted a single time for each applicable row and column.\") %>%\n rtf_encode() %>%\n write_rtf(\"tlf/tlf_spec_ae.rtf\")\n\n\n\n\n\n\n\n\n\n\nMore discussion on page_by, group_by and subline_by features can be found on the r2rtf package website.\nThe procedure to generate a baseline characteristics table can be summarized as follows:\n\nStep 1: Read data (i.e., adae and adsl) into R.\nStep 2: Count the number of participants by SOC and treatment arm (rows with bold text) and save into t1.\nStep 3: Count the number of participants in each AE term by SOC, AE term, and treatment arm (rows without bold text) and save into t2.\nStep 4: Bind t1 and t2 by row into t_ae.\nStep 5: Count the number of participants in each arm as t_pop.\nStep 6: Row-wise combine t_pop and t_ae into tbl_ae_spec.\nStep 7: Format the output by using r2rtf.",
+ "text": "Following ICH E3 guidance, we need to summarize number of participants for each specific AE in Section 12.2, Adverse Events (AEs).\n\nlibrary(haven) # Read SAS data\nlibrary(dplyr) # Manipulate data\nlibrary(tidyr) # Manipulate data\nlibrary(r2rtf) # Reporting in RTF format\n\nIn this chapter, we illustrate how to summarize simplified specific AE information for a study.\n\n\n\n\n\n\n\n\n\nThe data used to summarize AE information is in adsl and adae datasets.\n\nadsl <- read_sas(\"data-adam/adsl.sas7bdat\")\nadae <- read_sas(\"data-adam/adae.sas7bdat\")\n\nFor illustration purpose, we only provide counts in the simplified table. The percentage of participants for each AE can be calculated as shown in Chapter 7.\nHere, we focus on the analysis script for two advanced features for a table layout.\n\ngroup content: AE can be summarized in multiple nested layers. (e.g., by system organ class (SOC, AESOC) and specific AE term (AEDECOD))\npagenization: there are many AE terms that can not be covered in one page. Column headers and SOC information need to be repeated on every page.\n\nIn the code below, we count the number of participants in each AE term by SOC and treatment arm, and we create a new variable order and set it as 0. The variable order can help with the data manipulation later.\n\nfmt_num <- function(x, digits, width = digits + 4) {\n formatC(\n x,\n digits = digits,\n format = \"f\",\n width = width\n )\n}\n\n\nana <- adae %>%\n mutate(\n AESOC = tools::toTitleCase(tolower(AESOC)),\n AEDECOD = tools::toTitleCase(tolower(AEDECOD))\n )\n\nt1 <- ana %>%\n group_by(TRTAN, AESOC) %>%\n summarise(n = fmt_num(n_distinct(USUBJID), digits = 0)) %>%\n mutate(AEDECOD = AESOC, order = 0)\n\nt1\n#> # A tibble: 61 × 5\n#> # Groups: TRTAN [3]\n#> TRTAN AESOC n AEDECOD order\n#> <dbl> <chr> <chr> <chr> <dbl>\n#> 1 0 Cardiac Disorders \" 1… Cardia… 0\n#> 2 0 Ear and Labyrinth Disorders \" … Ear an… 0\n#> 3 0 Eye Disorders \" … Eye Di… 0\n#> 4 0 Gastrointestinal Disorders \" 1… Gastro… 0\n#> 5 0 General Disorders and Administration Site Conditio… \" 2… Genera… 0\n#> 6 0 Hepatobiliary Disorders \" … Hepato… 0\n#> 7 0 Infections and Infestations \" 1… Infect… 0\n#> 8 0 Injury, Poisoning and Procedural Complications \" … Injury… 0\n#> 9 0 Investigations \" 1… Invest… 0\n#> 10 0 Metabolism and Nutrition Disorders \" … Metabo… 0\n#> # ℹ 51 more rows\n\nIn the code below, we count the number of subjects in each AE term by SOC, AE term, and treatment arm. Here we also create a new variable order and set it as 1.\n\nt2 <- ana %>%\n group_by(TRTAN, AESOC, AEDECOD) %>%\n summarise(n = fmt_num(n_distinct(USUBJID), digits = 0)) %>%\n mutate(order = 1)\n\nt2\n#> # A tibble: 373 × 5\n#> # Groups: TRTAN, AESOC [61]\n#> TRTAN AESOC AEDECOD n order\n#> <dbl> <chr> <chr> <chr> <dbl>\n#> 1 0 Cardiac Disorders Atrial Fibrillation \" 1\" 1\n#> 2 0 Cardiac Disorders Atrial Hypertrophy \" 1\" 1\n#> 3 0 Cardiac Disorders Atrioventricular Block First Degree \" 1\" 1\n#> 4 0 Cardiac Disorders Atrioventricular Block Second Degree \" 2\" 1\n#> 5 0 Cardiac Disorders Bradycardia \" 1\" 1\n#> 6 0 Cardiac Disorders Bundle Branch Block Left \" 1\" 1\n#> 7 0 Cardiac Disorders Bundle Branch Block Right \" 1\" 1\n#> 8 0 Cardiac Disorders Cardiac Failure Congestive \" 1\" 1\n#> 9 0 Cardiac Disorders Myocardial Infarction \" 4\" 1\n#> 10 0 Cardiac Disorders Sinus Arrhythmia \" 1\" 1\n#> # ℹ 363 more rows\n\nWe prepare reporting data for AE information using code below:\n\nt_ae <- bind_rows(t1, t2) %>%\n pivot_wider(\n id_cols = c(AESOC, order, AEDECOD),\n names_from = TRTAN,\n names_prefix = \"n_\",\n values_from = n,\n values_fill = fmt_num(0, digits = 0)\n ) %>%\n arrange(AESOC, order, AEDECOD) %>%\n select(AESOC, AEDECOD, starts_with(\"n\"))\n\nt_ae\n#> # A tibble: 265 × 5\n#> AESOC AEDECOD n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 Cardiac Disorders Cardiac Disorders \" 13\" \" 13\" \" 18\"\n#> 2 Cardiac Disorders Atrial Fibrillation \" 1\" \" 1\" \" 3\"\n#> 3 Cardiac Disorders Atrial Flutter \" 0\" \" 1\" \" 1\"\n#> 4 Cardiac Disorders Atrial Hypertrophy \" 1\" \" 0\" \" 0\"\n#> # ℹ 261 more rows\n\nWe prepare reporting data for analysis population using code below:\n\ncount_by <- function(data, # Input data set\n grp, # Group variable\n var, # Analysis variable\n var_label = var, # Analysis variable label\n id = \"USUBJID\") { # Subject ID variable\n data <- data %>% rename(grp = !!grp, var = !!var, id = !!id)\n\n left_join(\n count(data, grp, var),\n count(data, grp, name = \"tot\"),\n by = \"grp\",\n ) %>%\n mutate(\n pct = fmt_num(100 * n / tot, digits = 1),\n n = fmt_num(n, digits = 0),\n npct = paste0(n, \" (\", pct, \")\")\n ) %>%\n pivot_wider(\n id_cols = var,\n names_from = grp,\n values_from = c(n, pct, npct),\n values_fill = list(n = \"0\", pct = fmt_num(0, digits = 0))\n ) %>%\n mutate(var_label = var_label)\n}\n\n\nt_pop <- adsl %>%\n filter(SAFFL == \"Y\") %>%\n count_by(\"TRT01AN\", \"SAFFL\",\n var_label = \"Participants in population\"\n ) %>%\n mutate(\n AESOC = \"pop\",\n AEDECOD = var_label\n ) %>%\n select(AESOC, AEDECOD, starts_with(\"n_\"))\n\nt_pop\n#> # A tibble: 1 × 5\n#> AESOC AEDECOD n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 pop Participants in population \" 86\" \" 84\" \" 84\"\n\nThe final report data is saved in tbl_ae_spec. We also add a blank row between population and AE information in the reporting table.\n\ntbl_ae_spec <- bind_rows(\n t_pop,\n data.frame(AESOC = \"pop\"),\n t_ae\n) %>%\n mutate(AEDECOD = ifelse(AEDECOD == AESOC,\n AEDECOD, paste0(\" \", AEDECOD)\n ))\n\ntbl_ae_spec\n#> # A tibble: 267 × 5\n#> AESOC AEDECOD n_0 n_54 n_81 \n#> <chr> <chr> <chr> <chr> <chr> \n#> 1 pop \" Participants in population\" \" 86\" \" 84\" \" 84\"\n#> 2 pop <NA> <NA> <NA> <NA> \n#> 3 Cardiac Disorders \"Cardiac Disorders\" \" 13\" \" 13\" \" 18\"\n#> 4 Cardiac Disorders \" Atrial Fibrillation\" \" 1\" \" 1\" \" 3\"\n#> # ℹ 263 more rows\n\nWe define the format of the output as below:\nTo obtain the nested layout, we use the page_by argument in the rtf_body function. By defining page_by=\"AESOC\", r2rtf recognizes the variable as a group indicator.\nAfter setting pageby_row = \"first_row\", the first row is displayed as group header. If a group of information is broken into multiple pages, the group header row is repeated on each page by default.\nWe can also customize the text format by providing a matrix that has the same dimension as the input dataset (i.e., tbl_ae_spec). In the code below, we illustrate how to display bold text for group headers to highlight the nested structure of the table layout.\n\nn_row <- nrow(tbl_ae_spec)\nn_col <- ncol(tbl_ae_spec)\nid <- tbl_ae_spec$AESOC == tbl_ae_spec$AEDECOD\nid <- ifelse(is.na(id), FALSE, id)\n\ntext_format <- ifelse(id, \"b\", \"\")\n\n\ntbl_ae_spec %>%\n rtf_title(\n \"Analysis of Participants With Specific Adverse Events\",\n \"(Safety Analysis Population)\"\n ) %>%\n rtf_colheader(\" | Placebo | Xanomeline Low Dose| Xanomeline High Dose\",\n col_rel_width = c(3, rep(1, 3))\n ) %>%\n rtf_colheader(\" | n | n | n \",\n border_top = \"\",\n border_bottom = \"single\",\n col_rel_width = c(3, rep(1, 3))\n ) %>%\n rtf_body(\n col_rel_width = c(1, 3, rep(1, 3)),\n text_justification = c(\"l\", \"l\", rep(\"c\", 3)),\n text_format = matrix(text_format, nrow = n_row, ncol = n_col),\n page_by = \"AESOC\",\n pageby_row = \"first_row\"\n ) %>%\n rtf_footnote(\"Every subject is counted a single time for each applicable row and column.\") %>%\n rtf_encode() %>%\n write_rtf(\"tlf/tlf_spec_ae.rtf\")\n\n\n\n\n\n\n\n\n\n\nMore discussion on page_by, group_by and subline_by features can be found on the r2rtf package website.\nThe procedure to generate a baseline characteristics table can be summarized as follows:\n\nStep 1: Read data (i.e., adae and adsl) into R.\nStep 2: Count the number of participants by SOC and treatment arm (rows with bold text) and save into t1.\nStep 3: Count the number of participants in each AE term by SOC, AE term, and treatment arm (rows without bold text) and save into t2.\nStep 4: Bind t1 and t2 by row into t_ae.\nStep 5: Count the number of participants in each arm as t_pop.\nStep 6: Row-wise combine t_pop and t_ae into tbl_ae_spec.\nStep 7: Format the output by using r2rtf.",
"crumbs": [
"Delivering TLFs in CSR",
"8 Specific AE"
diff --git a/sitemap.xml b/sitemap.xml
index d8a37da..b728c5e 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -2,78 +2,78 @@
In the code below, we count the number of subjects in each AE term by SOC, AE term, and treatment arm. Here we also create a new variable order
and set it as 1
.
We prepare reporting data for AE information using code below:
We prepare reporting data for analysis population using code below:
We define the format of the output as below:
To obtain the nested layout, we use the page_by
argument in the rtf_body
function. By defining page_by="AESOC"
, r2rtf recognizes the variable as a group indicator.
We summarize the number and percentage of participants who meet each AE criteria.
We prepare reporting-ready dataset for each AE group.
We define the format of the output using code below:
We define the format of the output. We highlight items that are not discussed in previous discussion.
text_indent_first
and text_indent_left
are used to control the indent space of text. They are helpful when you need to control the white space of a long phrase, “AMERICAN INDIAN OR ALASKA NATIVE” in the table provides an example.
%>%
- adsl select(USUBJID, TRT01P, TRT01PN, DISCONFL, DCREASCD) %>%
- head(4)
- #> # A tibble: 4 × 5
-#> USUBJID TRT01P TRT01PN DISCONFL DCREASCD
-#> <chr> <chr> <dbl> <chr> <chr>
-#> 1 01-701-1015 Placebo 0 "" Completed
-#> 2 01-701-1023 Placebo 0 "Y" Adverse Event
-#> 3 01-701-1028 Xanomeline High Dose 81 "" Completed
-#> 4 01-701-1033 Xanomeline Low Dose 54 "Y" Sponsor Decision
%>% select(USUBJID, TRT01P, TRT01PN, DISCONFL, DCREASCD)
+ adsl #> # A tibble: 254 × 5
+#> USUBJID TRT01P TRT01PN DISCONFL DCREASCD
+#> <chr> <chr> <dbl> <chr> <chr>
+#> 1 01-701-1015 Placebo 0 "" Completed
+#> 2 01-701-1023 Placebo 0 "Y" Adverse Event
+#> 3 01-701-1028 Xanomeline High Dose 81 "" Completed
+#> 4 01-701-1033 Xanomeline Low Dose 54 "Y" Sponsor Decision
+#> # ℹ 250 more rows
In the code below, we calculate the number of participants in the analysis population by treatment arms.
In the code below, we calculate the number and percentage of participants who complete the study by treatment arms. We split n_reason
because we want to customize the row order of the table.
Now we combine individual rows into one table for reporting purpose. tbl_disp
is used as input for r2rtf to create final report.
In the below code, formatting of the final table is defined. Items that were not discussed in the previous sections, are highlighted below.
The rtf_title
defines table title. We can provide a vector for the title argument. Each value is a separate line. The format can also be controlled by providing a vector input in text format.
%>%
- ana select(USUBJID, TRTPN, AVISIT, AVAL, BASE, CHG) %>%
- head(4)
- #> # A tibble: 4 × 6
-#> USUBJID TRTPN AVISIT AVAL BASE CHG
-#> <chr> <dbl> <fct> <dbl> <dbl> <dbl>
-#> 1 01-701-1015 0 " Week 2" 4.66 4.72 -0.0555
-#> 2 01-701-1023 0 " Week 2" 5.77 5.33 0.444
-#> 3 01-701-1047 0 " Week 2" 5.55 5.55 0
-#> 4 01-701-1118 0 " Week 2" 4.88 4.05 0.833
%>% select(USUBJID, TRTPN, AVISIT, AVAL, BASE, CHG)
+ ana #> # A tibble: 1,377 × 6
+#> USUBJID TRTPN AVISIT AVAL BASE CHG
+#> <chr> <dbl> <fct> <dbl> <dbl> <dbl>
+#> 1 01-701-1015 0 " Week 2" 4.66 4.72 -0.0555
+#> 2 01-701-1023 0 " Week 2" 5.77 5.33 0.444
+#> 3 01-701-1047 0 " Week 2" 5.55 5.55 0
+#> 4 01-701-1118 0 " Week 2" 4.88 4.05 0.833
+#> # ℹ 1,373 more rows
For simplicity and illustration purpose, we use the last observation carried forward (LOCF) approach to handle missing data. LOCF approach is a single imputation approach that is not recommended in real application. Interested readers can find more discussion on missing data approaches in the book: The Prevention and Treatment of Missing Data in Clinical Trials.
|>
- r2rtf_adae select(USUBJID, TRTA, AEDECOD) |>
- head(4)
- #> USUBJID TRTA AEDECOD
-#> 1 01-701-1015 Placebo APPLICATION SITE ERYTHEMA
-#> 2 01-701-1015 Placebo APPLICATION SITE PRURITUS
-#> 3 01-701-1015 Placebo DIARRHOEA
-#> 4 01-701-1023 Placebo ERYTHEMA
|> select(USUBJID, TRTA, AEDECOD)
+ r2rtf_adae #> USUBJID TRTA AEDECOD
+#> 1 01-701-1015 Placebo APPLICATION SITE ERYTHEMA
+#> 2 01-701-1015 Placebo APPLICATION SITE PRURITUS
+#> 3 01-701-1015 Placebo DIARRHOEA
+#> 4 01-701-1023 Placebo ERYTHEMA
+#> # ℹ 1187 more rows
To manipulate the data and create a data frame containing the necessary information for the RTF table, we can use the dplyr and tidyr packages within the tidyverse.
Having prepared the dataset tbl
, we can now proceed with constructing the final RTF table using the r2rtf package. The r2rtf package has various functions, each designed for a specific type of table layout. Some commonly used verbs include:
head(tbl) |>
-rtf_body() |>
- rtf_encode() |>
- write_rtf("tlf/intro-ae1.rtf")
|>
+ tbl head() |>
+ rtf_body() |>
+ rtf_encode() |>
+ write_rtf("tlf/intro-ae1.rtf")
head(tbl) |>
-rtf_body(col_rel_width = c(3, 2, 2, 2)) |>
- rtf_encode() |>
- write_rtf("tlf/intro-ae2.rtf")
|>
+ tbl head() |>
+ rtf_body(col_rel_width = c(3, 2, 2, 2)) |>
+ rtf_encode() |>
+ write_rtf("tlf/intro-ae2.rtf")
head(tbl) |>
-rtf_colheader(
- colheader = "Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose",
- col_rel_width = c(3, 2, 2, 2)
- |>
- ) rtf_body(col_rel_width = c(3, 2, 2, 2)) |>
- rtf_encode() %>%
- write_rtf("tlf/intro-ae3.rtf")
|>
+ tbl head() |>
+ rtf_colheader(
+ colheader = "Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose",
+ col_rel_width = c(3, 2, 2, 2)
+ |>
+ ) rtf_body(col_rel_width = c(3, 2, 2, 2)) |>
+ rtf_encode() %>%
+ write_rtf("tlf/intro-ae3.rtf")
head(tbl) |>
-rtf_body(text_justification = c("l", "c", "c", "c")) |>
- rtf_encode() |>
- write_rtf("tlf/intro-ae5.rtf")
|>
+ tbl head() |>
+ rtf_body(text_justification = c("l", "c", "c", "c")) |>
+ rtf_encode() |>
+ write_rtf("tlf/intro-ae5.rtf")
head(tbl) %>%
-rtf_colheader(
- colheader = " | Treatment",
- col_rel_width = c(3, 6)
- |>
- ) rtf_colheader(
- colheader = "Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose",
- border_top = c("", "single", "single", "single"),
- col_rel_width = c(3, 2, 2, 2)
- |>
- ) rtf_body(col_rel_width = c(3, 2, 2, 2)) %>%
- rtf_encode() |>
- write_rtf("tlf/intro-ae7.rtf")
|>
+ tbl head() |>
+ rtf_colheader(
+ colheader = " | Treatment",
+ col_rel_width = c(3, 6)
+ |>
+ ) rtf_colheader(
+ colheader = "Adverse Events | Placebo | Xanomeline High Dose | Xanomeline Low Dose",
+ border_top = c("", "single", "single", "single"),
+ col_rel_width = c(3, 2, 2, 2)
+ |>
+ ) rtf_body(col_rel_width = c(3, 2, 2, 2)) %>%
+ rtf_encode() |>
+ write_rtf("tlf/intro-ae7.rtf")
%>%
- adsl select(USUBJID, ITTFL, EFFFL, SAFFL) %>%
- head(4)
- #> # A tibble: 4 × 4
-#> USUBJID ITTFL EFFFL SAFFL
-#> <chr> <chr> <chr> <chr>
-#> 1 01-701-1015 Y Y Y
-#> 2 01-701-1023 Y Y Y
-#> 3 01-701-1028 Y Y Y
-#> 4 01-701-1033 Y Y Y
%>% select(USUBJID, ITTFL, EFFFL, SAFFL)
+ adsl #> # A tibble: 254 × 4
+#> USUBJID ITTFL EFFFL SAFFL
+#> <chr> <chr> <chr> <chr>
+#> 1 01-701-1015 Y Y Y
+#> 2 01-701-1023 Y Y Y
+#> 3 01-701-1028 Y Y Y
+#> 4 01-701-1033 Y Y Y
+#> # ℹ 250 more rows