diff --git a/main/404.html b/main/404.html
index 9e935a330..9c7648f44 100644
--- a/main/404.html
+++ b/main/404.html
@@ -75,6 +75,18 @@
Changelog
+
+ Versions
+
diff --git a/main/LICENSE-text.html b/main/LICENSE-text.html
index e8fee769c..b45070b4d 100644
--- a/main/LICENSE-text.html
+++ b/main/LICENSE-text.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/articles/advanced_usage.html b/main/articles/advanced_usage.html
index 99a493ca1..6d27bff48 100644
--- a/main/articles/advanced_usage.html
+++ b/main/articles/advanced_usage.html
@@ -77,6 +77,18 @@
Changelog
+
+ Versions
+
diff --git a/main/articles/introduction.html b/main/articles/introduction.html
index 4579ee260..3c8a8c342 100644
--- a/main/articles/introduction.html
+++ b/main/articles/introduction.html
@@ -77,6 +77,18 @@
Changelog
+
+ Versions
+
diff --git a/main/index.html b/main/index.html
index 5e63c075a..bae6b17eb 100644
--- a/main/index.html
+++ b/main/index.html
@@ -95,6 +95,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/CellValue.html b/main/reference/CellValue.html
index bf38ae9f6..5b6838817 100644
--- a/main/reference/CellValue.html
+++ b/main/reference/CellValue.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/EmptyColInfo.html b/main/reference/EmptyColInfo.html
index 420335913..2238a5b5c 100644
--- a/main/reference/EmptyColInfo.html
+++ b/main/reference/EmptyColInfo.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/ManualSplit.html b/main/reference/ManualSplit.html
index 70b091b9c..be4123392 100644
--- a/main/reference/ManualSplit.html
+++ b/main/reference/ManualSplit.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/MultiVarSplit.html b/main/reference/MultiVarSplit.html
index 1c019827b..42ee427d8 100644
--- a/main/reference/MultiVarSplit.html
+++ b/main/reference/MultiVarSplit.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/VarLevelSplit.html b/main/reference/VarLevelSplit.html
index ff6146a03..adc34fe7c 100644
--- a/main/reference/VarLevelSplit.html
+++ b/main/reference/VarLevelSplit.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/Viewer.html b/main/reference/Viewer.html
index 01e48a24f..92642b240 100644
--- a/main/reference/Viewer.html
+++ b/main/reference/Viewer.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/add_colcounts.html b/main/reference/add_colcounts.html
index 979ba8016..717d0af6d 100644
--- a/main/reference/add_colcounts.html
+++ b/main/reference/add_colcounts.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/add_combo_facet.html b/main/reference/add_combo_facet.html
index 6ecd60961..822dead4c 100644
--- a/main/reference/add_combo_facet.html
+++ b/main/reference/add_combo_facet.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/add_combo_levels.html b/main/reference/add_combo_levels.html
index c703834f2..242c83009 100644
--- a/main/reference/add_combo_levels.html
+++ b/main/reference/add_combo_levels.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/add_existing_table.html b/main/reference/add_existing_table.html
index 44b92a057..a18bc92a3 100644
--- a/main/reference/add_existing_table.html
+++ b/main/reference/add_existing_table.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/add_overall_col.html b/main/reference/add_overall_col.html
index 622db500d..1123f5229 100644
--- a/main/reference/add_overall_col.html
+++ b/main/reference/add_overall_col.html
@@ -60,6 +60,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/add_overall_level.html b/main/reference/add_overall_level.html
index 50f22e925..03d1c1931 100644
--- a/main/reference/add_overall_level.html
+++ b/main/reference/add_overall_level.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/additional_fun_params.html b/main/reference/additional_fun_params.html
index 17988ef4c..b1ee784cc 100644
--- a/main/reference/additional_fun_params.html
+++ b/main/reference/additional_fun_params.html
@@ -60,6 +60,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/analyze.html b/main/reference/analyze.html
index c2a9ec99f..9603011c9 100644
--- a/main/reference/analyze.html
+++ b/main/reference/analyze.html
@@ -60,6 +60,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/analyze_colvars.html b/main/reference/analyze_colvars.html
index 875d4ad5c..522c16ee3 100644
--- a/main/reference/analyze_colvars.html
+++ b/main/reference/analyze_colvars.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/append_topleft.html b/main/reference/append_topleft.html
index 8848eabd3..1ff41e403 100644
--- a/main/reference/append_topleft.html
+++ b/main/reference/append_topleft.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/as_html.html b/main/reference/as_html.html
index ddb25aaa4..7689792ed 100644
--- a/main/reference/as_html.html
+++ b/main/reference/as_html.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/as_result_df.html b/main/reference/as_result_df.html
index cefe61ddb..09ae5f1a0 100644
--- a/main/reference/as_result_df.html
+++ b/main/reference/as_result_df.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/asvec.html b/main/reference/asvec.html
index 3c79253f0..e0f6ab9ff 100644
--- a/main/reference/asvec.html
+++ b/main/reference/asvec.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/avarspl.html b/main/reference/avarspl.html
index 678d1aa77..56547d123 100644
--- a/main/reference/avarspl.html
+++ b/main/reference/avarspl.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/basic_table.html b/main/reference/basic_table.html
index 8489a2aba..250ce4274 100644
--- a/main/reference/basic_table.html
+++ b/main/reference/basic_table.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/brackets.html b/main/reference/brackets.html
index 2a996c930..1b6909945 100644
--- a/main/reference/brackets.html
+++ b/main/reference/brackets.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/build_table.html b/main/reference/build_table.html
index 020b13fbf..66b017e92 100644
--- a/main/reference/build_table.html
+++ b/main/reference/build_table.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/cbind_rtables.html b/main/reference/cbind_rtables.html
index 34771cad4..ff4b799a0 100644
--- a/main/reference/cbind_rtables.html
+++ b/main/reference/cbind_rtables.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/cell_values.html b/main/reference/cell_values.html
index c8e1c6dde..37e6e18e7 100644
--- a/main/reference/cell_values.html
+++ b/main/reference/cell_values.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/cinfo.html b/main/reference/cinfo.html
index 499fbad82..3b828bc8d 100644
--- a/main/reference/cinfo.html
+++ b/main/reference/cinfo.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/clear_imods.html b/main/reference/clear_imods.html
index ba05a291f..3896b84a0 100644
--- a/main/reference/clear_imods.html
+++ b/main/reference/clear_imods.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/col_accessors.html b/main/reference/col_accessors.html
index 56610bc7c..43fcf7f43 100644
--- a/main/reference/col_accessors.html
+++ b/main/reference/col_accessors.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/collect_leaves.html b/main/reference/collect_leaves.html
index 344853b0e..149487619 100644
--- a/main/reference/collect_leaves.html
+++ b/main/reference/collect_leaves.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/compare_rtables.html b/main/reference/compare_rtables.html
index 35f2cfa5c..efd2d4bee 100644
--- a/main/reference/compare_rtables.html
+++ b/main/reference/compare_rtables.html
@@ -60,6 +60,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/compat_args.html b/main/reference/compat_args.html
index 9fd706bac..b47032359 100644
--- a/main/reference/compat_args.html
+++ b/main/reference/compat_args.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/constr_args.html b/main/reference/constr_args.html
index ddb7f9ec2..68163ece4 100644
--- a/main/reference/constr_args.html
+++ b/main/reference/constr_args.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/content_table.html b/main/reference/content_table.html
index 99d7243db..2cbce391b 100644
--- a/main/reference/content_table.html
+++ b/main/reference/content_table.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/counts_wpcts.html b/main/reference/counts_wpcts.html
index 52cda3cda..41c8e0441 100644
--- a/main/reference/counts_wpcts.html
+++ b/main/reference/counts_wpcts.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/custom_split_funs.html b/main/reference/custom_split_funs.html
index d14062681..eee151835 100644
--- a/main/reference/custom_split_funs.html
+++ b/main/reference/custom_split_funs.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/cutsplits.html b/main/reference/cutsplits.html
index 03dbaf591..b45ba2193 100644
--- a/main/reference/cutsplits.html
+++ b/main/reference/cutsplits.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/df_to_tt.html b/main/reference/df_to_tt.html
index 34a9e70e5..15592e9e2 100644
--- a/main/reference/df_to_tt.html
+++ b/main/reference/df_to_tt.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/dimensions.html b/main/reference/dimensions.html
index 0391e3d47..54f5b55dc 100644
--- a/main/reference/dimensions.html
+++ b/main/reference/dimensions.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/do_base_split.html b/main/reference/do_base_split.html
index b19a11550..36b1946d5 100644
--- a/main/reference/do_base_split.html
+++ b/main/reference/do_base_split.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/drop_facet_levels.html b/main/reference/drop_facet_levels.html
index c6038aaa0..9ecfa7911 100644
--- a/main/reference/drop_facet_levels.html
+++ b/main/reference/drop_facet_levels.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/export_as_docx.html b/main/reference/export_as_docx.html
index 7c502bc88..2aa24e183 100644
--- a/main/reference/export_as_docx.html
+++ b/main/reference/export_as_docx.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/export_as_pdf.html b/main/reference/export_as_pdf.html
index c4375039b..838123066 100644
--- a/main/reference/export_as_pdf.html
+++ b/main/reference/export_as_pdf.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/find_degen_struct.html b/main/reference/find_degen_struct.html
index d654e3929..06eb54fd5 100644
--- a/main/reference/find_degen_struct.html
+++ b/main/reference/find_degen_struct.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/format_rcell.html b/main/reference/format_rcell.html
index c9439b96e..76d64d5f9 100644
--- a/main/reference/format_rcell.html
+++ b/main/reference/format_rcell.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/formatters_methods.html b/main/reference/formatters_methods.html
index d9c7d5833..b36d56574 100644
--- a/main/reference/formatters_methods.html
+++ b/main/reference/formatters_methods.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/gen_args.html b/main/reference/gen_args.html
index 8cae59e59..e67ed910a 100644
--- a/main/reference/gen_args.html
+++ b/main/reference/gen_args.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/gfc.html b/main/reference/gfc.html
index 6aa3e564e..731a4a8df 100644
--- a/main/reference/gfc.html
+++ b/main/reference/gfc.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/head_tail.html b/main/reference/head_tail.html
index 3a1434c9b..4dc7cefef 100644
--- a/main/reference/head_tail.html
+++ b/main/reference/head_tail.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/horizontal_sep.html b/main/reference/horizontal_sep.html
index a65e6eb86..972e46ac4 100644
--- a/main/reference/horizontal_sep.html
+++ b/main/reference/horizontal_sep.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/in_rows.html b/main/reference/in_rows.html
index 33493720e..51b8009d4 100644
--- a/main/reference/in_rows.html
+++ b/main/reference/in_rows.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/indent.html b/main/reference/indent.html
index 50c0d5329..9062ee798 100644
--- a/main/reference/indent.html
+++ b/main/reference/indent.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/indent_string.html b/main/reference/indent_string.html
index 55da6602c..9e6e6f35b 100644
--- a/main/reference/indent_string.html
+++ b/main/reference/indent_string.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/index.html b/main/reference/index.html
index 1a5a32ad3..1ec698a8b 100644
--- a/main/reference/index.html
+++ b/main/reference/index.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/insert_row_at_path.html b/main/reference/insert_row_at_path.html
index 6334d7b80..eded55f71 100644
--- a/main/reference/insert_row_at_path.html
+++ b/main/reference/insert_row_at_path.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/insert_rrow.html b/main/reference/insert_rrow.html
index 1380dd4ef..d4af48558 100644
--- a/main/reference/insert_rrow.html
+++ b/main/reference/insert_rrow.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/int_methods.html b/main/reference/int_methods.html
index 48101205d..8867f4f6a 100644
--- a/main/reference/int_methods.html
+++ b/main/reference/int_methods.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/is_rtable.html b/main/reference/is_rtable.html
index 174e8a5d3..4eefc9110 100644
--- a/main/reference/is_rtable.html
+++ b/main/reference/is_rtable.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/label_at_path.html b/main/reference/label_at_path.html
index 2a599001d..d45f6c637 100644
--- a/main/reference/label_at_path.html
+++ b/main/reference/label_at_path.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/length-CellValue-method.html b/main/reference/length-CellValue-method.html
index 538aab8dc..44996165f 100644
--- a/main/reference/length-CellValue-method.html
+++ b/main/reference/length-CellValue-method.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/list_wrap.html b/main/reference/list_wrap.html
index d9347703b..7c9ddbd55 100644
--- a/main/reference/list_wrap.html
+++ b/main/reference/list_wrap.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/lyt_args.html b/main/reference/lyt_args.html
index e8b0bd0cf..ff740a0b0 100644
--- a/main/reference/lyt_args.html
+++ b/main/reference/lyt_args.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/make_afun.html b/main/reference/make_afun.html
index aa8f80fa0..cdd317cbe 100644
--- a/main/reference/make_afun.html
+++ b/main/reference/make_afun.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/make_col_df.html b/main/reference/make_col_df.html
index 569231c66..fe71aa723 100644
--- a/main/reference/make_col_df.html
+++ b/main/reference/make_col_df.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/make_col_row_df.html b/main/reference/make_col_row_df.html
index 2c08eeb85..7b059b25c 100644
--- a/main/reference/make_col_row_df.html
+++ b/main/reference/make_col_row_df.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/make_split_fun.html b/main/reference/make_split_fun.html
index f8de1ce69..924340842 100644
--- a/main/reference/make_split_fun.html
+++ b/main/reference/make_split_fun.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/make_split_result.html b/main/reference/make_split_result.html
index 8c5118e05..7dbc37974 100644
--- a/main/reference/make_split_result.html
+++ b/main/reference/make_split_result.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/manual_cols.html b/main/reference/manual_cols.html
index fa2dda0b8..71b3015c1 100644
--- a/main/reference/manual_cols.html
+++ b/main/reference/manual_cols.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/matrix_form-VTableTree-method.html b/main/reference/matrix_form-VTableTree-method.html
index ef4e53e08..435a13272 100644
--- a/main/reference/matrix_form-VTableTree-method.html
+++ b/main/reference/matrix_form-VTableTree-method.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/names.html b/main/reference/names.html
index f1423123d..02505bb49 100644
--- a/main/reference/names.html
+++ b/main/reference/names.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/no_info.html b/main/reference/no_info.html
index 97b2cbff5..8ed60f777 100644
--- a/main/reference/no_info.html
+++ b/main/reference/no_info.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/paginate.html b/main/reference/paginate.html
index 933386bf3..6acc945e8 100644
--- a/main/reference/paginate.html
+++ b/main/reference/paginate.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/path_enriched_df.html b/main/reference/path_enriched_df.html
index 209794021..7b568fbf6 100644
--- a/main/reference/path_enriched_df.html
+++ b/main/reference/path_enriched_df.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/prune_table.html b/main/reference/prune_table.html
index 7d3c24887..b4c494b3d 100644
--- a/main/reference/prune_table.html
+++ b/main/reference/prune_table.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/qtable_layout.html b/main/reference/qtable_layout.html
index 302577747..3e135c5d7 100644
--- a/main/reference/qtable_layout.html
+++ b/main/reference/qtable_layout.html
@@ -60,6 +60,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rbind.html b/main/reference/rbind.html
index 89ab31cb7..e14864ed9 100644
--- a/main/reference/rbind.html
+++ b/main/reference/rbind.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rcell.html b/main/reference/rcell.html
index d4736715b..3f769a9bf 100644
--- a/main/reference/rcell.html
+++ b/main/reference/rcell.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/reexports.html b/main/reference/reexports.html
index 951c0065c..1f6940027 100644
--- a/main/reference/reexports.html
+++ b/main/reference/reexports.html
@@ -66,6 +66,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/ref_fnotes.html b/main/reference/ref_fnotes.html
index 681a7f657..3ca953837 100644
--- a/main/reference/ref_fnotes.html
+++ b/main/reference/ref_fnotes.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/result_df_specs.html b/main/reference/result_df_specs.html
index b56724b5a..8b50ce97e 100644
--- a/main/reference/result_df_specs.html
+++ b/main/reference/result_df_specs.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rheader.html b/main/reference/rheader.html
index 9c7b5deac..671721b6a 100644
--- a/main/reference/rheader.html
+++ b/main/reference/rheader.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/row_accessors.html b/main/reference/row_accessors.html
index 1352a0171..bce8137aa 100644
--- a/main/reference/row_accessors.html
+++ b/main/reference/row_accessors.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/row_paths_summary.html b/main/reference/row_paths_summary.html
index c12bab55d..348442a1a 100644
--- a/main/reference/row_paths_summary.html
+++ b/main/reference/row_paths_summary.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rowclasses.html b/main/reference/rowclasses.html
index f4c12398a..47a841315 100644
--- a/main/reference/rowclasses.html
+++ b/main/reference/rowclasses.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rrow.html b/main/reference/rrow.html
index df5c9dca6..178a87ee5 100644
--- a/main/reference/rrow.html
+++ b/main/reference/rrow.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rrowl.html b/main/reference/rrowl.html
index ebdd3d789..5d504621d 100644
--- a/main/reference/rrowl.html
+++ b/main/reference/rrowl.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rtable.html b/main/reference/rtable.html
index 7b4fa010b..092cc749a 100644
--- a/main/reference/rtable.html
+++ b/main/reference/rtable.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/rtinner.html b/main/reference/rtinner.html
index bfe67b59e..a78149f19 100644
--- a/main/reference/rtinner.html
+++ b/main/reference/rtinner.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/sanitize_table_struct.html b/main/reference/sanitize_table_struct.html
index 613ee70aa..d18aa884d 100644
--- a/main/reference/sanitize_table_struct.html
+++ b/main/reference/sanitize_table_struct.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/score_funs.html b/main/reference/score_funs.html
index 0a38b8002..cc73d450c 100644
--- a/main/reference/score_funs.html
+++ b/main/reference/score_funs.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/section_div.html b/main/reference/section_div.html
index d9d2f233c..e9d64b572 100644
--- a/main/reference/section_div.html
+++ b/main/reference/section_div.html
@@ -62,6 +62,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/sf_args.html b/main/reference/sf_args.html
index 54fcaf169..12706a198 100644
--- a/main/reference/sf_args.html
+++ b/main/reference/sf_args.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/sort_at_path.html b/main/reference/sort_at_path.html
index af727bb06..600e140e0 100644
--- a/main/reference/sort_at_path.html
+++ b/main/reference/sort_at_path.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/spl_context.html b/main/reference/spl_context.html
index 6b0fe0b4d..3c294eb85 100644
--- a/main/reference/spl_context.html
+++ b/main/reference/spl_context.html
@@ -58,6 +58,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/spl_context_to_disp_path.html b/main/reference/spl_context_to_disp_path.html
index 34a68b160..afc0e7344 100644
--- a/main/reference/spl_context_to_disp_path.html
+++ b/main/reference/spl_context_to_disp_path.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/spl_variable.html b/main/reference/spl_variable.html
index bd3a840d5..cfa434ce4 100644
--- a/main/reference/spl_variable.html
+++ b/main/reference/spl_variable.html
@@ -66,6 +66,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/split_cols_by.html b/main/reference/split_cols_by.html
index 9c56f2026..42ea80445 100644
--- a/main/reference/split_cols_by.html
+++ b/main/reference/split_cols_by.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/split_cols_by_multivar.html b/main/reference/split_cols_by_multivar.html
index c7e6f3299..09711c3f9 100644
--- a/main/reference/split_cols_by_multivar.html
+++ b/main/reference/split_cols_by_multivar.html
@@ -58,6 +58,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/split_funcs.html b/main/reference/split_funcs.html
index e049a5325..9a4a49b05 100644
--- a/main/reference/split_funcs.html
+++ b/main/reference/split_funcs.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/split_rows_by.html b/main/reference/split_rows_by.html
index d9638c1e3..3e4e59f9c 100644
--- a/main/reference/split_rows_by.html
+++ b/main/reference/split_rows_by.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/split_rows_by_multivar.html b/main/reference/split_rows_by_multivar.html
index 80b94f40b..af482cf28 100644
--- a/main/reference/split_rows_by_multivar.html
+++ b/main/reference/split_rows_by_multivar.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/summarize_row_groups.html b/main/reference/summarize_row_groups.html
index cf4f33982..89822e609 100644
--- a/main/reference/summarize_row_groups.html
+++ b/main/reference/summarize_row_groups.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/summarize_rows.html b/main/reference/summarize_rows.html
index 5f481f3ac..dc4ee1575 100644
--- a/main/reference/summarize_rows.html
+++ b/main/reference/summarize_rows.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/tabclasses.html b/main/reference/tabclasses.html
index 80221272f..5e6337a36 100644
--- a/main/reference/tabclasses.html
+++ b/main/reference/tabclasses.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/table_shell.html b/main/reference/table_shell.html
index 8048385a6..731c7253f 100644
--- a/main/reference/table_shell.html
+++ b/main/reference/table_shell.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/table_structure.html b/main/reference/table_structure.html
index 6d1e7cac5..787c4026f 100644
--- a/main/reference/table_structure.html
+++ b/main/reference/table_structure.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/top_left.html b/main/reference/top_left.html
index c5eb04e65..66f7714fa 100644
--- a/main/reference/top_left.html
+++ b/main/reference/top_left.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/tostring.html b/main/reference/tostring.html
index bf57fd7c3..5fe726918 100644
--- a/main/reference/tostring.html
+++ b/main/reference/tostring.html
@@ -62,6 +62,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/tree_children.html b/main/reference/tree_children.html
index 54170f953..77cf39531 100644
--- a/main/reference/tree_children.html
+++ b/main/reference/tree_children.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/trim_levels_in_facets.html b/main/reference/trim_levels_in_facets.html
index f3c586c1e..6eaf4c22b 100644
--- a/main/reference/trim_levels_in_facets.html
+++ b/main/reference/trim_levels_in_facets.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/trim_levels_to_map.html b/main/reference/trim_levels_to_map.html
index 4f93b6f00..0b0b504c0 100644
--- a/main/reference/trim_levels_to_map.html
+++ b/main/reference/trim_levels_to_map.html
@@ -58,6 +58,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/trim_prune_funs.html b/main/reference/trim_prune_funs.html
index 52f757cb0..21933019d 100644
--- a/main/reference/trim_prune_funs.html
+++ b/main/reference/trim_prune_funs.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/trim_rows.html b/main/reference/trim_rows.html
index 4de2fc8db..687f3a1f0 100644
--- a/main/reference/trim_rows.html
+++ b/main/reference/trim_rows.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/trim_zero_rows.html b/main/reference/trim_zero_rows.html
index 1f9ebd704..92d2ef517 100644
--- a/main/reference/trim_zero_rows.html
+++ b/main/reference/trim_zero_rows.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/tsv_io.html b/main/reference/tsv_io.html
index f4bcfba7e..b91127110 100644
--- a/main/reference/tsv_io.html
+++ b/main/reference/tsv_io.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/tt_to_flextable.html b/main/reference/tt_to_flextable.html
index c2cbd4352..89a4d7bff 100644
--- a/main/reference/tt_to_flextable.html
+++ b/main/reference/tt_to_flextable.html
@@ -56,6 +56,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/ttap.html b/main/reference/ttap.html
index d036b44f9..a1495a4d1 100644
--- a/main/reference/ttap.html
+++ b/main/reference/ttap.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/update_ref_indexing.html b/main/reference/update_ref_indexing.html
index fc1a08ecc..de6376510 100644
--- a/main/reference/update_ref_indexing.html
+++ b/main/reference/update_ref_indexing.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/validate_table_struct.html b/main/reference/validate_table_struct.html
index b559f07ab..d0d7ef107 100644
--- a/main/reference/validate_table_struct.html
+++ b/main/reference/validate_table_struct.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/value_formats.html b/main/reference/value_formats.html
index 7ba153b74..9c3aa1cd5 100644
--- a/main/reference/value_formats.html
+++ b/main/reference/value_formats.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/varcuts.html b/main/reference/varcuts.html
index f46a4100f..ae1008774 100644
--- a/main/reference/varcuts.html
+++ b/main/reference/varcuts.html
@@ -54,6 +54,18 @@
Changelog
+
+ Versions
+
diff --git a/main/reference/vil.html b/main/reference/vil.html
index ca8a69941..08fd83ebf 100644
--- a/main/reference/vil.html
+++ b/main/reference/vil.html
@@ -52,6 +52,18 @@
Changelog
+
+ Versions
+
diff --git a/main/search.json b/main/search.json
index 3341d6e59..27939f220 100644
--- a/main/search.json
+++ b/main/search.json
@@ -1 +1 @@
-[{"path":"https://insightsengineering.github.io/rtables/ISSUE_TEMPLATE.html","id":null,"dir":"","previous_headings":"","what":"Reporting an Issue with rtables","title":"Reporting an Issue with rtables","text":"Please briefly describe problem , relevant, output expect. Please also provide output utils::sessionInfo() devtools::session_info() end post. possible, please include minimal, reproducible example. rtables team much likely resolve issue able reproduce locally. Please delete preamble read . brief description problem","code":"library(rtables) # your reproducible example here"},{"path":"https://insightsengineering.github.io/rtables/articles/advanced_usage.html","id":"note","dir":"Articles","previous_headings":"","what":"NOTE","title":"rtables Advanced Usage","text":"vignette currently development. code prose appears version vignette main branch repository work/correct, likely final form. Initialization","code":"library(rtables)"},{"path":"https://insightsengineering.github.io/rtables/articles/advanced_usage.html","id":"control-splitting-with-provided-function-limited-customization","dir":"Articles","previous_headings":"","what":"Control splitting with provided function (limited customization)","title":"rtables Advanced Usage","text":"rtables provides array functions control splitting logic without creating entirely new split functions. default split_*_by facets data based categorical variable. continuous variables, split_*_by_cutfun can leveraged create categories corresponding faceting, break points dependent data. Alternatively, split_*_by_cuts can used breakpoints predefined split_*_by_quartiles data faceted quantile.","code":"d1 <- subset(ex_adsl, AGE < 25) d1$AGE <- as.factor(d1$AGE) lyt1 <- basic_table() %>% split_cols_by(\"AGE\") %>% analyze(\"SEX\") build_table(lyt1, d1) ## 20 21 23 24 ## ———————————————————————————————————— ## F 0 2 4 5 ## M 1 1 2 3 ## U 0 0 0 0 ## UNDIFFERENTIATED 0 0 0 0 sd_cutfun <- function(x) { cutpoints <- c( min(x), mean(x) - sd(x), mean(x) + sd(x), max(x) ) names(cutpoints) <- c(\"\", \"Low\", \"Medium\", \"High\") cutpoints } lyt1 <- basic_table() %>% split_cols_by_cutfun(\"AGE\", cutfun = sd_cutfun) %>% analyze(\"SEX\") build_table(lyt1, ex_adsl) ## Low Medium High ## —————————————————————————————————————— ## F 36 165 21 ## M 21 115 30 ## U 1 8 0 ## UNDIFFERENTIATED 0 1 2 lyt1 <- basic_table() %>% split_cols_by_cuts( \"AGE\", cuts = c(0, 30, 60, 100), cutlabels = c(\"0-30 y.o.\", \"30-60 y.o.\", \"60-100 y.o.\") ) %>% analyze(\"SEX\") build_table(lyt1, ex_adsl) ## 0-30 y.o. 30-60 y.o. 60-100 y.o. ## ——————————————————————————————————————————————————————— ## F 71 150 1 ## M 48 116 2 ## U 2 7 0 ## UNDIFFERENTIATED 1 2 0"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/advanced_usage.html","id":"adding-an-overall-column-only-when-the-split-would-already-define-2-facets","dir":"Articles","previous_headings":"Custom Split Functions","what":"Adding an Overall Column Only When The Split Would Already Define 2+ Facets","title":"rtables Advanced Usage","text":"custom split functions can anything, including conditionally applying one existing custom split functions. define function constructor accepts variable name want check, return custom split function behavior want using functions provided rtables cases: gives us desired behavior one column corner case: standard multi-column case: Notice use add_overall_level function constructor, immediately call constructed function --one-columns case.","code":"picky_splitter <- function(var) { function(df, spl, vals, labels, trim) { orig_vals <- vals if (is.null(vals)) { vec <- df[[var]] vals <- if (is.factor(vec)) levels(vec) else unique(vec) } if (length(vals) == 1) { do_base_split(spl = spl, df = df, vals = vals, labels = labels, trim = trim) } else { add_overall_level( \"Overall\", label = \"All Obs\", first = FALSE )(df = df, spl = spl, vals = orig_vals, trim = trim) } } } d1 <- subset(ex_adsl, ARM == \"A: Drug X\") d1$ARM <- factor(d1$ARM) lyt1 <- basic_table() %>% split_cols_by(\"ARM\", split_fun = picky_splitter(\"ARM\")) %>% analyze(\"AGE\") build_table(lyt1, d1) ## A: Drug X ## ———————————————— ## Mean 33.77 build_table(lyt1, ex_adsl) ## A: Drug X B: Placebo C: Combination All Obs ## ———————————————————————————————————————————————————————— ## Mean 33.77 35.43 35.43 34.88"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/advanced_usage.html","id":"what-is--spl_context","dir":"Articles","previous_headings":"Leveraging .spl_context","what":"What Is .spl_context?","title":"rtables Advanced Usage","text":".spl_context (see ?spl_context) mechanism rtables tabulation machinery gives custom split, analysis content (row-group summary) functions information overarching facet-structure splits cells generate reside . particular .spl_context ensures functions know (thus computations based ) following types information:","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/advanced_usage.html","id":"different-formats-for-different-values-within-a-row-split","dir":"Articles","previous_headings":"Leveraging .spl_context","what":"Different Formats For Different Values Within A Row-Split","title":"rtables Advanced Usage","text":"","code":"dta_test <- data.frame( USUBJID = rep(1:6, each = 3), PARAMCD = rep(\"lab\", 6 * 3), AVISIT = rep(paste0(\"V\", 1:3), 6), ARM = rep(LETTERS[1:3], rep(6, 3)), AVAL = c(9:1, rep(NA, 9)), CHG = c(1:9, rep(NA, 9)) ) my_afun <- function(x, .spl_context) { n <- sum(!is.na(x)) meanval <- mean(x, na.rm = TRUE) sdval <- sd(x, na.rm = TRUE) ## get the split value of the most recent parent ## (row) split above this analyze val <- .spl_context[nrow(.spl_context), \"value\"] ## do a silly thing to decide the different format precisiosn ## your real logic would go here valnum <- min(2L, as.integer(gsub(\"[^[:digit:]]*\", \"\", val))) fstringpt <- paste0(\"xx.\", strrep(\"x\", valnum)) fmt_mnsd <- sprintf(\"%s (%s)\", fstringpt, fstringpt) in_rows( n = n, \"Mean, SD\" = c(meanval, sdval), .formats = c(n = \"xx\", \"Mean, SD\" = fmt_mnsd) ) } lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"AVISIT\") %>% split_cols_by_multivar(vars = c(\"AVAL\", \"CHG\")) %>% analyze_colvars(my_afun) build_table(lyt, dta_test) ## A B C ## AVAL CHG AVAL CHG AVAL CHG ## ——————————————————————————————————————————————————————————————————————————— ## V1 ## n 2 2 1 1 0 0 ## Mean, SD 7.5 (2.1) 2.5 (2.1) 3.0 (NA) 7.0 (NA) NA NA ## V2 ## n 2 2 1 1 0 0 ## Mean, SD 6.50 (2.12) 3.50 (2.12) 2.00 (NA) 8.00 (NA) NA NA ## V3 ## n 2 2 1 1 0 0 ## Mean, SD 5.50 (2.12) 4.50 (2.12) 1.00 (NA) 9.00 (NA) NA NA"},{"path":"https://insightsengineering.github.io/rtables/articles/advanced_usage.html","id":"simulating-baseline-comparison-in-row-space","dir":"Articles","previous_headings":"Leveraging .spl_context","what":"Simulating ‘Baseline Comparison’ In Row Space","title":"rtables Advanced Usage","text":"can simulate formal modeling reference row(s) using extra_args machinery","code":"my_afun <- function(x, .var, .spl_context) { n <- sum(!is.na(x)) meanval <- mean(x, na.rm = TRUE) sdval <- sd(x, na.rm = TRUE) ## get the split value of the most recent parent ## (row) split above this analyze val <- .spl_context[nrow(.spl_context), \"value\"] ## we show it if its not a CHG within V1 show_it <- val != \"V1\" || .var != \"CHG\" ## do a silly thing to decide the different format precisiosn ## your real logic would go here valnum <- min(2L, as.integer(gsub(\"[^[:digit:]]*\", \"\", val))) fstringpt <- paste0(\"xx.\", strrep(\"x\", valnum)) fmt_mnsd <- if (show_it) sprintf(\"%s (%s)\", fstringpt, fstringpt) else \"xx\" in_rows( n = if (show_it) n, ## NULL otherwise \"Mean, SD\" = if (show_it) c(meanval, sdval), ## NULL otherwise .formats = c(n = \"xx\", \"Mean, SD\" = fmt_mnsd) ) } lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"AVISIT\") %>% split_cols_by_multivar(vars = c(\"AVAL\", \"CHG\")) %>% analyze_colvars(my_afun) build_table(lyt, dta_test) ## A B C ## AVAL CHG AVAL CHG AVAL CHG ## ——————————————————————————————————————————————————————————————————————————— ## V1 ## n 2 1 0 ## Mean, SD 7.5 (2.1) 3.0 (NA) NA ## V2 ## n 2 2 1 1 0 0 ## Mean, SD 6.50 (2.12) 3.50 (2.12) 2.00 (NA) 8.00 (NA) NA NA ## V3 ## n 2 2 1 1 0 0 ## Mean, SD 5.50 (2.12) 4.50 (2.12) 1.00 (NA) 9.00 (NA) NA NA my_afun <- function(x, .var, ref_rowgroup, .spl_context) { n <- sum(!is.na(x)) meanval <- mean(x, na.rm = TRUE) sdval <- sd(x, na.rm = TRUE) ## get the split value of the most recent parent ## (row) split above this analyze val <- .spl_context[nrow(.spl_context), \"value\"] ## we show it if its not a CHG within V1 show_it <- val != ref_rowgroup || .var != \"CHG\" fmt_mnsd <- if (show_it) \"xx.x (xx.x)\" else \"xx\" in_rows( n = if (show_it) n, ## NULL otherwise \"Mean, SD\" = if (show_it) c(meanval, sdval), ## NULL otherwise .formats = c(n = \"xx\", \"Mean, SD\" = fmt_mnsd) ) } lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"AVISIT\") %>% split_cols_by_multivar(vars = c(\"AVAL\", \"CHG\")) %>% analyze_colvars(my_afun, extra_args = list(ref_rowgroup = \"V1\")) build_table(lyt2, dta_test) ## A B C ## AVAL CHG AVAL CHG AVAL CHG ## ————————————————————————————————————————————————————————————————————— ## V1 ## n 2 1 0 ## Mean, SD 7.5 (2.1) 3.0 (NA) NA ## V2 ## n 2 2 1 1 0 0 ## Mean, SD 6.5 (2.1) 3.5 (2.1) 2.0 (NA) 8.0 (NA) NA NA ## V3 ## n 2 2 1 1 0 0 ## Mean, SD 5.5 (2.1) 4.5 (2.1) 1.0 (NA) 9.0 (NA) NA NA"},{"path":"https://insightsengineering.github.io/rtables/articles/baseline.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Comparing Against Baselines or Control","text":"Often data one column considered reference/baseline/comparison group compared data columns. example, lets calculate average age: difference average AGE placebo arm arms: Note column order changed reference group displayed first column. cases want cells blank reference column, (e.g., “B: Placebo”) use non_ref_rcell() instead rcell(), pass .in_ref_col second argument: can see arguments available afun manual analyze().","code":"library(rtables) lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(\"AGE\") tbl <- build_table(lyt, DM) tbl # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 34.91 33.02 34.57 lyt2 <- basic_table() %>% split_cols_by(\"ARM\", ref_group = \"B: Placebo\") %>% analyze(\"AGE\", afun = function(x, .ref_group) { in_rows( \"Difference of Averages\" = rcell(mean(x) - mean(.ref_group), format = \"xx.xx\") ) }) tbl2 <- build_table(lyt2, DM) tbl2 # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————— # Difference of Averages 1.89 0.00 1.55 lyt3 <- basic_table() %>% split_cols_by(\"ARM\", ref_group = \"B: Placebo\") %>% analyze( \"AGE\", afun = function(x, .ref_group, .in_ref_col) { in_rows( \"Difference of Averages\" = non_ref_rcell(mean(x) - mean(.ref_group), is_ref = .in_ref_col, format = \"xx.xx\") ) } ) tbl3 <- build_table(lyt3, DM) tbl3 # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————— # Difference of Averages 1.89 1.55 lyt4 <- basic_table() %>% split_cols_by(\"ARM\", ref_group = \"B: Placebo\") %>% analyze( \"AGE\", afun = function(x, .ref_group, .in_ref_col) { in_rows( \"Difference of Averages\" = non_ref_rcell(mean(x) - mean(.ref_group), is_ref = .in_ref_col, format = \"xx.xx\"), \"another row\" = non_ref_rcell(\"aaa\", .in_ref_col) ) } ) tbl4 <- build_table(lyt4, DM) tbl4 # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————— # Difference of Averages 1.89 1.55 # another row aaa aaa"},{"path":"https://insightsengineering.github.io/rtables/articles/baseline.html","id":"row-splitting","dir":"Articles","previous_headings":"","what":"Row Splitting","title":"Comparing Against Baselines or Control","text":"adding row-splitting reference data may represented column without row splitting. example: data assigned .ref_full full data reference column whereas data assigned .ref_group respects subsetting defined row-splitting hence subset argument x df afun.","code":"lyt5 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\", ref_group = \"B: Placebo\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% analyze(\"AGE\", afun = function(x, .ref_group, .ref_full, .in_ref_col) { in_rows( \"is reference (.in_ref_col)\" = rcell(.in_ref_col), \"ref cell N (.ref_group)\" = rcell(length(.ref_group)), \"ref column N (.ref_full)\" = rcell(length(.ref_full)) ) }) tbl5 <- build_table(lyt5, subset(DM, SEX %in% c(\"M\", \"F\"))) tbl5 # A: Drug X B: Placebo C: Combination # (N=121) (N=106) (N=129) # —————————————————————————————————————————————————————————————————————— # F # is reference (.in_ref_col) FALSE TRUE FALSE # ref cell N (.ref_group) 56 56 56 # ref column N (.ref_full) 106 106 106 # M # is reference (.in_ref_col) FALSE TRUE FALSE # ref cell N (.ref_group) 50 50 50 # ref column N (.ref_full) 106 106 106"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Example Clinical Trials Tables","text":"vignette create demographic table adverse event table response table time--event analysis table using rtables layout facility. , demonstrate layout based tabulation framework can specify structure relations commonly found analyzing clinical trials data. Note data created using random number generators. ex_* data currently attached rtables package provided formatters package created using publicly available random.cdisc.data R package. packages used vignette :","code":"library(rtables) library(tibble) library(dplyr)"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"demographic-table","dir":"Articles","previous_headings":"","what":"Demographic Table","title":"Example Clinical Trials Tables","text":"Demographic tables summarize variables content different population subsets (encoded columns). One feature analyze() introduced previous vignette analysis function afun can specify multiple rows in_rows() function: Multiple variables can analyzed one analyze() call: Hence, afun can process different data vector types (.e. variables selected data) fairly close standard demographic table. function either creates count table number summary argument x factor numeric, respectively: Note use rcell wrap results order add formatting instructions rtables. can use s_summary outside context tabulation: can now create commonly used variant demographic table: Note analyze() can also called multiple times sequence: leads table identical summary_tbl: clinical trials analyses number patients per column often referred N (rather overall population outside clinical trials commonly referred N). Column Ns added setting show_colcounts argument basic_table() TRUE:","code":"ADSL <- ex_adsl # Example ADSL dataset lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = \"AGE\", afun = function(x) { in_rows( \"Mean (sd)\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\"), \"Range\" = rcell(range(x), format = \"xx.xx - xx.xx\") ) }) tbl <- build_table(lyt, ADSL) tbl # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # Range 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = c(\"AGE\", \"BMRKR1\"), afun = function(x) { in_rows( \"Mean (sd)\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\"), \"Range\" = rcell(range(x), format = \"xx.xx - xx.xx\") ) }) tbl2 <- build_table(lyt2, ADSL) tbl2 # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————— # AGE # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # Range 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR1 # Mean (sd) 5.97 (3.55) 5.70 (3.31) 5.62 (3.49) # Range 0.41 - 17.67 0.65 - 14.24 0.17 - 21.39 s_summary <- function(x) { if (is.numeric(x)) { in_rows( \"n\" = rcell(sum(!is.na(x)), format = \"xx\"), \"Mean (sd)\" = rcell(c(mean(x, na.rm = TRUE), sd(x, na.rm = TRUE)), format = \"xx.xx (xx.xx)\"), \"IQR\" = rcell(IQR(x, na.rm = TRUE), format = \"xx.xx\"), \"min - max\" = rcell(range(x, na.rm = TRUE), format = \"xx.xx - xx.xx\") ) } else if (is.factor(x)) { vs <- as.list(table(x)) do.call(in_rows, lapply(vs, rcell, format = \"xx\")) } else { stop(\"type not supported\") } } s_summary(ADSL$AGE) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod row_label # 1 n 400 0 n # 2 Mean (sd) 34.88 (7.44) 0 Mean (sd) # 3 IQR 10.00 0 IQR # 4 min - max 20.00 - 69.00 0 min - max s_summary(ADSL$SEX) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod row_label # 1 F 222 0 F # 2 M 166 0 M # 3 U 9 0 U # 4 UNDIFFERENTIATED 3 0 UNDIFFERENTIATED summary_lyt <- basic_table() %>% split_cols_by(var = \"ARM\") %>% analyze(c(\"AGE\", \"SEX\"), afun = s_summary) summary_tbl <- build_table(summary_lyt, ADSL) summary_tbl # A: Drug X B: Placebo C: Combination # ——————————————————————————————————————————————————————————————————— # AGE # n 134 134 132 # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # IQR 11.00 10.00 10.00 # min - max 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # SEX # F 79 77 66 # M 51 55 60 # U 3 2 4 # UNDIFFERENTIATED 1 0 2 summary_lyt2 <- basic_table() %>% split_cols_by(var = \"ARM\") %>% analyze(\"AGE\", s_summary) %>% analyze(\"SEX\", s_summary) summary_tbl2 <- build_table(summary_lyt2, ADSL) summary_tbl2 # A: Drug X B: Placebo C: Combination # ——————————————————————————————————————————————————————————————————— # AGE # n 134 134 132 # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # IQR 11.00 10.00 10.00 # min - max 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # SEX # F 79 77 66 # M 51 55 60 # U 3 2 4 # UNDIFFERENTIATED 1 0 2 identical(summary_tbl, summary_tbl2) # [1] TRUE summary_lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARMCD\") %>% analyze(c(\"AGE\", \"SEX\"), s_summary) summary_tbl3 <- build_table(summary_lyt3, ADSL) summary_tbl3 # ARM A ARM B ARM C # (N=134) (N=134) (N=132) # —————————————————————————————————————————————————————————————————— # AGE # n 134 134 132 # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # IQR 11.00 10.00 10.00 # min - max 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # SEX # F 79 77 66 # M 51 55 60 # U 3 2 4 # UNDIFFERENTIATED 1 0 2"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"variations-on-the-demographic-table","dir":"Articles","previous_headings":"Demographic Table","what":"Variations on the Demographic Table","title":"Example Clinical Trials Tables","text":"now show couple variations demographic table developed . variations structure analysis, hence don’t require modification s_summary function. start standard table analyzing variables AGE BMRKR2 variables: Assume like analysis carried per gender encoded row space: now subset ADSL include males females analysis order reduce number rows table: Note UNDIFFERENTIATED U levels still show table. tabulation respects factor levels level order, exactly split table function . empty levels dropped rtables needs know splitting time via split_fun argument split_rows_by(). number predefined functions. example drop_split_levels() required drop empty levels splitting time. Splitting big topic eventually addressed specific package vignette. table labels M F descriptive. can add full labels follows: next table variation stratify gender AGE analysis. nested argument set FALSE analyze() call: split rows groups (Male Female ) one might want summarize groups: usually showing count column percentages. especially important missing data. example, create table add missing data AGE variable: easy see many females males arm n represents number non-missing data elements variables. Groups within rows defined splitting can summarized summarize_row_groups(), example: couple things note : Group summaries produce “content” rows. Visually, ’s impossible distinguish data rows content rows. difference justified (’s important design decision) paginate tables content rows default repeated group gets divided via pagination. Conceptually content rows summarize patient population analyzed hence often count & group percentages (default behavior summarize_row_groups()). can recreate default behavior (count percentage) defining cfun illustrative purposes results table : Note cfun, like afun (used analyze()), can operate either variables, passed via x argument, data.frames tibbles, passed via df argument (afun can optionally request df ). Unlike afun, cfun must accept labelstr second argument gives default group label (factor level splitting) hence modified:","code":"lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% analyze(c(\"AGE\", \"BMRKR2\"), s_summary) tbl <- build_table(lyt, ADSL) tbl # A: Drug X B: Placebo C: Combination # (N=134) (N=134) (N=132) # ———————————————————————————————————————————————————————————— # AGE # n 134 134 132 # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # IQR 11.00 10.00 10.00 # min - max 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 50 45 40 # MEDIUM 37 56 42 # HIGH 47 33 50 lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(c(\"AGE\", \"BMRKR2\"), s_summary) tbl <- build_table(lyt, ADSL) tbl # A: Drug X B: Placebo C: Combination # (N=134) (N=134) (N=132) # ————————————————————————————————————————————————————————————————— # F # AGE # n 79 77 66 # Mean (sd) 32.76 (6.09) 34.12 (7.06) 35.20 (7.43) # IQR 9.00 8.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 64.00 # BMRKR2 # LOW 26 21 26 # MEDIUM 21 38 17 # HIGH 32 18 23 # M # AGE # n 51 55 60 # Mean (sd) 35.57 (7.08) 37.44 (8.69) 35.38 (8.24) # IQR 11.00 9.00 11.00 # min - max 23.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 21 23 11 # MEDIUM 15 18 23 # HIGH 15 14 26 # U # AGE # n 3 2 4 # Mean (sd) 31.67 (3.21) 31.00 (5.66) 35.25 (3.10) # IQR 3.00 4.00 3.25 # min - max 28.00 - 34.00 27.00 - 35.00 31.00 - 38.00 # BMRKR2 # LOW 2 1 1 # MEDIUM 1 0 2 # HIGH 0 1 1 # UNDIFFERENTIATED # AGE # n 1 0 2 # Mean (sd) 28.00 (NA) NA 45.00 (1.41) # IQR 0.00 NA 1.00 # min - max 28.00 - 28.00 Inf - -Inf 44.00 - 46.00 # BMRKR2 # LOW 1 0 2 # MEDIUM 0 0 0 # HIGH 0 0 0 ADSL_M_F <- filter(ADSL, SEX %in% c(\"M\", \"F\")) lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(c(\"AGE\", \"BMRKR2\"), s_summary) tbl2 <- build_table(lyt2, ADSL_M_F) tbl2 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # ————————————————————————————————————————————————————————————————— # F # AGE # n 79 77 66 # Mean (sd) 32.76 (6.09) 34.12 (7.06) 35.20 (7.43) # IQR 9.00 8.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 64.00 # BMRKR2 # LOW 26 21 26 # MEDIUM 21 38 17 # HIGH 32 18 23 # M # AGE # n 51 55 60 # Mean (sd) 35.57 (7.08) 37.44 (8.69) 35.38 (8.24) # IQR 11.00 9.00 11.00 # min - max 23.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 21 23 11 # MEDIUM 15 18 23 # HIGH 15 14 26 # U # AGE # n 0 0 0 # Mean (sd) NA NA NA # IQR NA NA NA # min - max Inf - -Inf Inf - -Inf Inf - -Inf # BMRKR2 # LOW 0 0 0 # MEDIUM 0 0 0 # HIGH 0 0 0 # UNDIFFERENTIATED # AGE # n 0 0 0 # Mean (sd) NA NA NA # IQR NA NA NA # min - max Inf - -Inf Inf - -Inf Inf - -Inf # BMRKR2 # LOW 0 0 0 # MEDIUM 0 0 0 # HIGH 0 0 0 lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels, child_labels = \"visible\") %>% analyze(c(\"AGE\", \"BMRKR2\"), s_summary) tbl3 <- build_table(lyt3, ADSL_M_F) tbl3 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # —————————————————————————————————————————————————————————————— # F # AGE # n 79 77 66 # Mean (sd) 32.76 (6.09) 34.12 (7.06) 35.20 (7.43) # IQR 9.00 8.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 64.00 # BMRKR2 # LOW 26 21 26 # MEDIUM 21 38 17 # HIGH 32 18 23 # M # AGE # n 51 55 60 # Mean (sd) 35.57 (7.08) 37.44 (8.69) 35.38 (8.24) # IQR 11.00 9.00 11.00 # min - max 23.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 21 23 11 # MEDIUM 15 18 23 # HIGH 15 14 26 ADSL_M_F_l <- ADSL_M_F %>% mutate(lbl_sex = case_when( SEX == \"M\" ~ \"Male\", SEX == \"F\" ~ \"Female\", SEX == \"U\" ~ \"Unknown\", SEX == \"UNDIFFERENTIATED\" ~ \"Undifferentiated\" )) lyt4 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", labels_var = \"lbl_sex\", split_fun = drop_split_levels, child_labels = \"visible\") %>% analyze(c(\"AGE\", \"BMRKR2\"), s_summary) tbl4 <- build_table(lyt4, ADSL_M_F_l) tbl4 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # —————————————————————————————————————————————————————————————— # Female # AGE # n 79 77 66 # Mean (sd) 32.76 (6.09) 34.12 (7.06) 35.20 (7.43) # IQR 9.00 8.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 64.00 # BMRKR2 # LOW 26 21 26 # MEDIUM 21 38 17 # HIGH 32 18 23 # Male # AGE # n 51 55 60 # Mean (sd) 35.57 (7.08) 37.44 (8.69) 35.38 (8.24) # IQR 11.00 9.00 11.00 # min - max 23.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 21 23 11 # MEDIUM 15 18 23 # HIGH 15 14 26 lyt5 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", labels_var = \"lbl_sex\", split_fun = drop_split_levels, child_labels = \"visible\") %>% analyze(\"AGE\", s_summary, show_labels = \"visible\") %>% analyze(\"BMRKR2\", s_summary, nested = FALSE, show_labels = \"visible\") tbl5 <- build_table(lyt5, ADSL_M_F_l) tbl5 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # —————————————————————————————————————————————————————————————— # Female # AGE # n 79 77 66 # Mean (sd) 32.76 (6.09) 34.12 (7.06) 35.20 (7.43) # IQR 9.00 8.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 64.00 # Male # AGE # n 51 55 60 # Mean (sd) 35.57 (7.08) 37.44 (8.69) 35.38 (8.24) # IQR 11.00 9.00 11.00 # min - max 23.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 47 44 37 # MEDIUM 36 56 40 # HIGH 47 32 49 insert_NAs <- function(x) { x[sample(c(TRUE, FALSE), length(x), TRUE, prob = c(0.2, 0.8))] <- NA x } set.seed(1) ADSL_NA <- ADSL_M_F_l %>% mutate(AGE = insert_NAs(AGE)) lyt6 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by( \"SEX\", labels_var = \"lbl_sex\", split_fun = drop_split_levels, child_labels = \"visible\" ) %>% analyze(\"AGE\", s_summary) %>% analyze(\"BMRKR2\", s_summary, nested = FALSE, show_labels = \"visible\") tbl6 <- build_table(lyt6, filter(ADSL_NA, SEX %in% c(\"M\", \"F\"))) tbl6 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # ———————————————————————————————————————————————————————————— # Female # n 65 61 54 # Mean (sd) 32.71 (6.07) 34.33 (7.31) 34.61 (6.78) # IQR 9.00 10.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 54.00 # Male # n 44 44 50 # Mean (sd) 35.66 (6.78) 36.93 (8.18) 35.64 (8.42) # IQR 10.50 8.25 10.75 # min - max 24.00 - 48.00 21.00 - 58.00 20.00 - 69.00 # BMRKR2 # LOW 47 44 37 # MEDIUM 36 56 40 # HIGH 47 32 49 lyt7 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", labels_var = \"lbl_sex\", split_fun = drop_split_levels) %>% summarize_row_groups() %>% analyze(\"AGE\", s_summary) %>% analyze(\"BMRKR2\", afun = s_summary, nested = FALSE, show_labels = \"visible\") tbl7 <- build_table(lyt7, filter(ADSL_NA, SEX %in% c(\"M\", \"F\"))) tbl7 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # ———————————————————————————————————————————————————————————— # Female 79 (60.8%) 77 (58.3%) 66 (52.4%) # n 65 61 54 # Mean (sd) 32.71 (6.07) 34.33 (7.31) 34.61 (6.78) # IQR 9.00 10.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 54.00 # Male 51 (39.2%) 55 (41.7%) 60 (47.6%) # n 44 44 50 # Mean (sd) 35.66 (6.78) 36.93 (8.18) 35.64 (8.42) # IQR 10.50 8.25 10.75 # min - max 24.00 - 48.00 21.00 - 58.00 20.00 - 69.00 # BMRKR2 # LOW 47 44 37 # MEDIUM 36 56 40 # HIGH 47 32 49 lyt8 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", labels_var = \"lbl_sex\", split_fun = drop_split_levels) %>% summarize_row_groups(cfun = function(df, labelstr, .N_col, ...) { in_rows( rcell(nrow(df) * c(1, 1 / .N_col), format = \"xx (xx.xx%)\"), .labels = labelstr ) }) %>% analyze(\"AGE\", s_summary) %>% analyze(\"BEP01FL\", afun = s_summary, nested = FALSE, show_labels = \"visible\") tbl8 <- build_table(lyt8, filter(ADSL_NA, SEX %in% c(\"M\", \"F\"))) tbl8 # A: Drug X B: Placebo C: Combination # (N=130) (N=132) (N=126) # ———————————————————————————————————————————————————————————— # Female 79 (60.77%) 77 (58.33%) 66 (52.38%) # n 65 61 54 # Mean (sd) 32.71 (6.07) 34.33 (7.31) 34.61 (6.78) # IQR 9.00 10.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 54.00 # Male 51 (39.23%) 55 (41.67%) 60 (47.62%) # n 44 44 50 # Mean (sd) 35.66 (6.78) 36.93 (8.18) 35.64 (8.42) # IQR 10.50 8.25 10.75 # min - max 24.00 - 48.00 21.00 - 58.00 20.00 - 69.00 # BEP01FL # Y 67 63 65 # N 63 69 61 lyt9 <- basic_table() %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", labels_var = \"lbl_sex\", split_fun = drop_split_levels, child_labels = \"hidden\") %>% summarize_row_groups(cfun = function(df, labelstr, .N_col, ...) { in_rows( rcell(nrow(df) * c(1, 1 / .N_col), format = \"xx (xx.xx%)\"), .labels = paste0(labelstr, \": count (perc.)\") ) }) %>% analyze(\"AGE\", s_summary) %>% analyze(\"BEP01FL\", s_summary, nested = FALSE, show_labels = \"visible\") tbl9 <- build_table(lyt9, filter(ADSL_NA, SEX %in% c(\"M\", \"F\"))) tbl9 # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————————————————— # Female: count (perc.) 79 (60.77%) 77 (58.33%) 66 (52.38%) # n 65 61 54 # Mean (sd) 32.71 (6.07) 34.33 (7.31) 34.61 (6.78) # IQR 9.00 10.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 54.00 # Male: count (perc.) 51 (39.23%) 55 (41.67%) 60 (47.62%) # n 44 44 50 # Mean (sd) 35.66 (6.78) 36.93 (8.18) 35.64 (8.42) # IQR 10.50 8.25 10.75 # min - max 24.00 - 48.00 21.00 - 58.00 20.00 - 69.00 # BEP01FL # Y 67 63 65 # N 63 69 61"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"using-layouts","dir":"Articles","previous_headings":"Demographic Table","what":"Using Layouts","title":"Example Clinical Trials Tables","text":"Layouts couple advantages tabulating tables directly: .e. separate analyses description actual data referencing variable names happens via strings (non-standard evaluation (NSE) needed, though arguably either feature shortcoming) layouts can reused example demonstrates reusability layouts: can now build table ADSL patients older 18:","code":"adsl_lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% analyze(c(\"AGE\", \"SEX\"), afun = s_summary) adsl_lyt # A Pre-data Table Layout # # Column-Split Structure: # ARM (lvls) # # Row-Split Structure: # AGE:SEX (** multivar analysis **) adsl_tbl <- build_table(adsl_lyt, ADSL) adsl_tbl # A: Drug X B: Placebo C: Combination # (N=134) (N=134) (N=132) # ——————————————————————————————————————————————————————————————————— # AGE # n 134 134 132 # Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) # IQR 11.00 10.00 10.00 # min - max 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # SEX # F 79 77 66 # M 51 55 60 # U 3 2 4 # UNDIFFERENTIATED 1 0 2 adsl_f_tbl <- build_table(lyt, ADSL %>% filter(AGE > 18)) # Warning in min(x): no non-missing arguments to min; returning Inf # Warning in max(x): no non-missing arguments to max; returning -Inf adsl_f_tbl # A: Drug X B: Placebo C: Combination # (N=134) (N=134) (N=132) # ————————————————————————————————————————————————————————————————— # F # AGE # n 79 77 66 # Mean (sd) 32.76 (6.09) 34.12 (7.06) 35.20 (7.43) # IQR 9.00 8.00 6.75 # min - max 21.00 - 47.00 23.00 - 58.00 21.00 - 64.00 # BMRKR2 # LOW 26 21 26 # MEDIUM 21 38 17 # HIGH 32 18 23 # M # AGE # n 51 55 60 # Mean (sd) 35.57 (7.08) 37.44 (8.69) 35.38 (8.24) # IQR 11.00 9.00 11.00 # min - max 23.00 - 50.00 21.00 - 62.00 20.00 - 69.00 # BMRKR2 # LOW 21 23 11 # MEDIUM 15 18 23 # HIGH 15 14 26 # U # AGE # n 3 2 4 # Mean (sd) 31.67 (3.21) 31.00 (5.66) 35.25 (3.10) # IQR 3.00 4.00 3.25 # min - max 28.00 - 34.00 27.00 - 35.00 31.00 - 38.00 # BMRKR2 # LOW 2 1 1 # MEDIUM 1 0 2 # HIGH 0 1 1 # UNDIFFERENTIATED # AGE # n 1 0 2 # Mean (sd) 28.00 (NA) NA 45.00 (1.41) # IQR 0.00 NA 1.00 # min - max 28.00 - 28.00 Inf - -Inf 44.00 - 46.00 # BMRKR2 # LOW 1 0 2 # MEDIUM 0 0 0 # HIGH 0 0 0"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"adverse-events","dir":"Articles","previous_headings":"","what":"Adverse Events","title":"Example Clinical Trials Tables","text":"number different adverse event tables. now present two tables show adverse events ID grade ID. time won’t use ADAE dataset random.cdisc.data rather generate dataset fly (see Adrian’s 2016 Phuse paper):","code":"set.seed(1) lookup <- tribble( ~AEDECOD, ~AEBODSYS, ~AETOXGR, \"HEADACHE\", \"NERVOUS SYSTEM DISORDERS\", \"5\", \"BACK PAIN\", \"MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS\", \"2\", \"GINGIVAL BLEEDING\", \"GASTROINTESTINAL DISORDERS\", \"1\", \"HYPOTENSION\", \"VASCULAR DISORDERS\", \"3\", \"FAECES SOFT\", \"GASTROINTESTINAL DISORDERS\", \"2\", \"ABDOMINAL DISCOMFORT\", \"GASTROINTESTINAL DISORDERS\", \"1\", \"DIARRHEA\", \"GASTROINTESTINAL DISORDERS\", \"1\", \"ABDOMINAL FULLNESS DUE TO GAS\", \"GASTROINTESTINAL DISORDERS\", \"1\", \"NAUSEA (INTERMITTENT)\", \"GASTROINTESTINAL DISORDERS\", \"2\", \"WEAKNESS\", \"MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS\", \"3\", \"ORTHOSTATIC HYPOTENSION\", \"VASCULAR DISORDERS\", \"4\" ) normalize <- function(x) x / sum(x) weightsA <- normalize(c(0.1, dlnorm(seq(0, 5, length.out = 25), meanlog = 3))) weightsB <- normalize(c(0.2, dlnorm(seq(0, 5, length.out = 25)))) N_pop <- 300 ADSL2 <- data.frame( USUBJID = seq(1, N_pop, by = 1), ARM = sample(c(\"ARM A\", \"ARM B\"), N_pop, TRUE), SEX = sample(c(\"F\", \"M\"), N_pop, TRUE), AGE = 20 + rbinom(N_pop, size = 40, prob = 0.7) ) l.adae <- mapply( ADSL2$USUBJID, ADSL2$ARM, ADSL2$SEX, ADSL2$AGE, FUN = function(id, arm, sex, age) { n_ae <- sample(0:25, 1, prob = if (arm == \"ARM A\") weightsA else weightsB) i <- sample(seq_len(nrow(lookup)), size = n_ae, replace = TRUE, prob = c(6, rep(1, 10)) / 16) lookup[i, ] %>% mutate( AESEQ = seq_len(n()), USUBJID = id, ARM = arm, SEX = sex, AGE = age ) }, SIMPLIFY = FALSE ) ADAE2 <- do.call(rbind, l.adae) ADAE2 <- ADAE2 %>% mutate( ARM = factor(ARM, levels = c(\"ARM A\", \"ARM B\")), AEDECOD = as.factor(AEDECOD), AEBODSYS = as.factor(AEBODSYS), AETOXGR = factor(AETOXGR, levels = as.character(1:5)) ) %>% select(USUBJID, ARM, AGE, SEX, AESEQ, AEDECOD, AEBODSYS, AETOXGR) ADAE2 # # A tibble: 3,118 × 8 # USUBJID ARM AGE SEX AESEQ AEDECOD AEBODSYS AETOXGR # # 1 1 ARM A 45 F 1 NAUSEA (INTERMITTENT) GASTROINTESTIN… 2 # 2 1 ARM A 45 F 2 HEADACHE NERVOUS SYSTEM… 5 # 3 1 ARM A 45 F 3 HEADACHE NERVOUS SYSTEM… 5 # 4 1 ARM A 45 F 4 HEADACHE NERVOUS SYSTEM… 5 # 5 1 ARM A 45 F 5 HEADACHE NERVOUS SYSTEM… 5 # 6 1 ARM A 45 F 6 HEADACHE NERVOUS SYSTEM… 5 # 7 1 ARM A 45 F 7 HEADACHE NERVOUS SYSTEM… 5 # 8 1 ARM A 45 F 8 HEADACHE NERVOUS SYSTEM… 5 # 9 1 ARM A 45 F 9 HEADACHE NERVOUS SYSTEM… 5 # 10 1 ARM A 45 F 10 FAECES SOFT GASTROINTESTIN… 2 # # ℹ 3,108 more rows"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"adverse-events-by-id","dir":"Articles","previous_headings":"Adverse Events","what":"Adverse Events By ID","title":"Example Clinical Trials Tables","text":"start defining events summary function: , population 5 patients one patient 2 AEs one patient 1 AE three patients AEs get following summary: .N_col argument special keyword argument build_table() passes population size respective column. list keyword arguments functions passed afun analyze(), refer documentation ?analyze. now use s_events_patients summary function tabulation: Note column Ns wrong default set number rows per group (.e. number AEs per arm ). also affects percentages. table interested number patients per column/arm usually taken ADSL (var ADSL2 ). rtables handles allowing us override column counts computed. can specify alt_counts_df build_table(). , rtables calculates column counts applying column faceting alt_counts_df primary data tabulation: Alternatively, desired column counts already calculated, can specified directly via col_counts argument build_table(), though specifying alt_counts_df preferred mechanism. next calculate information per system organ class: now add count table AEDECOD AEBODSYS. default analyze() behavior factor create count table per level (using rtab_inner): indent_mod argument enables relative indenting changes tree structure table result desired indentation default. table far however usual adverse event table counts total number events number subjects one events particular term. get correct table need write custom analysis function: desired AE table : Note missing overall summary first two rows. can added initial analyze() call. Finally, wanted prune 0 count rows can trim_rows() function: Pruning larger topic separate rtables package vignette.","code":"s_events_patients <- function(x, labelstr, .N_col) { in_rows( \"Total number of patients with at least one event\" = rcell(length(unique(x)) * c(1, 1 / .N_col), format = \"xx (xx.xx%)\"), \"Total number of events\" = rcell(length(x), format = \"xx\") ) } s_events_patients(x = c(\"id 1\", \"id 1\", \"id 2\"), .N_col = 5) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod # 1 Total number of patients with at least one event 2 (40.00%) 0 # 2 Total number of events 3 0 # row_label # 1 Total number of patients with at least one event # 2 Total number of events adae_lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% analyze(\"USUBJID\", s_events_patients) adae_tbl <- build_table(adae_lyt, ADAE2) adae_tbl # ARM A ARM B # (N=2060) (N=1058) # ————————————————————————————————————————————————————————————————————————————— # Total number of patients with at least one event 114 (5.53%) 150 (14.18%) # Total number of events 2060 1058 adae_adsl_tbl <- build_table(adae_lyt, ADAE2, alt_counts_df = ADSL2) adae_adsl_tbl # ARM A ARM B # (N=146) (N=154) # —————————————————————————————————————————————————————————————————————————————— # Total number of patients with at least one event 114 (78.08%) 150 (97.40%) # Total number of events 2060 1058 adae_soc_lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% analyze(\"USUBJID\", s_events_patients) %>% split_rows_by(\"AEBODSYS\", child_labels = \"visible\", nested = FALSE) %>% summarize_row_groups(\"USUBJID\", cfun = s_events_patients) adae_soc_tbl <- build_table(adae_soc_lyt, ADAE2, alt_counts_df = ADSL2) adae_soc_tbl # ARM A ARM B # (N=146) (N=154) # ———————————————————————————————————————————————————————————————————————————————— # Total number of patients with at least one event 114 (78.08%) 150 (97.40%) # Total number of events 2060 1058 # GASTROINTESTINAL DISORDERS # Total number of patients with at least one event 114 (78.08%) 130 (84.42%) # Total number of events 760 374 # MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS # Total number of patients with at least one event 98 (67.12%) 81 (52.60%) # Total number of events 273 142 # NERVOUS SYSTEM DISORDERS # Total number of patients with at least one event 113 (77.40%) 133 (86.36%) # Total number of events 787 420 # VASCULAR DISORDERS # Total number of patients with at least one event 93 (63.70%) 75 (48.70%) # Total number of events 240 122 adae_soc_lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"AEBODSYS\", child_labels = \"visible\", indent_mod = 1) %>% summarize_row_groups(\"USUBJID\", cfun = s_events_patients) %>% analyze(\"AEDECOD\", indent_mod = -1) adae_soc_tbl2 <- build_table(adae_soc_lyt2, ADAE2, alt_counts_df = ADSL2) adae_soc_tbl2 # ARM A ARM B # (N=146) (N=154) # —————————————————————————————————————————————————————————————————————————————————— # GASTROINTESTINAL DISORDERS # Total number of patients with at least one event 114 (78.08%) 130 (84.42%) # Total number of events 760 374 # ABDOMINAL DISCOMFORT 113 65 # ABDOMINAL FULLNESS DUE TO GAS 119 65 # BACK PAIN 0 0 # DIARRHEA 107 53 # FAECES SOFT 122 58 # GINGIVAL BLEEDING 147 71 # HEADACHE 0 0 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 152 62 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 0 0 # MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS # Total number of patients with at least one event 98 (67.12%) 81 (52.60%) # Total number of events 273 142 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 135 75 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 0 0 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 138 67 # NERVOUS SYSTEM DISORDERS # Total number of patients with at least one event 113 (77.40%) 133 (86.36%) # Total number of events 787 420 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 0 0 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 787 420 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 0 0 # VASCULAR DISORDERS # Total number of patients with at least one event 93 (63.70%) 75 (48.70%) # Total number of events 240 122 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 0 0 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 0 0 # HYPOTENSION 104 58 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 136 64 # WEAKNESS 0 0 table_count_once_per_id <- function(df, termvar = \"AEDECOD\", idvar = \"USUBJID\") { x <- df[[termvar]] id <- df[[idvar]] counts <- table(x[!duplicated(id)]) in_rows( .list = as.vector(counts), .labels = names(counts) ) } table_count_once_per_id(ADAE2) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod # 1 ABDOMINAL DISCOMFORT 23 0 # 2 ABDOMINAL FULLNESS DUE TO GAS 21 0 # 3 BACK PAIN 20 0 # 4 DIARRHEA 7 0 # 5 FAECES SOFT 11 0 # 6 GINGIVAL BLEEDING 15 0 # 7 HEADACHE 100 0 # 8 HYPOTENSION 16 0 # 9 NAUSEA (INTERMITTENT) 21 0 # 10 ORTHOSTATIC HYPOTENSION 14 0 # 11 WEAKNESS 16 0 # row_label # 1 ABDOMINAL DISCOMFORT # 2 ABDOMINAL FULLNESS DUE TO GAS # 3 BACK PAIN # 4 DIARRHEA # 5 FAECES SOFT # 6 GINGIVAL BLEEDING # 7 HEADACHE # 8 HYPOTENSION # 9 NAUSEA (INTERMITTENT) # 10 ORTHOSTATIC HYPOTENSION # 11 WEAKNESS adae_soc_lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"AEBODSYS\", child_labels = \"visible\", indent_mod = 1) %>% summarize_row_groups(\"USUBJID\", cfun = s_events_patients) %>% analyze(\"AEDECOD\", afun = table_count_once_per_id, show_labels = \"hidden\", indent_mod = -1) adae_soc_tbl3 <- build_table(adae_soc_lyt3, ADAE2, alt_counts_df = ADSL2) adae_soc_tbl3 # ARM A ARM B # (N=146) (N=154) # —————————————————————————————————————————————————————————————————————————————————— # GASTROINTESTINAL DISORDERS # Total number of patients with at least one event 114 (78.08%) 130 (84.42%) # Total number of events 760 374 # ABDOMINAL DISCOMFORT 24 28 # ABDOMINAL FULLNESS DUE TO GAS 18 26 # BACK PAIN 0 0 # DIARRHEA 17 17 # FAECES SOFT 17 14 # GINGIVAL BLEEDING 18 25 # HEADACHE 0 0 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 20 20 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 0 0 # MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS # Total number of patients with at least one event 98 (67.12%) 81 (52.60%) # Total number of events 273 142 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 58 45 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 0 0 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 40 36 # NERVOUS SYSTEM DISORDERS # Total number of patients with at least one event 113 (77.40%) 133 (86.36%) # Total number of events 787 420 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 0 0 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 113 133 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 0 0 # VASCULAR DISORDERS # Total number of patients with at least one event 93 (63.70%) 75 (48.70%) # Total number of events 240 122 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 0 0 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 0 0 # HYPOTENSION 44 31 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 49 44 # WEAKNESS 0 0 adae_soc_lyt4 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% analyze(\"USUBJID\", afun = s_events_patients) %>% split_rows_by(\"AEBODSYS\", child_labels = \"visible\", indent_mod = 1, section_div = \"\") %>% summarize_row_groups(\"USUBJID\", cfun = s_events_patients) %>% analyze(\"AEDECOD\", table_count_once_per_id, show_labels = \"hidden\", indent_mod = -1) adae_soc_tbl4 <- build_table(adae_soc_lyt4, ADAE2, alt_counts_df = ADSL2) adae_soc_tbl4 # ARM A ARM B # (N=146) (N=154) # —————————————————————————————————————————————————————————————————————————————————— # Total number of patients with at least one event 114 (78.08%) 150 (97.40%) # Total number of events 2060 1058 # GASTROINTESTINAL DISORDERS # Total number of patients with at least one event 114 (78.08%) 130 (84.42%) # Total number of events 760 374 # ABDOMINAL DISCOMFORT 24 28 # ABDOMINAL FULLNESS DUE TO GAS 18 26 # BACK PAIN 0 0 # DIARRHEA 17 17 # FAECES SOFT 17 14 # GINGIVAL BLEEDING 18 25 # HEADACHE 0 0 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 20 20 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 0 0 # # MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS # Total number of patients with at least one event 98 (67.12%) 81 (52.60%) # Total number of events 273 142 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 58 45 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 0 0 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 40 36 # # NERVOUS SYSTEM DISORDERS # Total number of patients with at least one event 113 (77.40%) 133 (86.36%) # Total number of events 787 420 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 0 0 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 113 133 # HYPOTENSION 0 0 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 0 0 # WEAKNESS 0 0 # # VASCULAR DISORDERS # Total number of patients with at least one event 93 (63.70%) 75 (48.70%) # Total number of events 240 122 # ABDOMINAL DISCOMFORT 0 0 # ABDOMINAL FULLNESS DUE TO GAS 0 0 # BACK PAIN 0 0 # DIARRHEA 0 0 # FAECES SOFT 0 0 # GINGIVAL BLEEDING 0 0 # HEADACHE 0 0 # HYPOTENSION 44 31 # NAUSEA (INTERMITTENT) 0 0 # ORTHOSTATIC HYPOTENSION 49 44 # WEAKNESS 0 0 trim_rows(adae_soc_tbl4) # ARM A ARM B # (N=146) (N=154) # —————————————————————————————————————————————————————————————————————————————————— # Total number of patients with at least one event 114 (78.08%) 150 (97.40%) # Total number of events 2060 1058 # GASTROINTESTINAL DISORDERS # Total number of patients with at least one event 114 (78.08%) 130 (84.42%) # Total number of events 760 374 # ABDOMINAL DISCOMFORT 24 28 # ABDOMINAL FULLNESS DUE TO GAS 18 26 # DIARRHEA 17 17 # FAECES SOFT 17 14 # GINGIVAL BLEEDING 18 25 # NAUSEA (INTERMITTENT) 20 20 # # MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS # Total number of patients with at least one event 98 (67.12%) 81 (52.60%) # Total number of events 273 142 # BACK PAIN 58 45 # WEAKNESS 40 36 # # NERVOUS SYSTEM DISORDERS # Total number of patients with at least one event 113 (77.40%) 133 (86.36%) # Total number of events 787 420 # HEADACHE 113 133 # # VASCULAR DISORDERS # Total number of patients with at least one event 93 (63.70%) 75 (48.70%) # Total number of events 240 122 # HYPOTENSION 44 31 # ORTHOSTATIC HYPOTENSION 49 44"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"adverse-events-by-id-and-by-grade","dir":"Articles","previous_headings":"Adverse Events","what":"Adverse Events By ID and By Grade","title":"Example Clinical Trials Tables","text":"adverse events table ID grade shows many patients least one adverse event per grade different subsets data (e.g. defined system organ class). table show zero count grades. Note add “overall” groups custom split function. layouting concepts needed create table already introduced far:","code":"table_count_grade_once_per_id <- function(df, labelstr = \"\", gradevar = \"AETOXGR\", idvar = \"USUBJID\", grade_levels = NULL) { id <- df[[idvar]] grade <- df[[gradevar]] if (!is.null(grade_levels)) { stopifnot(all(grade %in% grade_levels)) grade <- factor(grade, levels = grade_levels) } id_sel <- !duplicated(id) in_rows( \"--Any Grade--\" = sum(id_sel), .list = as.list(table(grade[id_sel])) ) } table_count_grade_once_per_id(ex_adae, grade_levels = 1:5) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod row_label # 1 --Any Grade-- 365 0 --Any Grade-- # 2 1 131 0 1 # 3 2 70 0 2 # 4 3 74 0 3 # 5 4 25 0 4 # 6 5 65 0 5 adae_grade_lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% analyze( \"AETOXGR\", afun = table_count_grade_once_per_id, extra_args = list(grade_levels = 1:5), var_labels = \"- Any adverse events -\", show_labels = \"visible\" ) %>% split_rows_by(\"AEBODSYS\", child_labels = \"visible\", indent_mod = 1) %>% summarize_row_groups(cfun = table_count_grade_once_per_id, format = \"xx\", indent_mod = 1) %>% split_rows_by(\"AEDECOD\", child_labels = \"visible\", indent_mod = -2) %>% analyze( \"AETOXGR\", afun = table_count_grade_once_per_id, extra_args = list(grade_levels = 1:5), show_labels = \"hidden\" ) adae_grade_tbl <- build_table(adae_grade_lyt, ADAE2, alt_counts_df = ADSL2) adae_grade_tbl # ARM A ARM B # (N=146) (N=154) # ————————————————————————————————————————————————————————————————————— # - Any adverse events - # --Any Grade-- 114 150 # 1 32 34 # 2 22 30 # 3 11 21 # 4 8 6 # 5 41 59 # GASTROINTESTINAL DISORDERS # --Any Grade-- 114 130 # 1 77 96 # 2 37 34 # 3 0 0 # 4 0 0 # 5 0 0 # ABDOMINAL DISCOMFORT # --Any Grade-- 68 49 # 1 68 49 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ABDOMINAL FULLNESS DUE TO GAS # --Any Grade-- 73 51 # 1 73 51 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # BACK PAIN # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # DIARRHEA # --Any Grade-- 68 40 # 1 68 40 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # FAECES SOFT # --Any Grade-- 76 44 # 1 0 0 # 2 76 44 # 3 0 0 # 4 0 0 # 5 0 0 # GINGIVAL BLEEDING # --Any Grade-- 80 52 # 1 80 52 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HEADACHE # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HYPOTENSION # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # NAUSEA (INTERMITTENT) # --Any Grade-- 83 50 # 1 0 0 # 2 83 50 # 3 0 0 # 4 0 0 # 5 0 0 # ORTHOSTATIC HYPOTENSION # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # WEAKNESS # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # MUSCULOSKELETAL AND CONNECTIVE TISSUE DISORDERS # --Any Grade-- 98 81 # 1 0 0 # 2 58 45 # 3 40 36 # 4 0 0 # 5 0 0 # ABDOMINAL DISCOMFORT # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ABDOMINAL FULLNESS DUE TO GAS # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # BACK PAIN # --Any Grade-- 79 62 # 1 0 0 # 2 79 62 # 3 0 0 # 4 0 0 # 5 0 0 # DIARRHEA # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # FAECES SOFT # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # GINGIVAL BLEEDING # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HEADACHE # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HYPOTENSION # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # NAUSEA (INTERMITTENT) # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ORTHOSTATIC HYPOTENSION # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # WEAKNESS # --Any Grade-- 73 43 # 1 0 0 # 2 0 0 # 3 73 43 # 4 0 0 # 5 0 0 # NERVOUS SYSTEM DISORDERS # --Any Grade-- 113 133 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 113 133 # ABDOMINAL DISCOMFORT # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ABDOMINAL FULLNESS DUE TO GAS # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # BACK PAIN # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # DIARRHEA # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # FAECES SOFT # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # GINGIVAL BLEEDING # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HEADACHE # --Any Grade-- 113 133 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 113 133 # HYPOTENSION # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # NAUSEA (INTERMITTENT) # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ORTHOSTATIC HYPOTENSION # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # WEAKNESS # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # VASCULAR DISORDERS # --Any Grade-- 93 75 # 1 0 0 # 2 0 0 # 3 44 31 # 4 49 44 # 5 0 0 # ABDOMINAL DISCOMFORT # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ABDOMINAL FULLNESS DUE TO GAS # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # BACK PAIN # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # DIARRHEA # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # FAECES SOFT # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # GINGIVAL BLEEDING # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HEADACHE # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # HYPOTENSION # --Any Grade-- 66 43 # 1 0 0 # 2 0 0 # 3 66 43 # 4 0 0 # 5 0 0 # NAUSEA (INTERMITTENT) # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0 # ORTHOSTATIC HYPOTENSION # --Any Grade-- 70 54 # 1 0 0 # 2 0 0 # 3 0 0 # 4 70 54 # 5 0 0 # WEAKNESS # --Any Grade-- 0 0 # 1 0 0 # 2 0 0 # 3 0 0 # 4 0 0 # 5 0 0"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"response-table","dir":"Articles","previous_headings":"","what":"Response Table","title":"Example Clinical Trials Tables","text":"response table create composed 3 parts: Binary response table Unstratified analysis comparison vs. control group Multinomial response table Let’s start first part fairly simple derive: Note set ref_group argument split_cols_by() current table effect use cell data responder non-responder counts. ref_group argument needed part 2 3 table. now look implementation part 2: unstratified analysis comparison vs. control group. Let’s start analysis function: Hence can now add next vignette table: Next add part 3: multinomial response table. , adding row-split response level, thing binary response table . can now create final response table three parts: case wanted rename levels AVALC remove CI NE follows: Note table missing rows gaps make readable. row spacing feature rtables roadmap implemented future.","code":"ADRS_BESRSPI <- ex_adrs %>% filter(PARAMCD == \"BESRSPI\") %>% mutate( rsp = factor(AVALC %in% c(\"CR\", \"PR\"), levels = c(TRUE, FALSE), labels = c(\"Responders\", \"Non-Responders\")), is_rsp = (rsp == \"Responders\") ) s_proportion <- function(x, .N_col) { in_rows( .list = lapply( as.list(table(x)), function(xi) rcell(xi * c(1, 1 / .N_col), format = \"xx.xx (xx.xx%)\") ) ) } rsp_lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARMCD\", ref_group = \"ARM A\") %>% analyze(\"rsp\", s_proportion, show_labels = \"hidden\") rsp_tbl <- build_table(rsp_lyt, ADRS_BESRSPI) rsp_tbl # ARM A ARM B ARM C # (N=134) (N=134) (N=132) # ——————————————————————————————————————————————————————————————————— # Responders 114.00 (85.07%) 90.00 (67.16%) 120.00 (90.91%) # Non-Responders 20.00 (14.93%) 44.00 (32.84%) 12.00 (9.09%) s_unstrat_resp <- function(x, .ref_group, .in_ref_col) { if (.in_ref_col) { return(in_rows( \"Difference in Response Rates (%)\" = rcell(numeric(0)), \"95% CI (Wald, with correction)\" = rcell(numeric(0)), \"p-value (Chi-Squared Test)\" = rcell(numeric(0)), \"Odds Ratio (95% CI)\" = rcell(numeric(0)) )) } fit <- stats::prop.test( x = c(sum(x), sum(.ref_group)), n = c(length(x), length(.ref_group)), correct = FALSE ) fit_glm <- stats::glm( formula = rsp ~ group, data = data.frame( rsp = c(.ref_group, x), group = factor(rep(c(\"ref\", \"x\"), times = c(length(.ref_group), length(x))), levels = c(\"ref\", \"x\")) ), family = binomial(link = \"logit\") ) in_rows( \"Difference in Response Rates (%)\" = non_ref_rcell( (mean(x) - mean(.ref_group)) * 100, .in_ref_col, format = \"xx.xx\" ), \"95% CI (Wald, with correction)\" = non_ref_rcell( fit$conf.int * 100, .in_ref_col, format = \"(xx.xx, xx.xx)\" ), \"p-value (Chi-Squared Test)\" = non_ref_rcell( fit$p.value, .in_ref_col, format = \"x.xxxx | (<0.0001)\" ), \"Odds Ratio (95% CI)\" = non_ref_rcell( c( exp(stats::coef(fit_glm)[-1]), exp(stats::confint.default(fit_glm, level = .95)[-1, , drop = FALSE]) ), .in_ref_col, format = \"xx.xx (xx.xx - xx.xx)\" ) ) } s_unstrat_resp( x = ADRS_BESRSPI %>% filter(ARM == \"A: Drug X\") %>% pull(is_rsp), .ref_group = ADRS_BESRSPI %>% filter(ARM == \"B: Placebo\") %>% pull(is_rsp), .in_ref_col = FALSE ) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod # 1 Difference in Response Rates (%) 17.91 0 # 2 95% CI (Wald, with correction) (7.93, 27.89) 0 # 3 p-value (Chi-Squared Test) 0.0006 0 # 4 Odds Ratio (95% CI) 2.79 (1.53 - 5.06) 0 # row_label # 1 Difference in Response Rates (%) # 2 95% CI (Wald, with correction) # 3 p-value (Chi-Squared Test) # 4 Odds Ratio (95% CI) rsp_lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARMCD\", ref_group = \"ARM A\") %>% analyze(\"rsp\", s_proportion, show_labels = \"hidden\") %>% analyze( \"is_rsp\", s_unstrat_resp, show_labels = \"visible\", var_labels = \"Unstratified Response Analysis\" ) rsp_tbl2 <- build_table(rsp_lyt2, ADRS_BESRSPI) rsp_tbl2 # ARM A ARM B ARM C # (N=134) (N=134) (N=132) # —————————————————————————————————————————————————————————————————————————————————————————————— # Responders 114.00 (85.07%) 90.00 (67.16%) 120.00 (90.91%) # Non-Responders 20.00 (14.93%) 44.00 (32.84%) 12.00 (9.09%) # Unstratified Response Analysis # Difference in Response Rates (%) -17.91 5.83 # 95% CI (Wald, with correction) (-27.89, -7.93) (-1.94, 13.61) # p-value (Chi-Squared Test) 0.0006 0.1436 # Odds Ratio (95% CI) 0.36 (0.20 - 0.65) 1.75 (0.82 - 3.75) s_prop <- function(df, .N_col) { in_rows( \"95% CI (Wald, with correction)\" = rcell(binom.test(nrow(df), .N_col)$conf.int * 100, format = \"(xx.xx, xx.xx)\") ) } s_prop( df = ADRS_BESRSPI %>% filter(ARM == \"A: Drug X\", AVALC == \"CR\"), .N_col = sum(ADRS_BESRSPI$ARM == \"A: Drug X\") ) # RowsVerticalSection (in_rows) object print method: # ---------------------------- # row_name formatted_cell indent_mod # 1 95% CI (Wald, with correction) (49.38, 66.67) 0 # row_label # 1 95% CI (Wald, with correction) rsp_lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARMCD\", ref_group = \"ARM A\") %>% analyze(\"rsp\", s_proportion, show_labels = \"hidden\") %>% analyze( \"is_rsp\", s_unstrat_resp, show_labels = \"visible\", var_labels = \"Unstratified Response Analysis\" ) %>% split_rows_by( var = \"AVALC\", split_fun = reorder_split_levels(neworder = c(\"CR\", \"PR\", \"SD\", \"NON CR/PD\", \"PD\", \"NE\"), drlevels = TRUE), nested = FALSE ) %>% summarize_row_groups() %>% analyze(\"AVALC\", afun = s_prop) rsp_tbl3 <- build_table(rsp_lyt3, ADRS_BESRSPI) rsp_tbl3 # ARM A ARM B ARM C # (N=134) (N=134) (N=132) # —————————————————————————————————————————————————————————————————————————————————————————————— # Responders 114.00 (85.07%) 90.00 (67.16%) 120.00 (90.91%) # Non-Responders 20.00 (14.93%) 44.00 (32.84%) 12.00 (9.09%) # Unstratified Response Analysis # Difference in Response Rates (%) -17.91 5.83 # 95% CI (Wald, with correction) (-27.89, -7.93) (-1.94, 13.61) # p-value (Chi-Squared Test) 0.0006 0.1436 # Odds Ratio (95% CI) 0.36 (0.20 - 0.65) 1.75 (0.82 - 3.75) # CR 78 (58.2%) 55 (41.0%) 97 (73.5%) # 95% CI (Wald, with correction) (49.38, 66.67) (32.63, 49.87) (65.10, 80.79) # PR 36 (26.9%) 35 (26.1%) 23 (17.4%) # 95% CI (Wald, with correction) (19.58, 35.20) (18.92, 34.41) (11.38, 24.99) # SD 20 (14.9%) 44 (32.8%) 12 (9.1%) # 95% CI (Wald, with correction) (9.36, 22.11) (24.97, 41.47) (4.79, 15.34) rsp_label <- function(x) { rsp_full_label <- c( CR = \"Complete Response (CR)\", PR = \"Partial Response (PR)\", SD = \"Stable Disease (SD)\", `NON CR/PD` = \"Non-CR or Non-PD (NON CR/PD)\", PD = \"Progressive Disease (PD)\", NE = \"Not Evaluable (NE)\", Missing = \"Missing\", `NE/Missing` = \"Missing or unevaluable\" ) stopifnot(all(x %in% names(rsp_full_label))) rsp_full_label[x] } rsp_lyt4 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARMCD\", ref_group = \"ARM A\") %>% analyze(\"rsp\", s_proportion, show_labels = \"hidden\") %>% analyze( \"is_rsp\", s_unstrat_resp, show_labels = \"visible\", var_labels = \"Unstratified Response Analysis\" ) %>% split_rows_by( var = \"AVALC\", split_fun = keep_split_levels(c(\"CR\", \"PR\", \"SD\", \"PD\"), reorder = TRUE), nested = FALSE ) %>% summarize_row_groups(cfun = function(df, labelstr, .N_col) { in_rows(nrow(df) * c(1, 1 / .N_col), .formats = \"xx (xx.xx%)\", .labels = rsp_label(labelstr)) }) %>% analyze(\"AVALC\", afun = s_prop) %>% analyze(\"AVALC\", afun = function(x, .N_col) { in_rows(rcell(sum(x == \"NE\") * c(1, 1 / .N_col), format = \"xx.xx (xx.xx%)\"), .labels = rsp_label(\"NE\")) }, nested = FALSE) rsp_tbl4 <- build_table(rsp_lyt4, ADRS_BESRSPI) rsp_tbl4 # ARM A ARM B ARM C # (N=134) (N=134) (N=132) # —————————————————————————————————————————————————————————————————————————————————————————————— # Responders 114.00 (85.07%) 90.00 (67.16%) 120.00 (90.91%) # Non-Responders 20.00 (14.93%) 44.00 (32.84%) 12.00 (9.09%) # Unstratified Response Analysis # Difference in Response Rates (%) -17.91 5.83 # 95% CI (Wald, with correction) (-27.89, -7.93) (-1.94, 13.61) # p-value (Chi-Squared Test) 0.0006 0.1436 # Odds Ratio (95% CI) 0.36 (0.20 - 0.65) 1.75 (0.82 - 3.75) # Complete Response (CR) 78 (58.21%) 55 (41.04%) 97 (73.48%) # 95% CI (Wald, with correction) (49.38, 66.67) (32.63, 49.87) (65.10, 80.79) # Partial Response (PR) 36 (26.87%) 35 (26.12%) 23 (17.42%) # 95% CI (Wald, with correction) (19.58, 35.20) (18.92, 34.41) (11.38, 24.99) # Stable Disease (SD) 20 (14.93%) 44 (32.84%) 12 (9.09%) # 95% CI (Wald, with correction) (9.36, 22.11) (24.97, 41.47) (4.79, 15.34) # Progressive Disease (PD) 0 (0.00%) 0 (0.00%) 0 (0.00%) # 95% CI (Wald, with correction) (0.00, 2.72) (0.00, 2.72) (0.00, 2.76) # Not Evaluable (NE) 0.00 (0.00%) 0.00 (0.00%) 0.00 (0.00%)"},{"path":"https://insightsengineering.github.io/rtables/articles/clinical_trials.html","id":"time-to-event-analysis-table","dir":"Articles","previous_headings":"","what":"Time to Event Analysis Table","title":"Example Clinical Trials Tables","text":"time event analysis table constructed consists four parts: Overall subject counts Censored subjects summary Cox proportional-hazards analysis Time--event analysis table constructed sequential use analyze() function, four custom analysis functions corresponding four parts listed . addition table includes referential footnotes relevant table contents. table faceted column-wise arm. First start loading necessary packages preparing data used construction table. adtte dataset used preparing models adtte2 dataset handles missing values “Censor Date Description” column used produce final table. add censoring data example purposes. Next create basic analysis function, a_count_subjs prints overall unique subject counts percentages within data. analysis function created generate counts censored subjects level factor variable dataset. case cnsr_counter function applied CNSDTDSC variable contains censor date description censored subject. function generates counts fractions unique subjects corresponding factor level, excluding missing values (uncensored patients). Cox proportional-hazards (Cox P-H) analysis generated next third custom analysis function, a_cph. Prior creating analysis function, Cox P-H model fit data using coxph() Surv() functions survival package. model used input a_cph analysis function returns hazard ratios, 95% confidence intervals, p-values comparing reference group - case leftmost column. fourth final analysis function, a_tte, generates time first adverse event table three rows corresponding Median, 95% Confidence Interval, Min Max respectively. First survival table constructed summary table survival model using survfit() Surv() functions survival package. table given input a_tte produces table time first adverse event consisting previously mentioned summary statistics. Additionally, a_tte function creates referential footnote within table indicate censoring occurred data. Now able use four analysis functions build time event analysis table. set show_colcounts argument basic_table() TRUE first print total subject counts column. Next use split_cols_by() split table three columns corresponding three different levels ARM, specify first arm, \": Drug X\" act reference group compared - reference group used Cox P-H analysis. call analyze() sequentially using four custom analysis functions argument afun specifying additional arguments necessary. use build_table() construct rtable using adtte2 dataset. Finally, annotate table using fnotes_at_path() function specify product-limit estimates used calculate statistics listed “Time first adverse event” heading within table. referential footnote created earlier time--event analysis function (a_tte) also displayed.","code":"library(survival) adtte <- ex_adaette %>% dplyr::filter(PARAMCD == \"AETTE2\", SAFFL == \"Y\") # Add censoring to data for example adtte[adtte$AVAL > 1.0, ] <- adtte[adtte$AVAL > 1.0, ] %>% mutate(AVAL = 1.0, CNSR = 1) adtte2 <- adtte %>% mutate(CNSDTDSC = ifelse(CNSDTDSC == \"\", \"__none__\", CNSDTDSC)) a_count_subjs <- function(x, .N_col) { in_rows( \"Subjects with Adverse Events n (%)\" = rcell(length(unique(x)) * c(1, 1 / .N_col), format = \"xx (xx.xx%)\") ) } cnsr_counter <- function(df, .var, .N_col) { x <- df[!duplicated(df$USUBJID), .var] x <- x[x != \"__none__\"] lapply(table(x), function(xi) rcell(xi * c(1, 1 / .N_col), format = \"xx (xx.xx%)\")) } cph <- coxph(Surv(AVAL, CNSR == 0) ~ ACTARM + STRATA1, ties = \"exact\", data = adtte) a_cph <- function(df, .var, .in_ref_col, .ref_full, full_cox_fit) { if (.in_ref_col) { ret <- replicate(3, list(rcell(NULL))) } else { curtrt <- df[[.var]][1] coefs <- coef(full_cox_fit) sel_pos <- grep(curtrt, names(coefs), fixed = TRUE) hrval <- exp(coefs[sel_pos]) sdf <- survdiff(Surv(AVAL, CNSR == 0) ~ ACTARM + STRATA1, data = rbind(df, .ref_full)) pval <- (1 - pchisq(sdf$chisq, length(sdf$n) - 1)) / 2 ci_val <- exp(unlist(confint(full_cox_fit)[sel_pos, ])) ret <- list( rcell(hrval, format = \"xx.x\"), rcell(ci_val, format = \"(xx.x, xx.x)\"), rcell(pval, format = \"x.xxxx | (<0.0001)\") ) } in_rows( .list = ret, .names = c(\"Hazard ratio\", \"95% confidence interval\", \"p-value (one-sided stratified log rank)\") ) } surv_tbl <- as.data.frame( summary(survfit(Surv(AVAL, CNSR == 0) ~ ACTARM, data = adtte, conf.type = \"log-log\"))$table ) %>% dplyr::mutate( ACTARM = factor(gsub(\"ACTARM=\", \"\", row.names(.)), levels = levels(adtte$ACTARM)), ind = FALSE ) a_tte <- function(df, .var, kp_table) { ind <- grep(df[[.var]][1], row.names(kp_table), fixed = TRUE) minmax <- range(df[[\"AVAL\"]]) mm_val_str <- format_value(minmax, format = \"xx.x, xx.x\") rowfn <- list() if (all(df$CNSR[df$AVAL == minmax[2]])) { mm_val_str <- paste0(mm_val_str, \"*\") rowfn <- \"* indicates censoring\" } in_rows( Median = kp_table[ind, \"median\", drop = TRUE], \"95% confidence interval\" = unlist(kp_table[ind, c(\"0.95LCL\", \"0.95UCL\")]), \"Min Max\" = mm_val_str, .formats = c(\"xx.xx\", \"xx.xx - xx.xx\", \"xx\"), .row_footnotes = list(NULL, NULL, rowfn) ) } lyt <- basic_table(show_colcounts = TRUE) %>% ## Column faceting split_cols_by(\"ARM\", ref_group = \"A: Drug X\") %>% ## Overall count analyze(\"USUBJID\", a_count_subjs, show_labels = \"hidden\") %>% ## Censored subjects summary analyze(\"CNSDTDSC\", cnsr_counter, var_labels = \"Censored Subjects\", show_labels = \"visible\") %>% ## Cox P-H analysis analyze(\"ARM\", a_cph, extra_args = list(full_cox_fit = cph), show_labels = \"hidden\") %>% ## Time-to-event analysis analyze( \"ARM\", a_tte, var_labels = \"Time to first adverse event\", show_labels = \"visible\", extra_args = list(kp_table = surv_tbl), table_names = \"kapmeier\" ) tbl_tte <- build_table(lyt, adtte2) fnotes_at_path( tbl_tte, c(\"ma_USUBJID_CNSDTDSC_ARM_kapmeier\", \"kapmeier\") ) <- \"Product-limit (Kaplan-Meier) estimates.\" tbl_tte # A: Drug X B: Placebo C: Combination # (N=134) (N=134) (N=132) # ———————————————————————————————————————————————————————————————————————————————————————— # Subjects with Adverse Events n (%) 134 (100.00%) 134 (100.00%) 132 (100.00%) # Censored Subjects # Clinical Cut Off 6 (4.48%) 3 (2.24%) 14 (10.61%) # Completion or Discontinuation 9 (6.72%) 5 (3.73%) 9 (6.82%) # End of AE Reporting Period 14 (10.45%) 7 (5.22%) 14 (10.61%) # Preferred Term 11 (8.21%) 5 (3.73%) 13 (9.85%) # Hazard ratio 0.7 1.0 # 95% confidence interval (0.5, 0.9) (0.8, 1.4) # p-value (one-sided stratified log rank) 0.1070 0.4880 # Time to first adverse event {1} # Median 0.23 0.39 0.29 # 95% confidence interval 0.18 - 0.33 0.29 - 0.49 0.22 - 0.35 # Min Max {2} 0.0, 1.0* 0.0, 1.0* 0.0, 1.0* # ———————————————————————————————————————————————————————————————————————————————————————— # # {1} - Product-limit (Kaplan-Meier) estimates. # {2} - * indicates censoring # ————————————————————————————————————————————————————————————————————————————————————————"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"customizing-appearance","dir":"Articles","previous_headings":"","what":"Customizing Appearance","title":"Customizing Appearance","text":"vignette, describe various ways can modify customize appearance rtables. Loading package:","code":"library(rtables) library(dplyr)"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"rows-and-cell-values-alignments","dir":"Articles","previous_headings":"Customizing Appearance","what":"Rows and cell values alignments","title":"Customizing Appearance","text":"possible align content assigning \"left\", \"center\" (default), \"right\" .aligns align arguments in_rows() rcell(), respectively. also possible use decimal, dec_right, dec_left decimal alignments. first takes numerical values aligns decimal character . every value column align = \"decimal\". Also numberic without decimal values aligned according imaginary . specified . dec_left dec_right behave similarly, difference column present empty spaces left right, pushes values towards left right taking one value decimal characters, right, non-decimal values left. details, please read related documentation page help(\"decimal_align\"). Please consider using ?in_rows ?rcell clarifications two arguments, use formatters::list_valid_aligns() see available alignment options. following show two simplified examples use align .aligns, respectively. concepts can well applied clinical table shown following, complex, example.","code":"# In rcell we use align. lyt <- basic_table() %>% analyze(\"AGE\", function(x) { in_rows( left = rcell(\"l\", align = \"left\"), right = rcell(\"r\", align = \"right\"), center = rcell(\"c\", align = \"center\") ) }) tbl <- build_table(lyt, DM) tbl # all obs # ———————————————— # left l # right r # center c # In in_rows, we use .aligns. This can either set the general value or the # single values (see NB). lyt2 <- basic_table() %>% analyze(\"AGE\", function(x) { in_rows( left = rcell(\"l\"), right = rcell(\"r\"), center = rcell(\"c\"), .aligns = c(\"right\") ) # NB: .aligns = c(\"right\", \"left\", \"center\") }) tbl2 <- build_table(lyt2, DM) tbl2 # all obs # ———————————————— # left l # right r # center c lyt3 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(c(\"AGE\", \"STRATA1\"), function(x) { if (is.numeric(x)) { in_rows( \"mean\" = rcell(mean(x)), \"sd\" = rcell(sd(x)), .formats = c(\"xx.x\"), .aligns = \"left\" ) } else if (is.factor(x)) { rcell(length(unique(x)), align = \"right\") } else { stop(\"Unsupported type\") } }, show_labels = \"visible\", na_str = \"NE\") tbl3 <- build_table(lyt3, ex_adsl) tbl3 # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # AGE # mean 32.8 34.1 35.2 # sd 6.1 7.1 7.4 # STRATA1 # STRATA1 3 3 3 # M # AGE # mean 35.6 37.4 35.4 # sd 7.1 8.7 8.2 # STRATA1 # STRATA1 3 3 3 # U # AGE # mean 31.7 31.0 35.2 # sd 3.2 5.7 3.1 # STRATA1 # STRATA1 3 2 3 # UNDIFFERENTIATED # AGE # mean 28.0 NE 45.0 # sd NE NE 1.4 # STRATA1 # STRATA1 1 0 2"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"top-left-materials","dir":"Articles","previous_headings":"Customizing Appearance","what":"Top-left Materials","title":"Customizing Appearance","text":"sequence strings printed area column header display first row label can modified pre-processing using label position argument row splits split_rows_by, append_topleft function, post-processing using top_left() function. Note: Indenting automatically added label_pos = \"topleft\". Within layout initializer: Specify label position using split_rows function. Notice position STRATA1 SEX. Post-processing using top_left() function:","code":"lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\") %>% append_topleft(\"New top_left material here\") build_table(lyt, DM) # New top_left material here A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————————— # A # Mean 32.53 32.30 35.76 # B # Mean 35.46 32.42 34.39 # C # Mean 36.34 34.45 33.54 lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"STRATA1\", label_pos = \"topleft\") %>% split_rows_by(\"SEX\", label_pos = \"topleft\") %>% analyze(\"AGE\") build_table(lyt, DM) # STRATA1 # SEX A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————— # A # F # Mean 30.91 32.91 35.95 # M # Mean 35.07 31.09 35.60 # U # Mean NA NA NA # UNDIFFERENTIATED # Mean NA NA NA # B # F # Mean 34.85 32.88 34.42 # M # Mean 36.64 32.09 34.37 # U # Mean NA NA NA # UNDIFFERENTIATED # Mean NA NA NA # C # F # Mean 35.19 36.00 34.32 # M # Mean 37.39 32.81 32.83 # U # Mean NA NA NA # UNDIFFERENTIATED # Mean NA NA NA lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(c(\"AGE\", \"STRATA1\"), function(x) { if (is.numeric(x)) { in_rows( \"mean\" = rcell(mean(x)), \"sd\" = rcell(sd(x)), .formats = c(\"xx.x\"), .aligns = \"left\" ) } else if (is.factor(x)) { rcell(length(unique(x)), align = \"right\") } else { stop(\"Unsupported type\") } }, show_labels = \"visible\", na_str = \"NE\") %>% build_table(ex_adsl) # Adding top-left material top_left(lyt) <- \"New top-left material here\" lyt # New top-left material here A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————————— # F # AGE # mean 32.8 34.1 35.2 # sd 6.1 7.1 7.4 # STRATA1 # STRATA1 3 3 3 # M # AGE # mean 35.6 37.4 35.4 # sd 7.1 8.7 8.2 # STRATA1 # STRATA1 3 3 3 # U # AGE # mean 31.7 31.0 35.2 # sd 3.2 5.7 3.1 # STRATA1 # STRATA1 3 2 3 # UNDIFFERENTIATED # AGE # mean 28.0 NE 45.0 # sd NE NE 1.4 # STRATA1 # STRATA1 1 0 2"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"table-inset","dir":"Articles","previous_headings":"Customizing Appearance","what":"Table Inset","title":"Customizing Appearance","text":"Table title, table body, referential footnotes main footers can inset left alignment titles provenance footer materials. can modified within layout initializer basic_table() using inset argument post-processing table_inset(). Using layout initializer: Using post-processing function: Without inset - inset 5 characters - example table produced clinical data. Compare inset table main footer two tables. Without inset - inset - Notice, inset apply title materials (main title, subtitles, page titles), provenance footer materials. Inset settings applied top-left materials, referential footnotes main footer materials horizontal dividers.","code":"lyt <- basic_table(inset = 5) %>% analyze(\"AGE\") build_table(lyt, DM) # all obs # —————————————— # Mean 34.22 lyt <- basic_table() %>% analyze(\"AGE\") tbl <- build_table(lyt, DM) tbl # all obs # —————————————— # Mean 34.22 table_inset(tbl) <- 5 tbl # all obs # —————————————— # Mean 34.22 analysisfun <- function(x, ...) { in_rows( row1 = 5, row2 = c(1, 2), .row_footnotes = list(row1 = \"row 1 rfn\"), .cell_footnotes = list(row2 = \"row 2 cfn\") ) } lyt <- basic_table( title = \"Title says Whaaaat\", subtitles = \"Oh, ok.\", main_footer = \"ha HA! Footer!\", prov_footer = \"provenaaaaance\" ) %>% split_cols_by(\"ARM\") %>% analyze(\"AGE\", afun = analysisfun) result <- build_table(lyt, ex_adsl) result # Title says Whaaaat # Oh, ok. # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # row1 {1} 5 5 5 # row2 1, 2 {2} 1, 2 {2} 1, 2 {2} # —————————————————————————————————————————————————— # # {1} - row 1 rfn # {2} - row 2 cfn # —————————————————————————————————————————————————— # # ha HA! Footer! # # provenaaaaance table_inset(result) <- 5 result # Title says Whaaaat # Oh, ok. # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # row1 {1} 5 5 5 # row2 1, 2 {2} 1, 2 {2} 1, 2 {2} # —————————————————————————————————————————————————— # # {1} - row 1 rfn # {2} - row 2 cfn # —————————————————————————————————————————————————— # # ha HA! Footer! # # provenaaaaance"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"horizontal-separation","dir":"Articles","previous_headings":"Customizing Appearance","what":"Horizontal Separation","title":"Customizing Appearance","text":"character value can specified modify horizontal separation column headers table. Horizontal separation applies : separating title + subtitles column labels + top left materials, column labels + top left material row labels + cells, row labels + cells footer content, Referential footnotes main + provenance content something sides divider. , replace default line “=”.","code":"tbl <- basic_table() %>% split_cols_by(\"Species\") %>% add_colcounts() %>% analyze(c(\"Sepal.Length\", \"Petal.Width\"), function(x) { in_rows( mean_sd = c(mean(x), sd(x)), var = var(x), min_max = range(x), .formats = c(\"xx.xx (xx.xx)\", \"xx.xxx\", \"xx.x - xx.x\"), .labels = c(\"Mean (sd)\", \"Variance\", \"Min - Max\") ) }) %>% build_table(iris, hsep = \"=\") tbl # setosa versicolor virginica # (N=50) (N=50) (N=50) # ====================================================== # Sepal.Length # Mean (sd) 5.01 (0.35) 5.94 (0.52) 6.59 (0.64) # Variance 0.124 0.266 0.404 # Min - Max 4.3 - 5.8 4.9 - 7.0 4.9 - 7.9 # Petal.Width # Mean (sd) 0.25 (0.11) 1.33 (0.20) 2.03 (0.27) # Variance 0.011 0.039 0.075 # Min - Max 0.1 - 0.6 1.0 - 1.8 1.4 - 2.5"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"section-dividers","dir":"Articles","previous_headings":"Customizing Appearance","what":"Section Dividers","title":"Customizing Appearance","text":"character value can specified section divider succeed every group defined split instruction. Note, trailing divider end table never printed. , “+” repeated used section divider. Section dividers can set ” ” create blank line. Separation characters can specified different row splits. However, one printed “pile ” next .","code":"lyt <- basic_table() %>% split_cols_by(\"Species\") %>% analyze(head(names(iris), -1), afun = function(x) { list( \"mean / sd\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\"), \"range\" = rcell(diff(range(x)), format = \"xx.xx\") ) }, section_div = \"+\") build_table(lyt, iris) # setosa versicolor virginica # —————————————————————————————————————————————————————— # Sepal.Length # mean / sd 5.01 (0.35) 5.94 (0.52) 6.59 (0.64) # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # range 1.50 2.10 3.00 # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Sepal.Width # mean / sd 3.43 (0.38) 2.77 (0.31) 2.97 (0.32) # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # range 2.10 1.40 1.60 # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Petal.Length # mean / sd 1.46 (0.17) 4.26 (0.47) 5.55 (0.55) # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # range 0.90 2.10 2.40 # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # Petal.Width # mean / sd 0.25 (0.11) 1.33 (0.20) 2.03 (0.27) # ++++++++++++++++++++++++++++++++++++++++++++++++++++++ # range 0.50 0.80 1.10 lyt <- basic_table() %>% split_cols_by(\"Species\") %>% analyze(head(names(iris), -1), afun = function(x) { list( \"mean / sd\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\"), \"range\" = rcell(diff(range(x)), format = \"xx.xx\") ) }, section_div = \" \") build_table(lyt, iris) # setosa versicolor virginica # —————————————————————————————————————————————————————— # Sepal.Length # mean / sd 5.01 (0.35) 5.94 (0.52) 6.59 (0.64) # # range 1.50 2.10 3.00 # # Sepal.Width # mean / sd 3.43 (0.38) 2.77 (0.31) 2.97 (0.32) # # range 2.10 1.40 1.60 # # Petal.Length # mean / sd 1.46 (0.17) 4.26 (0.47) 5.55 (0.55) # # range 0.90 2.10 2.40 # # Petal.Width # mean / sd 0.25 (0.11) 1.33 (0.20) 2.03 (0.27) # # range 0.50 0.80 1.10 lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"RACE\", section_div = \"=\") %>% split_rows_by(\"STRATA1\", section_div = \"~\") %>% analyze(\"AGE\", mean, var_labels = \"Age\", format = \"xx.xx\") build_table(lyt, DM) # A: Drug X B: Placebo C: Combination # ——————————————————————————————————————————————————————————————————————————————————— # ASIAN # A # mean 32.19 33.90 36.81 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean 34.12 31.62 34.73 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean 36.21 33.00 32.39 # =================================================================================== # BLACK OR AFRICAN AMERICAN # A # mean 31.50 28.57 33.62 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean 35.60 30.83 33.67 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean 35.50 34.18 35.00 # =================================================================================== # WHITE # A # mean 37.67 31.33 33.17 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean 39.86 39.00 34.75 # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean 39.75 44.67 36.75 # =================================================================================== # AMERICAN INDIAN OR ALASKA NATIVE # A # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean NA NA NA # =================================================================================== # MULTIPLE # A # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean NA NA NA # =================================================================================== # NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER # A # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean NA NA NA # =================================================================================== # OTHER # A # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean NA NA NA # =================================================================================== # UNKNOWN # A # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # B # mean NA NA NA # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # C # mean NA NA NA"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"indent-modifier","dir":"Articles","previous_headings":"Customizing Appearance","what":"Indent Modifier","title":"Customizing Appearance","text":"Tables default indenting level splitting. custom indent value can supplied indent_mod argument within split function modify default. Compare indenting tables : Default Indent - Modified indent -","code":"basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\", mean, format = \"xx.x\") %>% build_table(DM) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # # —————————————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 # C # mean 35.2 36.0 34.3 # M # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 # C # mean 37.4 32.8 32.8 # U # A # mean NA NA NA # B # mean NA NA NA # C # mean NA NA NA # UNDIFFERENTIATED # A # mean NA NA NA # B # mean NA NA NA # C # mean NA NA NA # —————————————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", indent_mod = 3) %>% split_rows_by(\"STRATA1\", indent_mod = 5) %>% analyze(\"AGE\", mean, format = \"xx.x\") %>% build_table(DM) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # # —————————————————————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————————————— # F # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 # C # mean 35.2 36.0 34.3 # M # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 # C # mean 37.4 32.8 32.8 # U # A # mean NA NA NA # B # mean NA NA NA # C # mean NA NA NA # UNDIFFERENTIATED # A # mean NA NA NA # B # mean NA NA NA # C # mean NA NA NA # —————————————————————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"variable-label-visibility","dir":"Articles","previous_headings":"Customizing Appearance","what":"Variable Label Visibility","title":"Customizing Appearance","text":"split instructions, visibility label variable split can modified visible, hidden topleft show_labels argument, label_pos argument, child_labels argument applicable. Note: name levels contained variable. analyze calls, indicates variable visible multiple variables analyzed level nesting. Visibility labels groups generated split can also modified using child_label argument split call. child_label argument can force labels visible addition content rows hide move content rows. Notice placement “AGE” label example: set default, label AGE repeated since one variable analyzed level nesting. Override setting show_labels argument “visible”. example using label_pos argument modifying label visibility: Label order mirror order split_rows_by calls. labels subgroups hidden, label_pos argument set hidden. “SEX” label position hidden - “SEX” label position top-left materials -","code":"lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels, child_labels = \"visible\") %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\", mean, show_labels = \"default\") build_table(lyt, DM) # A: Drug X B: Placebo C: Combination # (N=121) (N=106) (N=129) # ————————————————————————————————————————————————————————————————— # F # A # mean 30.9090909090909 32.9090909090909 35.95 # B # mean 34.8518518518519 32.8823529411765 34.4210526315789 # C # mean 35.1904761904762 36 34.3181818181818 # M # A # mean 35.0714285714286 31.0909090909091 35.6 # B # mean 36.6428571428571 32.0869565217391 34.3684210526316 # C # mean 37.3913043478261 32.8125 32.8333333333333 lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(var = \"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels, child_labels = \"hidden\") %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\", mean, show_labels = \"visible\") build_table(lyt2, DM) # A: Drug X B: Placebo C: Combination # (N=121) (N=106) (N=129) # ————————————————————————————————————————————————————————————————— # A # AGE # mean 30.9090909090909 32.9090909090909 35.95 # B # AGE # mean 34.8518518518519 32.8823529411765 34.4210526315789 # C # AGE # mean 35.1904761904762 36 34.3181818181818 # A # AGE # mean 35.0714285714286 31.0909090909091 35.6 # B # AGE # mean 36.6428571428571 32.0869565217391 34.3684210526316 # C # AGE # mean 37.3913043478261 32.8125 32.8333333333333 basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels, label_pos = \"visible\") %>% split_rows_by(\"STRATA1\", label_pos = \"hidden\") %>% analyze(\"AGE\", mean, format = \"xx.x\") %>% build_table(DM) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # # ———————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————— # SEX # F # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 # C # mean 35.2 36.0 34.3 # M # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 # C # mean 37.4 32.8 32.8 # ———————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels, label_pos = \"topleft\") %>% split_rows_by(\"STRATA1\", label_pos = \"hidden\") %>% analyze(\"AGE\", mean, format = \"xx.x\") %>% build_table(DM) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # # —————————————————————————————————————————————————— # SEX A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # F # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 # C # mean 35.2 36.0 34.3 # M # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 # C # mean 37.4 32.8 32.8 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a"},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"cell-label-and-annotation-wrapping","dir":"Articles","previous_headings":"Customizing Appearance","what":"Cell, Label, and Annotation Wrapping","title":"Customizing Appearance","text":"rtable can rendered customized width setting custom rendering widths cell contents, row labels, titles/footers. demonstrated using sample data table . section aim render table reduced width since table wide contents several cells, labels, titles/footers. following sections use toString() function render table string form. resulting string representation ready printed written plain text file, use strsplit() function combination matrix() function preview rendered wrapped table matrix form within vignette.","code":"trimmed_data <- ex_adsl %>% filter(SEX %in% c(\"M\", \"F\")) %>% filter(RACE %in% levels(RACE)[1:2]) levels(trimmed_data$ARM)[1] <- \"Incredibly long column name to be wrapped\" levels(trimmed_data$ARM)[2] <- \"This_column_name_should_be_split_somewhere\" wide_tbl <- basic_table( title = \"Title that is too long and also needs to be wrapped to a smaller width\", subtitles = \"Subtitle that is also long and also needs to be wrapped to a smaller width\", main_footer = \"Footnote that is wider than expected for this table.\", prov_footer = \"Provenance footer material that is also wider than expected for this table.\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"RACE\", split_fun = drop_split_levels) %>% analyze( c(\"AGE\", \"EOSDY\"), na_str = \"Very long cell contents to_be_wrapped_and_splitted\", inclNAs = TRUE ) %>% build_table(trimmed_data) wide_tbl # Title that is too long and also needs to be wrapped to a smaller width # Subtitle that is also long and also needs to be wrapped to a smaller width # # ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— # Incredibly long column name to be wrapped This_column_name_should_be_split_somewhere C: Combination # ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN # AGE # Mean 32.50 36.68 36.99 # EOSDY # Mean Very long cell contents to_be_wrapped_and_splitted Very long cell contents to_be_wrapped_and_splitted Very long cell contents to_be_wrapped_and_splitted # BLACK OR AFRICAN AMERICAN # AGE # Mean 34.27 34.93 33.71 # EOSDY # Mean Very long cell contents to_be_wrapped_and_splitted Very long cell contents to_be_wrapped_and_splitted Very long cell contents to_be_wrapped_and_splitted # ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— # # Footnote that is wider than expected for this table. # # Provenance footer material that is also wider than expected for this table."},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"cell-label-wrapping","dir":"Articles","previous_headings":"Customizing Appearance > Cell, Label, and Annotation Wrapping","what":"Cell & Label Wrapping","title":"Customizing Appearance","text":"width rendered table can customized wrapping column widths. done setting custom width values via widths argument toString() function. length vector passed widths argument must equal total number columns table, including row labels column, value vector corresponding maximum width (characters) allowed column, left right. Similarly, wrapping can applied exporting table via one four export_as_* functions implementing pagination via paginate_table() function rtables package. cases, rendered column widths set using colwidths argument takes input format widths argument toString(). example, wide_tbl four columns (1 row label column 3 content columns) set widths use rendered table. set width row label column 10 characters widths 3 content columns 8 characters. words longer specified width broken continued following line. default 3 spaces separating columns rendered table can customized via col_gap argument toString() width customization desired. resulting output can see table correctly rendered using wrapping total width 43 characters, titles footers remain wider rendered table.","code":"result_wrap_cells <- toString(wide_tbl, widths = c(10, 8, 8, 8)) matrix_wrap_cells <- matrix(strsplit(result_wrap_cells, \"\\n\")[[1]], ncol = 1) matrix_wrap_cells # [,1] # [1,] \"Title that is too long and also needs to be wrapped to a smaller width\" # [2,] \"Subtitle that is also long and also needs to be wrapped to a smaller width\" # [3,] \"\" # [4,] \"———————————————————————————————————————————\" # [5,] \" Incredib This_col \" # [6,] \" ly long umn_name \" # [7,] \" column _should_ \" # [8,] \" name be_split \" # [9,] \" to be _somewhe C: Combi\" # [10,] \" wrapped re nation \" # [11,] \"———————————————————————————————————————————\" # [12,] \"ASIAN \" # [13,] \" AGE \" # [14,] \" Mean 32.50 36.68 36.99 \" # [15,] \" EOSDY \" # [16,] \" Mean Very Very Very \" # [17,] \" long long long \" # [18,] \" cell cell cell \" # [19,] \" contents contents contents\" # [20,] \" to_be_wr to_be_wr to_be_wr\" # [21,] \" apped_an apped_an apped_an\" # [22,] \" d_splitt d_splitt d_splitt\" # [23,] \" ed ed ed \" # [24,] \"BLACK OR \" # [25,] \"AFRICAN \" # [26,] \"AMERICAN \" # [27,] \" AGE \" # [28,] \" Mean 34.27 34.93 33.71 \" # [29,] \" EOSDY \" # [30,] \" Mean Very Very Very \" # [31,] \" long long long \" # [32,] \" cell cell cell \" # [33,] \" contents contents contents\" # [34,] \" to_be_wr to_be_wr to_be_wr\" # [35,] \" apped_an apped_an apped_an\" # [36,] \" d_splitt d_splitt d_splitt\" # [37,] \" ed ed ed \" # [38,] \"———————————————————————————————————————————\" # [39,] \"\" # [40,] \"Footnote that is wider than expected for this table.\" # [41,] \"\" # [42,] \"Provenance footer material that is also wider than expected for this table.\""},{"path":"https://insightsengineering.github.io/rtables/articles/custom_appearance.html","id":"title-footer-wrapping","dir":"Articles","previous_headings":"Customizing Appearance > Cell, Label, and Annotation Wrapping","what":"Title & Footer Wrapping","title":"Customizing Appearance","text":"addition wrapping column widths, titles footers can wrapped setting tf_wrap = TRUE toString() setting max_width argument toString() maximum width (characters) allowed titles/footers. four export_as_* functions paginate_table() can also wrap titles/footers setting two arguments. following code, set max_width = 43 rendered table annotations maximum width 43 characters.","code":"result_wrap_cells_tf <- toString( wide_tbl, widths = c(10, 8, 8, 8), tf_wrap = TRUE, max_width = 43 ) matrix_wrap_cells_tf <- matrix(strsplit(result_wrap_cells_tf, \"\\n\")[[1]], ncol = 1) matrix_wrap_cells_tf # [,1] # [1,] \"Title that is too long and also needs to be\" # [2,] \"wrapped to a smaller width\" # [3,] \"Subtitle that is also long and also needs\" # [4,] \"to be wrapped to a smaller width\" # [5,] \"\" # [6,] \"———————————————————————————————————————————\" # [7,] \" Incredib This_col \" # [8,] \" ly long umn_name \" # [9,] \" column _should_ \" # [10,] \" name be_split \" # [11,] \" to be _somewhe C: Combi\" # [12,] \" wrapped re nation \" # [13,] \"———————————————————————————————————————————\" # [14,] \"ASIAN \" # [15,] \" AGE \" # [16,] \" Mean 32.50 36.68 36.99 \" # [17,] \" EOSDY \" # [18,] \" Mean Very Very Very \" # [19,] \" long long long \" # [20,] \" cell cell cell \" # [21,] \" contents contents contents\" # [22,] \" to_be_wr to_be_wr to_be_wr\" # [23,] \" apped_an apped_an apped_an\" # [24,] \" d_splitt d_splitt d_splitt\" # [25,] \" ed ed ed \" # [26,] \"BLACK OR \" # [27,] \"AFRICAN \" # [28,] \"AMERICAN \" # [29,] \" AGE \" # [30,] \" Mean 34.27 34.93 33.71 \" # [31,] \" EOSDY \" # [32,] \" Mean Very Very Very \" # [33,] \" long long long \" # [34,] \" cell cell cell \" # [35,] \" contents contents contents\" # [36,] \" to_be_wr to_be_wr to_be_wr\" # [37,] \" apped_an apped_an apped_an\" # [38,] \" d_splitt d_splitt d_splitt\" # [39,] \" ed ed ed \" # [40,] \"———————————————————————————————————————————\" # [41,] \"\" # [42,] \"Footnote that is wider than expected for\" # [43,] \"this table.\" # [44,] \"\" # [45,] \"Provenance footer material that is also\" # [46,] \"wider than expected for this table.\""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"debugging","dir":"Articles > Dev-guide","previous_headings":"","what":"Debugging","title":"Debugging in `rtables` and Beyond","text":"short non-comprehensive guide debugging rtables. Regardless, considered valid personal use discretion.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"coding-in-practice","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"Coding in Practice","title":"Debugging in `rtables` and Beyond","text":"easy read find problems clever impossible debug","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"some-definitions","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"Some Definitions","title":"Debugging in `rtables` and Beyond","text":"Coding Error - Code intended -> Bug punch card Unexpected Input - Defensive programming FAIL FAST FAIL LOUD (FFFL) -> useful time consuming Bug Dependency -> never use dependencies can!","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"considerations-about-fffl","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"Considerations About FFFL","title":"Debugging in `rtables` and Beyond","text":"Errors close possible source. example, bad inputs found early. worst possible example software silently giving incorrect results. Common things can catch early missing values, column length == 0, length > 1.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"general-suggestions","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"General Suggestions","title":"Debugging in `rtables` and Beyond","text":"Robust code base attempt possibly problematic operations. Read Error Messages debugcall can add signature (formals) trace powerful can add reaction tracer good precise find happens options(error = recover) one best tools debug core tool developing allows step point function call sequence. dump.frames debugger: saves file object call debugger step recover.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"warn-global-option","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"warn Global Option","title":"Debugging in `rtables` and Beyond","text":"<0 ignored 0 top level function call 1 immediately occur >=2 throws errors <<- recover debugger gives global environment","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"lo-fi-debugging","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"lo-fi debugging","title":"Debugging in `rtables` and Beyond","text":"PRINT / CAT always low level debugging can used. helpful server jobs maybe terminal console output available browser() can used. example, can print position state function certain point untill find break point. comment blocks -> work pipes (can use identity() step nothing break pipes) browser() bombing","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"regression-tests","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"Regression Tests","title":"Debugging in `rtables` and Beyond","text":"Almost every bug become regression test.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"debugging-with-pipes","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"Debugging with Pipes","title":"Debugging in `rtables` and Beyond","text":"Pipes better write code horrible debug T pipe %T>% print midway debug_pipe() -> like T pipe going browser()","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"shiny-debugging","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"Shiny Debugging","title":"Debugging in `rtables` and Beyond","text":"difficult due reactivity.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"general-suggestion","dir":"Articles > Dev-guide","previous_headings":"Debugging","what":"General Suggestion","title":"Debugging in `rtables` and Beyond","text":"CLEVER CODE - , CLEVER ALSO SUBJECTIVE CHANGE TIME.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_debug_rtables.html","id":"debugging-in-rtables","dir":"Articles > Dev-guide","previous_headings":"","what":"Debugging in rtables","title":"Debugging in `rtables` and Beyond","text":"invite smart developer use provided examples way get “interactive” dynamic view internal algorithms routinely executed constructing tables rtables. achieved using browser() debugonce() internal exported functions (rtables::: rtables::), see moment. invite continuously autonomously explore multiple S3 S4 objects constitute complexity power rtables. , use following functions: methods(generic_function): function lists methods available generic function. Specifically S4 generic functions, showMethods(generic_function) gives detailed information method (e.g. inheritance). class(object): function returns class object. class one built-classes R, can use information search documentation examples. help(class) may informative call documentation specific class. Similarly, ? operator bring documentation page different S4 methods. S3 methods necessary postfix class name dot (e.g. ?summary.lm). getClass(class): describes type class compact way, slots , relationships may classes may inherit inherited . getClass(object) can see values slots object assigned. possible use str(object, max.level = 2) see less formal compact descriptions slots, may problematic one objects class slots. Hence, maximum number levels always limited 2 3 (max.level = 2). Similarly, attributes() can used retrieve information, need remember storing important variables way encouraged. Information regarding type class can retrieved mode() indirectly summary() .S4(). *getAnywhere(function) useful get source code internal functions specific generics. works well S3 methods, display relevant namespace methods found. Similarly, getMethod(S4_generic, S4_class) can retrieve source code class-specific S4 methods. eval(debugcall(generic_function(obj))): useful way browse S4 method, specifically defined object, without manually insert browser() code. also possible similarly R > 3.4.0 debug*() calls can triggering signature (class) specified. modern simplified wrappers tracing function trace().","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_notes.html","id":"disclaimer","dir":"Articles > Dev-guide","previous_headings":"","what":"Disclaimer","title":"Sparse notes on {rtables} internals","text":"collection notes divided issues working document end dev vignette one day.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_notes.html","id":"section_div-notes","dir":"Articles > Dev-guide","previous_headings":"","what":"section_div notes","title":"Sparse notes on {rtables} internals","text":"Everything layout built split objects, reside 00_tabletrees.R. section_div defined internally split object child_section_div assigned NA_character default. needs split objects need separator divisor. Object-wise, virtual class Split contains section_div following subclasses. tagged “X” constructor allows section_div assigned value different NA_character, “NX” otherwise. can updated related layout functions. important, covered tests analyze split_rows_by. Now relevant understand information saved table object built build_table. need see present assigned. Let’s go back 00tabletree.Rand look trailing_section_div. classes definitions goes, notice search trailing_section_div present virtual classes TableRow VTableTree. following class hierarchy makes `trailing_section_div: Always check constructors finding classes. case example, DataRow ContentRow share constructor, need add identical getter setters two classes virtual class TableRow. Different story LabelRow needs handle differently. Now, understand two feature, lets see structure table built section dividers: , show trailing_section_div methods TableRow virtual object, LabelRow, VTableTree. three make whole section_div structure VTableTree present TableTree ElementaryTable two main table objects. NA_character_ section_div printed split divisions. LabelRow TableRow different assignment allows row-wise modification separators. special case ContentRow, represented content_table(obj) one-line ElementaryTable, label row turned . Please take moment check following setter: only_sep_sections parameter used change separators (splits) data rows. happening forcefully set TRUE, automatically activated section_div(tbl) <- char_v character vector length < nrow(tbl). Notice exception ContentRow activated switcher is_content_table. content rows visible label row. see main table structure change two blocks depending only_sep_sections. TRUE VTableTree modified leading split section separators modified. Also consider looking section_div getter tests test-accessors.R insights structure. Also understand exactly bound output, please check result make_row_df() column trailing_sep. Indeed, alternative iterative method used make_row_df retrieve information separators table row. trailing separator definition, added header_section_div function parameter basic_table, possibly add empty line header (e.g. header_section_div(tbl) = \" \"). trailing separator, separator added header. close circle, please check trailing_sep header_section_div propagated printed/used formatters::toString.","code":"library(rtables) ## Loading required package: formatters ## Loading required package: magrittr ## ## Attaching package: 'rtables' ## The following object is masked from 'package:utils': ## ## str getClass(\"Split\") ## Virtual Class \"Split\" [package \"rtables\"] ## ## Slots: ## ## Name: payload name split_label ## Class: ANY character character ## ## Name: split_format split_na_str split_label_position ## Class: FormatSpec character character ## ## Name: content_fun content_format content_na_str ## Class: listOrNULL FormatSpec character ## ## Name: content_var label_children extra_args ## Class: character logical list ## ## Name: indent_modifier content_indent_modifier content_extra_args ## Class: integer integer list ## ## Name: page_title_prefix child_section_div ## Class: character character ## ## Known Subclasses: ## Class \"CustomizableSplit\", directly ## Class \"AllSplit\", directly ## Class \"VarStaticCutSplit\", directly ## Class \"VarDynCutSplit\", directly ## Class \"VAnalyzeSplit\", directly ## Class \"CompoundSplit\", directly ## Class \"VarLevelSplit\", by class \"CustomizableSplit\", distance 2 ## Class \"MultiVarSplit\", by class \"CustomizableSplit\", distance 2 ## Class \"RootSplit\", by class \"AllSplit\", distance 2 ## Class \"ManualSplit\", by class \"AllSplit\", distance 2 ## Class \"CumulativeCutSplit\", by class \"VarStaticCutSplit\", distance 2 ## Class \"AnalyzeVarSplit\", by class \"VAnalyzeSplit\", distance 2 ## Class \"AnalyzeColVarSplit\", by class \"VAnalyzeSplit\", distance 2 ## Class \"AnalyzeMultiVars\", by class \"CompoundSplit\", distance 2 ## Class \"VarLevWBaselineSplit\", by class \"VarLevelSplit\", distance 3 # Known Subclasses: #? Class \"CustomizableSplit\", directly # vclass used for grouping different split types (I guess) # Class \"AllSplit\", directly # NX # Class \"VarStaticCutSplit\", directly # X via make_static_cut_split # Class \"VarDynCutSplit\", directly # X # Class \"VAnalyzeSplit\", directly # X #? Class \"CompoundSplit\", directly # Used only for AnalyzeMultiVars (maybe not needed?) # Class \"VarLevelSplit\", by class \"CustomizableSplit\", distance 2 # X # Class \"MultiVarSplit\", by class \"CustomizableSplit\", distance 2 # X # Class \"RootSplit\", by class \"AllSplit\", distance 2 # NX # Class \"ManualSplit\", by class \"AllSplit\", distance 2 # X # Class \"CumulativeCutSplit\", by class \"VarStaticCutSplit\", distance 2 # X via make_static_cut_split # Class \"AnalyzeVarSplit\", by class \"VAnalyzeSplit\", distance 2 # Virtual # Class \"AnalyzeColVarSplit\", by class \"VAnalyzeSplit\", distance 2 # X # Class \"AnalyzeMultiVars\", by class \"CompoundSplit\", distance 2 # X # Class \"VarLevWBaselineSplit\", by class \"VarLevelSplit\", distance 3 # NX getClass(\"TableRow\") ## Virtual Class \"TableRow\" [package \"rtables\"] ## ## Slots: ## ## Name: leaf_value var_analyzed label ## Class: ANY character character ## ## Name: row_footnotes trailing_section_div level ## Class: list character integer ## ## Name: name col_info format ## Class: character InstantiatedColumnInfo FormatSpec ## ## Name: na_str indent_modifier table_inset ## Class: character integer integer ## ## Extends: ## Class \"VLeaf\", directly ## Class \"VTableNodeInfo\", directly ## Class \"VNodeInfo\", by class \"VLeaf\", distance 2 ## ## Known Subclasses: \"DataRow\", \"ContentRow\", \"LabelRow\" # Extends: # Class \"VLeaf\", directly # Class \"VTableNodeInfo\", directly # Class \"VNodeInfo\", by class \"VLeaf\", distance 2 # # Known Subclasses: \"DataRow\", \"ContentRow\", \"LabelRow\" getClass(\"VTableTree\") ## Virtual Class \"VTableTree\" [package \"rtables\"] ## ## Slots: ## ## Name: children rowspans labelrow ## Class: list data.frame LabelRow ## ## Name: page_titles horizontal_sep header_section_div ## Class: character character character ## ## Name: trailing_section_div col_info format ## Class: character InstantiatedColumnInfo FormatSpec ## ## Name: na_str indent_modifier table_inset ## Class: character integer integer ## ## Name: level name main_title ## Class: integer character character ## ## Name: subtitles main_footer provenance_footer ## Class: character character character ## ## Extends: ## Class \"VTableNodeInfo\", directly ## Class \"VTree\", directly ## Class \"VTitleFooter\", directly ## Class \"VNodeInfo\", by class \"VTableNodeInfo\", distance 2 ## ## Known Subclasses: \"ElementaryTable\", \"TableTree\" # Extends: # Class \"VTableNodeInfo\", directly # Class \"VTree\", directly # Class \"VTitleFooter\", directly # Class \"VNodeInfo\", by class \"VTableNodeInfo\", distance 2 # # Known Subclasses: \"ElementaryTable\", \"TableTree\" lyt <- basic_table() %>% split_rows_by(\"ARM\", section_div = \"+\") %>% split_rows_by(\"STRATA1\", section_div = \"\") %>% analyze(\"AGE\", afun = function(x) list(\"Mean\" = mean(x), \"Standard deviation\" = sd(x)), format = list(\"Mean\" = \"xx.\", \"Standard deviation\" = \"xx.\"), section_div = \"~\") tbl <- build_table(lyt, DM) print(tbl) ## all obs ## ———————————————————————————————— ## A: Drug X ## A ## Mean 33 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 7 ## ## B ## Mean 35 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 7 ## ## C ## Mean 36 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 9 ## ++++++++++++++++++++++++++++++++ ## B: Placebo ## A ## Mean 32 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 6 ## ## B ## Mean 32 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 6 ## ## C ## Mean 34 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 7 ## ++++++++++++++++++++++++++++++++ ## C: Combination ## A ## Mean 36 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 7 ## ## B ## Mean 34 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 6 ## ## C ## Mean 34 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Standard deviation 6 print(class(tbl)) # TableTree ## [1] \"TableTree\" ## attr(,\"package\") ## [1] \"rtables\" # methods(\"trailing_section_div\") # to see this please do devtools::load_all() # [1] trailing_section_div,LabelRow-method # trailing_section_div,TableRow-method # trailing_section_div,VTableTree-method setMethod(\"section_div<-\", \"VTableTree\", function(obj, value, only_sep_sections = FALSE) { char_v <- as.character(value) tree_depths <- unname(vapply(collect_leaves(obj), tt_level, numeric(1))) max_tree_depth <- max(tree_depths) stopifnot(is.logical(only_sep_sections)) .check_char_vector_for_section_div(char_v, max_tree_depth, nrow(obj)) # Automatic establishment of intent if (length(char_v) < nrow(obj)) { only_sep_sections <- TRUE } # Case where only separators or splits need to change externally if (only_sep_sections && length(char_v) < nrow(obj)) { if (length(char_v) == 1) { char_v <- rep(char_v, max_tree_depth - 1) # -1 is the data row } # Case where char_v is longer than the max depth char_v <- char_v[seq_len(min(max_tree_depth, length(char_v)))] # Filling up with NAs the rest of the tree depth section div chr vector missing_char_v_len <- max_tree_depth - length(char_v) char_v <- c(char_v, rep(NA_character_, missing_char_v_len)) # char_v <- unlist( # lapply(tree_depths, function(tree_depth_i) char_v[seq_len(tree_depth_i)]), # use.names = FALSE # ) } # Retrieving if it is a contentRow (no need for labelrow to be visible in this case) content_row_tbl <- content_table(obj) is_content_table <- isS4(content_row_tbl) && nrow(content_row_tbl) > 0 # Main table structure change if (labelrow_visible(obj) || is_content_table) { if (only_sep_sections) { # Only tables are modified trailing_section_div(tt_labelrow(obj)) <- NA_character_ trailing_section_div(obj) <- char_v[1] section_div(tree_children(obj), only_sep_sections = only_sep_sections) <- char_v[-1] } else { # All leaves are modified trailing_section_div(tt_labelrow(obj)) <- char_v[1] trailing_section_div(obj) <- NA_character_ section_div(tree_children(obj), only_sep_sections = only_sep_sections) <- char_v[-1] } } else { section_div(tree_children(obj), only_sep_sections = only_sep_sections) <- char_v } obj })"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"disclaimer","dir":"Articles > Dev-guide","previous_headings":"","what":"Disclaimer","title":"Split Machinery","text":"article intended use developers contain low-level explanations topics covered. user-friendly vignettes, please see Articles page rtables website. code prose appears version article main branch repository may reflect specific state things can less recent. guide describes important pieces split machinery unlikely change. Regardless, invite reader keep mind current repository code may drifted following material document, always best practice read code directly main. Please keep mind rtables still active development, seen efforts multiple contributors across different years. Therefore, may legacy mechanisms ongoing transformations look different future. working document may subjected deprecation updates, keep xxx comments indicate placeholders warnings -’s need work.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"introduction","dir":"Articles > Dev-guide","previous_headings":"","what":"Introduction","title":"Split Machinery","text":"scope article understanding rtables creates facets splitting incoming data hierarchical groups go root node singular rcells. latter level, also called leaf-level, contains final partition subjected analysis functions. details user perspective can found Split Functions vignette function documentation like ?split_rows_by ?split_funcs. following article describe split machinery works row domain. information split machinery works column domain covered separate article.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"process-and-methods","dir":"Articles > Dev-guide","previous_headings":"","what":"Process and Methods","title":"Split Machinery","text":"Beforehand, encourage reader familiarize Debugging rtables(xxx link ) article rtables Developers Guide. document generally valid R programming, tailored study understand complex packages rely heavily S3 S4 object programming like rtables. , explore study split machinery growing amount complexity, following relevant functions methods throughout execution. going basic complex discussing important special cases, hope able give good understanding split machinery works. practice, majority split engine resides source file R/split_funs.R, occasional incursion R/make_split_fun.R custom split function creation rarer references general tabulation files.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"do_split","dir":"Articles > Dev-guide","previous_headings":"","what":"do_split","title":"Split Machinery","text":"split machinery fundamental rtables relevant functions like do_split executed even split requested. following example shows can enter do_split start understanding class hierarchy main split engine. following code, copied do_split function code allow reader go general structure enhanced comments sections. section code reflects roughly one section article. see input parameters used. important parameters spl df - split objects input data.frame, respectively.","code":"library(rtables) # debugonce(rtables:::do_split) # Uncomment me to enter the function!!! basic_table() %>% build_table(DM) ## all obs ## —————————— # rtables 0.6.2 ### NB This is called at EACH level of recursive splitting do_split <- function(spl, df, vals = NULL, labels = NULL, trim = FALSE, spl_context) { # - CHECKS - # ## This will error if, e.g., df does not have columns ## required by spl, or generally any time the split (spl) ## can not be applied to df check_validsplit(spl, df) # - SPLIT FUNCTION - # ## In special cases, we need to partition data (split) ## in a very specific way, e.g. depending on the data or ## external values. These can be achieved by using a custom ## split function. ## note the <- here!!! if (!is.null(splfun <- split_fun(spl))) { ## Currently split functions take df, vals, labels and ## return list(values = ..., datasplit = ..., labels = ...), ## with an optional additional 'extras' element if (func_takes(splfun, \".spl_context\")) { ret <- tryCatch( splfun(df, spl, vals, labels, trim = trim, .spl_context = spl_context ), error = function(e) e ) ## rawvalues(spl_context)) } else { ret <- tryCatch(splfun(df, spl, vals, labels, trim = trim), error = function(e) e ) } if (is(ret, \"error\")) { stop( \"Error applying custom split function: \", ret$message, \"\\n\\tsplit: \", class(spl), \" (\", payloadmsg(spl), \")\\n\", \"\\toccured at path: \", spl_context_to_disp_path(spl_context), \"\\n\" ) } } else { # - .apply_split_inner - # ## This is called when no split function is provided. Please note that this function ## will also probably be called when the split function is provided, as long as the ## main splitting method is not willingly modified by the split function. ret <- .apply_split_inner(df = df, spl = spl, vals = vals, labels = labels, trim = trim) } # - EXTRA - # ## this adds .ref_full and .in_ref_col if (is(spl, \"VarLevWBaselineSplit\")) { ret <- .add_ref_extras(spl, df, ret) } # - FIXUPVALS - # ## This: ## - guarantees that ret$values contains SplitValue objects ## - removes the extras element since its redundant after the above ## - ensures datasplit and values lists are named according to labels ## - ensures labels are character not factor ret <- .fixupvals(ret) # - RETURN - # ret }"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"checks-and-classes","dir":"Articles > Dev-guide","previous_headings":"do_split","what":"Checks and Classes","title":"Split Machinery","text":"start looking first function called do_split. give us good overview split defined. function , course, check function (check_validsplit) used verify split valid data. following describe split-class hierarchy step--step, invite reader explore well. Let’s first search package check_validsplit. find defined generic R/split_funs.R, applied following “split” classes: VarLevelSplit, MultiVarSplit, VAnalyzeSplit, CompoundSplit, Split. Another way find information, useful spread complicated objects, using showMethods(check_validsplit). virtual class VAnalyzeSplit (convention virtual classes start “V”) defines main parent analysis split discuss detail related vignette vignette() (xxx). , can see analyze() calls actually mimic split objects create different results specific final split (node). Now, notice check_validsplit also called another location, main R/tt_dotabulation.R source file. something related making “analyze” rows mainly checks VAnalyzeSplit (link tabulation dev guide xxx). discuss classes appear examples (link class hierarchy xxx). moment, see class(spl) (main do_split function) dealing AllSplit object. calling showMethods(check_validsplit) produce following: means listed classes dedicated definition check_validsplit may largely differ others. class AllSplit function definition inherited Split class. Therefore, understand AllSplit parent class Split. one first definitions virtual class package one include “V” prefix. classes defined along constructors R/00tabletrees.R. Reading AllSplit structured can useful understanding split objects expected work. Please see comments following: can also print information calling getClass(\"AllSplit\") general slot definition, calling getClass(spl). Note first call give also lot information class hierarchy. information regarding class hierarchy, please refer relevant article (xxx). discuss majority slots end document. Now, let’s see can find values described constructor within object. , show compact representation given str. multiple hierarchical slots contain objects , calling str much less informative maximum level nesting set (e.g. max.level = 2). Details slots become necessary future examples, deal time. Now, gave hint complex class hierarchy makes rtables, explore autonomously. Let’s go forward do_split. case, AllSplit inherited Split, sure called function following (read comment!):","code":"# rtables 0.6.2 Function: check_validsplit (package rtables) spl=\"AllSplit\" (inherited from: spl=\"Split\") spl=\"CompoundSplit\" spl=\"MultiVarSplit\" spl=\"Split\" spl=\"VAnalyzeSplit\" spl=\"VarLevelSplit\" # rtables 0.6.2 setClass(\"AllSplit\", contains = \"Split\") AllSplit <- function(split_label = \"\", cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = NULL, extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = \"\", cextra_args = list(), ...) { if (is.null(split_name)) { # If the split has no name if (nzchar(split_label)) { # (std is \"\") split_name <- split_label } else { split_name <- \"all obs\" # No label, a standard split with all # observations is assigned. } } new(\"AllSplit\", split_label = split_label, content_fun = cfun, content_format = cformat, content_na_str = cna_str, split_format = split_format, split_na_str = split_na_str, name = split_name, label_children = FALSE, extra_args = extra_args, indent_modifier = as.integer(indent_mod), content_indent_modifier = as.integer(cindent_mod), content_var = cvar, split_label_position = \"hidden\", content_extra_args = cextra_args, page_title_prefix = NA_character_, child_section_div = NA_character_ ) } # rtables 0.6.2 Browse[2]> str(spl, max.level = 2) Formal class 'AllSplit' [package \"rtables\"] with 17 slots ..@ payload : NULL ..@ name : chr \"all obs\" ..@ split_label : chr \"\" ..@ split_format : NULL ..@ split_na_str : chr NA ..@ split_label_position : chr \"hidden\" ..@ content_fun : NULL ..@ content_format : NULL ..@ content_na_str : chr NA ..@ content_var : chr \"\" ..@ label_children : logi FALSE ..@ extra_args : list() ..@ indent_modifier : int 0 ..@ content_indent_modifier: int 0 ..@ content_extra_args : list() ..@ page_title_prefix : chr NA ..@ child_section_div : chr NA # rtables 0.6.2 ## Default does nothing, add methods as they become required setMethod( \"check_validsplit\", \"Split\", function(spl, df) invisible(NULL) )"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"split-functions-and--apply_split_inner","dir":"Articles > Dev-guide","previous_headings":"do_split","what":"Split Functions and .apply_split_inner","title":"Split Machinery","text":"diving custom split functions, need take moment analyze .apply_split_inner works. function routinely called whether split function. Let’s see case entering debugonce(.apply_split_inner). course, still currently browsing within do_split debug mode first example. print comment function following: reading .apply_split_inner, see fundamental functions - defined strictly internal use (convention start “.”) - generics depend kind split input. R/split_funs.R kind groups generic definitions beginning file. functions main dispatchers majority split machinery. clear example shows using S4 logic enables better clarity flexibility programming, allowing easy extension program. compactness also show showMethods result generic. Now, know .applysplit_extras function called first. specify vals therefore NULL. S4 generic function can seen showMethod(.applysplit_extras), definition can seen following: .applysplit_extras, simply extracts extra arguments split objects assigns relative split values. function covered detail later section. still split values available, function exit empty split. Otherwise, data divided different splits data subsets (facets) .applysplit_datapart. current example, resulting list comprises whole input dataset (getMethod(\".applysplit_datapart\", \"AllSplit\") list evident: function (spl, df, vals) list(df)). Next, split labels checked. present, split values (vals) used .applysplit_partlabels, transformed .character(vals) applied Split object. Otherwise, inserted labels checked names split values. Lastly, split values ordered according spl_child_order. case, concerns general AllSplit, sorting happen, .e. dependent simply number split values (seq_along(vals)).","code":"# rtables 0.6.2 .apply_split_inner <- function(spl, df, vals = NULL, labels = NULL, trim = FALSE) { # - INPUTS - # # In this case .applysplit_rawvals will attempt to find the split values if vals is NULL. # Please notice that there may be a non-mutually exclusive set or subset of elements that # will constitute the split. # - SPLIT VALS - # ## Try to calculate values first - most of the time we can if (is.null(vals)) { vals <- .applysplit_rawvals(spl, df) } # - EXTRA PARAMETERS - # # This call extracts extra parameters from the split, according to the split values extr <- .applysplit_extras(spl, df, vals) # If there are no values to do the split upon, we return an empty final split if (is.null(vals)) { return(list( values = list(), datasplit = list(), labels = list(), extras = list() )) } # - DATA SUBSETTING - # dpart <- .applysplit_datapart(spl, df, vals) # - LABEL RETRIEVAL - # if (is.null(labels)) { labels <- .applysplit_partlabels(spl, df, vals, labels) } else { stopifnot(names(labels) == names(vals)) } # - TRIM - # ## Get rid of columns that would not have any observations, ## but only if there were any rows to start with - if not ## we're in a manually constructed table column tree if (trim) { hasdata <- sapply(dpart, function(x) nrow(x) > 0) if (nrow(df) > 0 && length(dpart) > sum(hasdata)) { # some empties dpart <- dpart[hasdata] vals <- vals[hasdata] extr <- extr[hasdata] labels <- labels[hasdata] } } # - ORDER RESULTS - # # Finds relevant order depending on spl_child_order() if (is.null(spl_child_order(spl)) || is(spl, \"AllSplit\")) { vord <- seq_along(vals) } else { vord <- match( spl_child_order(spl), vals ) vord <- vord[!is.na(vord)] } ## FIXME: should be an S4 object, not a list ret <- list( values = vals[vord], datasplit = dpart[vord], labels = labels[vord], extras = extr[vord] ) ret } # rtables 0.6.2 # Retrieves the values that will constitute the splits (facets), not necessarily a unique list. # They could come from the data cuts for example -> it can be anything that produces a set of strings. setGeneric( \".applysplit_rawvals\", function(spl, df) standardGeneric(\".applysplit_rawvals\") ) # Browse[2]> showMethods(.applysplit_rawvals) # Function: .applysplit_rawvals (package rtables) # spl=\"AllSplit\" # spl=\"ManualSplit\" # spl=\"MultiVarSplit\" # spl=\"VAnalyzeSplit\" # spl=\"VarLevelSplit\" # spl=\"VarStaticCutSplit\" # Nothing here is inherited from the virtual class Split!!! # Contains the subset of the data (default, but these can overlap and can also NOT be mutually exclusive). setGeneric( \".applysplit_datapart\", function(spl, df, vals) standardGeneric(\".applysplit_datapart\") ) # Same as .applysplit_rawvals # Extract the extra parameter for the split setGeneric( \".applysplit_extras\", function(spl, df, vals) standardGeneric(\".applysplit_extras\") ) # Browse[2]> showMethods(.applysplit_extras) # Function: .applysplit_extras (package rtables) # spl=\"AllSplit\" # (inherited from: spl=\"Split\") # spl=\"Split\" # This means there is only a function for the virtual class Split. # So all splits behave the same!!! # Split label retrieval and assignment if visible. setGeneric( \".applysplit_partlabels\", function(spl, df, vals, labels) standardGeneric(\".applysplit_partlabels\") ) # Browse[2]> showMethods(.applysplit_partlabels) # Function: .applysplit_partlabels (package rtables) # spl=\"AllSplit\" # (inherited from: spl=\"Split\") # spl=\"MultiVarSplit\" # spl=\"Split\" # spl=\"VarLevelSplit\" setGeneric( \"check_validsplit\", # our friend function(spl, df) standardGeneric(\"check_validsplit\") ) # Note: check_validsplit is an internal function but may one day be exported. # This is why it does not have the \".\" prefix. setGeneric( \".applysplit_ref_vals\", function(spl, df, vals) standardGeneric(\".applysplit_ref_vals\") ) # Browse[2]> showMethods(.applysplit_ref_vals) # Function: .applysplit_ref_vals (package rtables) # spl=\"Split\" # spl=\"VarLevWBaselineSplit\" # rtables 0.6.2 Browse[3]> getMethod(\".applysplit_rawvals\", \"AllSplit\") Method Definition: function (spl, df) obj_name(spl) Signatures: spl target \"AllSplit\" defined \"AllSplit\" # What is obj_name -> slot in spl Browse[3]> obj_name(spl) [1] \"all obs\" # coming from Browse[3]> getMethod(\"obj_name\", \"Split\") Method Definition: function (obj) obj@name ##### Slot that we could see from str(spl, max.level = 2) Signatures: obj target \"Split\" defined \"Split\""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"a-simple-split","dir":"Articles > Dev-guide","previous_headings":"","what":"A Simple Split","title":"Split Machinery","text":"following, demonstrate row splits work using features already described. add two splits see behavior do_split changes. Note add analyze call split behave , giving empty table observations. default, calling analyze variable calculate mean data subset generated splits. want go beyond first call do_split design applied observations, purpose generating root split contains data splits (indeed AllSplit). achieve use debug(rtables:::do_split) instead debugonce(rtables:::do_split) need step splits. Alternatively, possible use powerful trace function enter cases input specific class. , following can used: trace(\"do_split\", quote((!(spl, \"AllSplit\")) browser()), = asNamespace(\"rtables\")). Note specify namespace . Multiple tracer elements can added expression(E1, E2), c(quote(E1), quote(E2)). Specific steps can specified parameter. Remember call untrace(\"do_split\", quote((!(spl, \"AllSplit\")) browser()), = asNamespace(\"rtables\")) finished remove trace. continuing, want check formal class spl. , can directly infer class different now (VarLevelSplit) understand split label hidden (split_label_position slot). Moreover, see specific value order specific split values. VarLevelSplit also seems three slots AllSplit. precisely? Remember always check constructor class definition R/00tabletrees.R exploratory tools suffice. Now, check_validsplit(spl, df) use different method (getMethod(\"check_validsplit\", \"VarLevelSplit\")). uses internal utility function .checkvarsok check vars, .e. payload, actually present names(df). next relevant function .apply_split_inner, exactly changes using debugonce(.apply_split_inner). course, function called directly custom split function provided. Since parameter vals specified (NULL), split values retrieved df using split payload select specific columns (varvec <- df[[spl_payload(spl)]]). Whenever split values specified retrieved selected column unique values (character) levels (factor). Next, .applysplit_datapart creates named list facets data subsets. case, result actually mutually exclusive partition data. specify split values column content retrieved via unique (case character vector) levels (case factors). .applysplit_partlabels bit less linear take account possibility specified labels payload. Instead looking function source code getMethod(\".applysplit_partlabels\", \"VarLevelSplit\"), can enter S4 generic function debugging mode follows: case, final labels vals explicitly assigned. order retrieved split object (spl_child_order(spl)) matched current split values. returned list processed . continue next call do_split, procedure followed second ARM split. applied partition created first split. main df now constituted subset (facet) total data, determined first split. repeated iteratively many data splits requested. concluding iteration, take moment discuss detail .fixupvals(partinfo) works. generic function source code can easily accessed. suggest running debugonce(.fixupvals) understand practice. fundamental aspects .fixupvals(partinfo) follows: Ensures labels character factor. Ensures splits data list values named according labels. Guarantees ret$values contains SplitValue objects. Removes list element extra since now included SplitValue. Note function can occasionally called return object (named list now). course, first call checks applied.","code":"# rtables 0.6.2 library(rtables) library(dplyr) # This filter is added to avoid having too many calls to do_split DM_tmp <- DM %>% filter(ARM %in% names(table(DM$ARM)[1:2])) %>% # limit to two filter(SEX %in% c(\"M\", \"F\")) %>% # limit to two mutate(SEX = factor(SEX), ARM = factor(ARM)) # to drop unused levels # debug(rtables:::do_split) lyt <- basic_table() %>% split_rows_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(\"BMRKR1\") # analyze() is needed for the table to have non-label rows lyt %>% build_table(DM_tmp) ## all obs ## ———————————————————— ## A: Drug X ## F ## Mean 6.06 ## M ## Mean 5.42 ## B: Placebo ## F ## Mean 6.24 ## M ## Mean 5.97 # undebug(rtables:::do_split) # rtables 0.6.2 Browse[2]> str(spl, max.level = 2) Formal class 'VarLevelSplit' [package \"rtables\"] with 20 slots ..@ value_label_var : chr \"ARM\" ..@ value_order : chr [1:2] \"A: Drug X\" \"B: Placebo\" ..@ split_fun : NULL ..@ payload : chr \"ARM\" ..@ name : chr \"ARM\" ..@ split_label : chr \"ARM\" ..@ split_format : NULL ..@ split_na_str : chr NA ..@ split_label_position : chr \"hidden\" ..@ content_fun : NULL ..@ content_format : NULL ..@ content_na_str : chr NA ..@ content_var : chr \"\" ..@ label_children : logi NA ..@ extra_args : list() ..@ indent_modifier : int 0 ..@ content_indent_modifier: int 0 ..@ content_extra_args : list() ..@ page_title_prefix : chr NA ..@ child_section_div : chr NA # rtables 0.6.2 slots_as <- getSlots(\"AllSplit\") # inherits virtual class Split and is general class for all splits # getClass(\"CustomizableSplit\") # -> Extends: \"Split\", Known Subclasses: Class \"VarLevelSplit\", directly slots_cs <- getSlots(\"CustomizableSplit\") # Adds split function slots_vls <- getSlots(\"VarLevelSplit\") slots_cs[!(names(slots_cs) %in% names(slots_as))] # split_fun # \"functionOrNULL\" slots_vls[!(names(slots_vls) %in% names(slots_cs))] # value_label_var value_order # \"character\" \"ANY\" # rtables 0.6.2 eval(debugcall(.applysplit_partlabels(spl, df, vals, labels))) # We leave to the smart developer to see how the labels are assigned # Remember to undebugcall() similarly! # rtables 0.6.2 # Can find the following core function: # vals <- make_splvalue_vec(vals, extr, labels = labels) # ---> Main list of SplitValue objects: iterative call of # new(\"SplitValue\", value = val, extra = extr, label = label) # Structure of ret before calling .fixupvals Browse[2]> str(ret, max.level = 2) List of 4 $ values : chr [1:2] \"A: Drug X\" \"B: Placebo\" $ datasplit:List of 2 ..$ A: Drug X : tibble [121 × 8] (S3: tbl_df/tbl/data.frame) ..$ B: Placebo: tibble [106 × 8] (S3: tbl_df/tbl/data.frame) $ labels : Named chr [1:2] \"A: Drug X\" \"B: Placebo\" ..- attr(*, \"names\")= chr [1:2] \"A: Drug X\" \"B: Placebo\" $ extras :List of 2 ..$ : list() ..$ : list() # Structure of ret after the function call Browse[2]> str(.fixupvals(ret), max.level = 2) List of 3 $ values :List of 2 ..$ A: Drug X :Formal class 'SplitValue' [package \"rtables\"] with 3 slots ..$ B: Placebo:Formal class 'SplitValue' [package \"rtables\"] with 3 slots $ datasplit:List of 2 ..$ A: Drug X : tibble [121 × 8] (S3: tbl_df/tbl/data.frame) ..$ B: Placebo: tibble [106 × 8] (S3: tbl_df/tbl/data.frame) $ labels : Named chr [1:2] \"A: Drug X\" \"B: Placebo\" ..- attr(*, \"names\")= chr [1:2] \"A: Drug X\" \"B: Placebo\" # The SplitValue object is fundamental Browse[2]> str(ret$values) List of 2 $ A: Drug X :Formal class 'SplitValue' [package \"rtables\"] with 3 slots .. ..@ extra: list() .. ..@ value: chr \"A: Drug X\" .. ..@ label: chr \"A: Drug X\" $ B: Placebo:Formal class 'SplitValue' [package \"rtables\"] with 3 slots .. ..@ extra: list() .. ..@ value: chr \"B: Placebo\" .. ..@ label: chr \"B: Placebo\""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"pre-made-split-functions","dir":"Articles > Dev-guide","previous_headings":"A Simple Split","what":"Pre-Made Split Functions","title":"Split Machinery","text":"start examining split function already defined rtables. scope filtering specific values follows: root split, enter split based SEX. specified split function, can retrieve split function using splfun <- split_fun(spl) enter -else statement two possible cases: whether split context . cases, error catching framework used give informative errors case failure. Later see depth works. invite reader always keep eye spl_context, fundamental sophisticated splits, e.g. cases split depends mainly preceding splits values. split function called, please take moment look drop_split_levels defined. see function fundamentally wrapper .apply_split_inner drops empty factor levels, therefore avoiding empty splits. many pre-made split functions included rtables. list functions can found Split Functions vignette, via ?split_funcs. leave developer look split functions work, particular trim_levels_to_map may interest.","code":"library(rtables) # debug(rtables:::do_split) # uncomment to see into the main split function basic_table() %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% analyze(\"BMRKR1\") %>% build_table(DM) ## all obs ## ———————————————— ## F ## Mean 6.04 ## M ## Mean 5.64 # undebug(rtables:::do_split) # This produces the same output as before (when filters were used) # rtables 0.6.2 > drop_split_levels function(df, spl, vals = NULL, labels = NULL, trim = FALSE) { # Retrieve split column var <- spl_payload(spl) df2 <- df ## This call is exactly the one we used when filtering to get rid of empty levels df2[[var]] <- factor(df[[var]]) ## Our main function! .apply_split_inner(spl, df2, vals = vals, labels = labels, trim = trim ) }"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"creating-custom-split-functions","dir":"Articles > Dev-guide","previous_headings":"A Simple Split","what":"Creating Custom Split Functions","title":"Split Machinery","text":"Now create custom split function. Firstly, see system manages error messages. general understanding custom split functions created, please read Custom Split Functions section Advanced Usage vignette see ?custom_split_funs. following code use browser() enter custom split functions. invite reader activate options(error = recover) investigate cases encounter error. Note can revert default behavior restarting R session, caching default option value, using callr retrieve default follows: default_opts <- callr::r(function(){options()}); options(error = default_opts$error). commented debugging lines allow inspect error. Alternatively, using recover option allow possibility select frame number, .e. trace level, enter. Selecting last frame number (10 case) allow see value ret rtables:::do_split causes error informative error message follows created. previous split function fails exploratory_split_fun given arguments accepts. simple way avoid add ... function call. Now let’s construct interesting split function (error): Now take moment dwell machinery included rtables create custom split functions. , please read relevant documentation ?make_split_fun. pre-made split functions included rtables written make_split_fun stable constructor functions previously used. invite reader take look make_split_fun.R. majority functions understandable knowledge gained guide far. important note core split function specified, commonly case, make_split_fun calls do_base_split directly, minimal wrapper well-known do_split. drop_facet_levels, example, pre-processing function core simply removes empty factor levels split “column”, thus avoiding showing empty lines. also possible provide list functions, can seen examples ?make_split_fun. Note pre- post-processing requires list input support possibility combining multiple functions. contrast, core splitting function must single function call expected stacked features. rarely needs modified majority included split functions work pre- post-processing. Included post-processing functions interesting interact split object, e.g. reordering facets adding overall facet (add_overall_facet). attentive reader noticed core function relies do_split many post-processing functions rely make_split_result, best way get correct split return structure. Note modifying core split works row space moment.","code":"# rtables 0.6.2 # Table call with only the function changing simple_table <- function(DM, f) { lyt <- basic_table() %>% split_rows_by(\"ARM\", split_fun = f) %>% analyze(\"BMRKR1\") lyt %>% build_table(DM) } # First round will fail because there are unused arguments exploratory_split_fun <- function(df, spl) NULL # debug(rtables:::do_split) err_msg <- tryCatch(simple_table(DM, exploratory_split_fun), error = function(e) e) # undebug(rtables:::do_split) message(err_msg$message) ## Error applying custom split function: unused arguments (vals, labels, trim = trim) ## split: VarLevelSplit (ARM) ## occured at path: root # rtables 0.6.2 # Debugging level 10: tt_dotabulation.R#627: do_split(spl, df, spl_context = spl_context) # Original call and final error > simple_table(DM, exploratory_split_fun) Error in do_split(spl, df, spl_context = spl_context) : Error applying custom split function: unused arguments (vals, labels, trim = trim) # This is main error split: VarLevelSplit (ARM) # Split reference occured at path: root # Path level (where it occurred) # rtables 0.6.2 f_brakes_if <- function(split_col = NULL, error = FALSE) { function(df, spl, ...) { # order matters! more than naming # browser() # To check how it works if (is.null(split_col)) { # Retrieves the default split_col <- spl_variable(spl) # Internal accessor to split obj } my_payload <- split_col # Changing split column value vals <- levels(df[[my_payload]]) # Extracting values to split datasplit <- lapply(seq_along(vals), function(i) { df[df[[my_payload]] == vals[[i]], ] }) names(datasplit) <- as.character(vals) # Error if (isTRUE(error)) { # browser() # If you need to check how it works mystery_error_values <- sapply(datasplit, function(x) mean(x$BMRKR1)) if (any(mystery_error_values > 6)) { stop( \"It should not be more than 6! Should it be? Found in split values: \", names(datasplit)[which(mystery_error_values > 6)] ) } } # Handy function to return a split result!! make_split_result(vals, datasplit, vals) } } simple_table(DM, f_brakes_if()) # works! ## all obs ## ———————————————————————— ## A: Drug X ## Mean 5.79 ## B: Placebo ## Mean 6.11 ## C: Combination ## Mean 5.69 simple_table(DM, f_brakes_if(split_col = \"STRATA1\")) # works! ## all obs ## ———————————————— ## A ## Mean 5.95 ## B ## Mean 5.90 ## C ## Mean 5.71 # simple_table(DM, f_brakes_if(error = TRUE)) # does not work, but returns an informative message # Error in do_split(spl, df, spl_context = spl_context) : # Error applying custom split function: It should not be more than 6! Should it be? Found in split values: B: Placebo # split: VarLevelSplit (ARM) # occurred at path: root"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"spl_context---adding-context-to-our-splits","dir":"Articles > Dev-guide","previous_headings":"A Simple Split > Creating Custom Split Functions","what":".spl_context - Adding Context to Our Splits","title":"Split Machinery","text":"best way understand split context , use , read Leveraging .spl_context section Advanced Usage vignette, use browser() within split function see structured. .spl_context needed rewriting core functions, propose wrapper do_base_split , handy redirection standard do_split without split function part (.e. wrapper .apply_split_inner, real core splitting machinery). curiosity, set trim = TRUE . trimming works mixed table (values 0s content), trim 0s. rarely case, encourage using replacement functions trim_levels_to_group trim_levels_to_map trimming. Nowadays, even impossible set differently trim = FALSE. (write issue informative error list xxx). can see split column variable (split, first column) level splitting procedure. value current split value dealt . next column, let’s see number rows data frames: sapply(.spl_context$full_parent_df, nrow) # [1] 356 121 36 36. Indeed, root level contains full input data frame, levels subgroups full data according split value. all_cols_n shows exactly numbers just described. obs current filter applied columns. Applying root data (row subgroup data) reveals current column-wise facet (row-wise row split). also possible use information make complex splits column space using full data frame value splits select interested values. something change simplify within rtables need becomes apparent.","code":"# rtables 0.6.2 browsing_f <- function(df, spl, .spl_context, ...) { # browser() # do_base_split(df, spl, ...) # order matters!! This would fail if done do_base_split(spl = spl, df = df, vals = NULL, labels = NULL, trim = TRUE) } fnc_tmp <- function(innervar) { # Exploring trim_levels_in_facets (check its form) function(ret, ...) { # browser() for (var in innervar) { # of course AGE is not here, so nothing is dropped!! ret$datasplit <- lapply(ret$datasplit, function(df) { df[[var]] <- factor(df[[var]]) df }) } ret } } basic_table() %>% split_rows_by(\"ARM\") %>% split_rows_by(\"STRATA1\") %>% split_rows_by_cuts(\"AGE\", cuts = c(0, 50, 100), cutlabels = c(\"young\", \"old\") ) %>% split_rows_by(\"SEX\", split_fun = make_split_fun( pre = list(drop_facet_levels), # This is dropping the SEX levels (AGE is upper level) core_split = browsing_f, post = list(fnc_tmp(\"AGE\")) # To drop these we should use a split_fun in the above level )) %>% summarize_row_groups() %>% build_table(DM) # The following is the .spl_contest printout: Browse[1]> .spl_context split value full_parent_df all_cols_n all obs 1 root root c(\"S1\", .... 356 TRUE, TR.... 2 ARM A: Drug X c(\"S6\", .... 121 TRUE, TR.... 3 STRATA1 A c(\"S14\",.... 36 TRUE, TR.... 4 AGE young c(\"S14\",.... 36 TRUE, TR.... # NOTE: make_split_fun(pre = list(drop_facet_levels)) and drop_split_levels # do the same thing in this case"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"extra-arguments-extra_args","dir":"Articles > Dev-guide","previous_headings":"A Simple Split","what":"Extra Arguments: extra_args","title":"Split Machinery","text":"functionality well-known used setting analysis functions (somewhat complicated example can found Example Complex Analysis Function vignette), show can also apply splits. demonstrated, seem like impossible cases considered vestigial deprecated.","code":"# rtables 0.6.2 # Let's use the tracer!! my_tracer <- quote(if (length(spl@extra_args) > 0) browser()) trace( what = \"do_split\", tracer = my_tracer, where = asNamespace(\"rtables\") ) custom_mean_var <- function(var) { function(df, labelstr, na.rm = FALSE, ...) { # browser() mean(df[[var]], na.rm = na.rm) } } DM_ageNA <- DM DM_ageNA$AGE[1] <- NA basic_table() %>% split_rows_by(\"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% summarize_row_groups( cfun = custom_mean_var(\"AGE\"), extra_args = list(na.rm = TRUE), format = \"xx.x\", label_fstr = \"label %s\" ) %>% # content_extra_args, c_extra_args are different slots!! (xxx) split_rows_by(\"STRATA1\", split_fun = keep_split_levels(\"A\")) %>% analyze(\"AGE\") %>% # check with the extra_args (xxx) build_table(DM_ageNA) # You can pass extra_args down to other splits. It is possible this will not not # work. Should it? That is why extra_args lives only in splits (xxx) check if it works # as is. Difficult to find an use case for this. Maybe it could work for the ref_group # info. That does not work with nesting already (fairly sure that it will break stuff). # Does it make sense to have more than one ref_group at any point of the analysis? No docs, # send a warning if users try to nest things with ref_group (that is passed around via # extra_args) # As we can see that was not possible. What if we now force it a bit? my_split_fun <- function(df, spl, .spl_context, ...) { spl@extra_args <- list(na.rm = TRUE) # does not work because do_split is not changing the object # the split does not do anything with it drop_split_levels(df, spl) } # does not work basic_table() %>% split_rows_by(\"ARM\") %>% split_rows_by(\"SEX\", split_fun = my_split_fun) %>% analyze(\"AGE\", inclNAs = TRUE, afun = mean) %>% # include_NAs is set FALSE build_table(DM_ageNA) # extra_args is in available in cols but not in rows, because different columns # may need it for different col space. Row-wise it seems not necessary. # The only thing that works is adding it to analyze (xxx) check if it is worth adding # We invite the developer now to test all the test files of this package with the tracer on # therefore -> extra_args is not currently used in splits (xxx could be wrong) # could be not being hooked up untrace(what = \"do_split\", where = asNamespace(\"rtables\")) # Let's try with the other variables identically my_tracer <- quote(if (!is.null(vals) || !is.null(labels) || isTRUE(trim)) { print(\"A LOT TO SAY\") message(\"CANT BLOCK US ALL\") stop(\"NOW FOR SURE\") browser() }) trace( what = \"do_split\", tracer = my_tracer, where = asNamespace(\"rtables\") ) # Run tests by copying the above in setup-fakedata.R (then devtools::test()) untrace( what = \"do_split\", where = asNamespace(\"rtables\") )"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_split_machinery.html","id":"multivarsplit-compoundsplit-examples","dir":"Articles > Dev-guide","previous_headings":"","what":"MultiVarSplit & CompoundSplit Examples","title":"Split Machinery","text":"final part article still construction, hence non-specific mentions list. xxx CompoundSplit generates facets one variable (e.g. cumulative distributions) MultiVarSplit uses different variables split. See AnalyzeMultiVars, inherits CompoundSplit details analyzes facets multiple times. MultiVarColSplit works analyze_colvars, scope article. .set_kids_sect_sep adds things children (can set split). First, want see MultiVarSplit class behaves example case taken ?split_rows_by_multivar. print output, notice two groups (one called “SEX” “STRATA1”) identical along columns. subgroup actually created. interesting way personalize splits help custom split functions split context, widely different subgroups table. invite reader try understand split_rows_by_multivar can row splits (see xxx comment previous code), split_cols_by_multivar . known bug moment, work towards fix . Known issues often linked source code GitHub issue number (e.g. #690). Lastly, briefly show example split cut function replace solve empty age groups problem . propose simplified situation: row split cases (*_cuts *_cutfun), empty levels dropped. expected can avoided using dedicated split function. Intentionally looking future split possible order determine element present . moment possible add spl_fun dedicated split functions like split_rows_by_cuts. Note previous table used summarize_row_groups, analyze calls. rendered table nicely, standard method use summarize_row_groups intended decorate row groups, .e. rows labels. Internally, rows called content rows analysis functions summarize_row_groups called cfun instead afun. Indeed, tabulation machinery also presents two differently described Tabulation Row Structure section Tabulation vignette. can try construct split function cuts manually make_split_fun: Alternatively, choose prune rows prune_table! add pre-proc z-scoring","code":"# rtables 0.6.2 my_tracer <- quote(if (is(spl, \"MultiVarSplit\")) browser()) trace( what = \"do_split\", tracer = my_tracer, where = asNamespace(\"rtables\") ) # We want also to take a look at the following: debugonce(rtables:::.apply_split_inner) lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by_multivar(c(\"BMRKR1\", \"BMRKR1\"), varlabels = c(\"SD\", \"MEAN\") ) %>% split_rows_by(\"COUNTRY\", split_fun = keep_split_levels(\"PAK\") ) %>% # xxx for #690 #691 summarize_row_groups() %>% analyze(c(\"AGE\", \"SEX\")) build_table(lyt, DM) # xxx check empty space on top -> check if it is a bug, file it untrace( what = \"do_split\", where = asNamespace(\"rtables\") ) # rtables 0.6.2 cutfun <- function(x) { # browser() cutpoints <- c(0, 50, 100) names(cutpoints) <- c(\"\", \"Younger\", \"Older\") cutpoints } tbl <- basic_table(show_colcounts = TRUE) %>% split_rows_by(\"ARM\", split_fun = drop_and_remove_levels(c(\"B: Placebo\", \"C: Combination\"))) %>% split_rows_by(\"STRATA1\") %>% split_rows_by_cutfun(\"AGE\", cutfun = cutfun) %>% # split_rows_by_cuts(\"AGE\", cuts = c(0, 50, 100), # cutlabels = c(\"young\", \"old\")) %>% # Works the same split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% summarize_row_groups() %>% # This is degenerate!!! build_table(DM) tbl ## all obs ## (N=356) ## ————————————————————————— ## A: Drug X ## A ## AGE ## Younger ## F 22 (6.2%) ## M 14 (3.9%) ## Older ## B ## AGE ## Younger ## F 26 (7.3%) ## M 14 (3.9%) ## Older ## F 1 (0.3%) ## C ## AGE ## Younger ## F 19 (5.3%) ## M 21 (5.9%) ## Older ## F 2 (0.6%) ## M 2 (0.6%) my_count_afun <- function(x, .N_col, .spl_context, ...) { # browser() out <- list(c(length(x), length(x) / .N_col)) names(out) <- .spl_context$value[nrow(.spl_context)] # workaround (xxx #689) in_rows( .list = out, .formats = c(\"xx (xx.x%)\") ) } # ?make_split_fun # To check for docs/examples # Core split cuts_core <- function(spl, df, vals, labels, .spl_context) { # browser() # file an issue xxx # variables that are split on are converted to factor during the original clean-up # cut split are not doing it but it is an exception. xxx # young_v <- as.numeric(df[[\"AGE\"]]) < 50 # current solution: young_v <- as.numeric(as.character(df[[\"AGE\"]])) < 50 make_split_result(c(\"young\", \"old\"), datasplit = list(df[young_v, ], df[!young_v, ]), labels = c(\"Younger\", \"Older\") ) } drop_empties <- function(splret, spl, fulldf, ...) { # browser() nrows_data_split <- vapply(splret$datasplit, nrow, numeric(1)) to_keep <- nrows_data_split > 0 make_split_result( splret$values[to_keep], splret$datasplit[to_keep], splret$labels[to_keep] ) } gen_split <- make_split_fun( core_split = cuts_core, post = list(drop_empties) ) tbl <- basic_table(show_colcounts = TRUE) %>% split_rows_by(\"ARM\", split_fun = keep_split_levels(c(\"A: Drug X\"))) %>% split_rows_by(\"STRATA1\") %>% split_rows_by(\"AGE\", split_fun = gen_split) %>% analyze(\"SEX\") %>% # It is the last step!! No need of BMRKR1 right? # split_rows_by(\"SEX\", split_fun = drop_split_levels, # child_labels = \"hidden\") %>% # close issue #689. would it work for # analyze_colvars? probably (xxx) # analyze(\"BMRKR1\", afun = my_count_afun) %>% # This is NOT degenerate!!! BMRKR1 is only placeholder build_table(DM) tbl # rtables 0.6.2 tbl <- basic_table(show_colcounts = TRUE) %>% split_rows_by(\"ARM\", split_fun = keep_split_levels(c(\"A: Drug X\"))) %>% split_rows_by(\"STRATA1\") %>% split_rows_by_cuts( \"AGE\", cuts = c(0, 50, 100), cutlabels = c(\"young\", \"old\") ) %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% summarize_row_groups() %>% # This is degenerate!!! # we keep it until #689 build_table(DM) tbl ## all obs ## (N=356) ## ————————————————————— ## A: Drug X ## A ## young ## F 22 (6.2%) ## M 14 (3.9%) ## old ## B ## young ## F 26 (7.3%) ## M 14 (3.9%) ## old ## F 1 (0.3%) ## C ## young ## F 19 (5.3%) ## M 21 (5.9%) ## old ## F 2 (0.6%) ## M 2 (0.6%) # Trying with pruning prune_table(tbl) # (xxx) what is going on here? it is degenerate so it has no real leaves ## NULL # It is degenerate -> what to do? # The same mechanism is applied in the case of NULL leaves, they are rolled up in the # table tree"},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_tabulation.html","id":"disclaimer","dir":"Articles > Dev-guide","previous_headings":"","what":"Disclaimer","title":"Tabulation","text":"article intended use developers contain low-level explanations topics covered. user-friendly vignettes, please see Articles page rtables website. code prose appears version article main branch repository may reflect specific state things can less recent. guide describes important aspects tabulation process unlikely change. Regardless, invite reader keep mind current repository code may drifted following material document, always best practice read code directly main. Please keep mind rtables still active development, seen efforts multiple contributors across different years. Therefore, may legacy mechanisms ongoing transformations look different future. working document may subjected deprecation updates, keep xxx comments indicate placeholders warnings -’s need work.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_tabulation.html","id":"introduction","dir":"Articles > Dev-guide","previous_headings":"","what":"Introduction","title":"Tabulation","text":"Tabulation rtables process takes pre-defined layout applies data. layout object, splits (see xxx link split machinery article) analyzes, can applied different data produce valid tables. process happens principally within tt_dotabulation.R file user-facing function build_table resides . occasionally use functions methods present files, like colby_construction.R make_subset_expr.R. assume reader already familiar documentation build_table. suggest reading split machinery vignette (xxx link) prior one, instrumental understanding layout object, essentially built splits, tabulated data supplied.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/dev-guide/dg_tabulation.html","id":"tabulation","dir":"Articles > Dev-guide","previous_headings":"","what":"Tabulation","title":"Tabulation","text":"enter build_table using debugonce see works. Now let’s look within build_table call. initial check layout pre-data table layout, checks column layout defined (clayout accessor), .e. column split. case, obs column added automatically observations. , couple defensive programming calls checks transformations finally data. can divided two categories: mainly concern layout, defined generics, concern data, instead function dependent layout class. Indeed, layout structured can divided clayout rlayout (column row layout). first one used create cinfo, general object container column splits information. second one contains obligatory data split, .e. root split (accessible root_spl), row splits’ vectors iterative splits row space. following, consider initial checks defensive programming. Along various checks defensive programming, find PreDataAxisLayout virtual class row column layouts inherit . Virtual classes handy group classes need share things like labels functions need applicable relative classes. See information rtables class hierarchy dedicated article (xxx add). Now, continue build_table. checks, notice TreePos() constructor object retains representation tree position along split values labels. mainly used create_colinfo, enter now debugonce(create_colinfo). function creates object represents column splits everything else may related columns. particular, column counts calculated function. parameter inputs follows: create_colinfo make_subset_expr.R. , see topleft present build_table, override one lyt. Entering create_colinfo, see following calls: Next function determination column counts. Currently, happens leaf level, can certainly calculated independently levels (open issue rtables, .e. print levels’ totals). Precedence column counts may documented (xxx todo). main use case analyzing participation-level dataset, multiple records per subject, like retain total numbers subjects per column, often taken subject-level dataset, use column counts. Originally, counts able added vector, often case users like possibility use alt_counts_df. cinfo object (InstantiatedColumnInfo) created information. continue inside build_table, see .make_ctab used make root split. general procedure generates initial root split content row. ctab applied content row, row contains label. ?summarize_row_groups, know rtables defines label rows, .e. content rows. .make_ctab similar function actual creates table rows, .make_tablerows. Note function uses parent_cfun .make_caller retrieve content function inserted levels. split structural handling table object row-creation engine, divided .make_tablerows call. search package, find function called twice, .make_ctab .make_analyzed_tab. two final elements table construction: creation rows. Going back build_table, see row layout actually list split vectors. fundamental line, kids <- lapply(seq_along(rlyt), function() {, allows us appreciate . Going forward see recursive_applysplit applied split vector. may worthwhile check vector looks like test case. last print informative. can see layout construction object built 2 VarLevelSplits rows one final AnalyzeMultiVars, leaf analysis split final level rows. second split vector following AnalyzeVarSplit. xxx get multiple split vectors, need escape nesting nest = FALSE adding split_rows_by call analyze call. Continuing recursive_applysplit, made two main calls: one .make_ctab makes content row calculates counts specified, .make_split_kids. eventually contains recursive_applysplit applied split vector built Splits analyze splits. generic handy switch different downstream processes. case (rlyt[[1]]) call method getMethod(\".make_split_kids\", \"Split\") twice getting analysis split. , (xxx) multi-variable split applies .make_split_kids elements, turn calling main getMethod(\".make_split_kids\", \"VAnalyzeSplit\") turn go .make_analyzed_tab. interesting edge cases different split cases, like split_by_multivars one splits reference group. internal code , called baseline. follow variable across function layers, see split (do_split) happens (getMethod(\".make_split_kids\", \"Split\")) second split reference group. done make available row calculate, example, differences reference group. Now move towards .make_tablerows, analysis functions become key place applied analyzed. First, external tryCatch used cache errors higher level, differentiate two major blocks. function parameters quite intuitive, exception spl_context. fundamental parameter keeps information splits can visible analysis functions. look value, see carried updated everywhere split happens, except columns. Column-related information added last, gen_onerv, lowest level one result value produced. .make_tablerows go gen_rowvalues, aside row referential footers handling. gen_rowvalues unpacks cinfo object crosses arriving row split information generate rows. particular, rawvals <- mapply(gen_onerv, maps columns generate list values corresponding table row. Looking final gen_onerv see (!(val, \"RowsVerticalSection\")) function in_rows called. invite reader explore building blocks in_rows , .make_tablerows constructs data row (DataRow) content row (ContentRow) depending whether called .make_ctab .make_analyzed_tab. .make_tablerows either makes content table “analysis table”. gen_rowvalues generates list stacks (RowsVerticalSection, one rows potentially!) column. add: conceptual part -> calculating things column putting side side slicing rows putting together -> rtables row dominant.","code":"# rtables 0.6.2 library(rtables) debugonce(build_table) # A very simple layout lyt <- basic_table() %>% split_rows_by(\"STRATA1\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% split_cols_by(\"ARM\") %>% analyze(\"BMRKR1\") # lyt must be a PreDataTableLayouts object is(lyt, \"PreDataTableLayouts\") lyt %>% build_table(DM) ## do checks and defensive programming now that we have the data lyt <- fix_dyncuts(lyt, df) # Create the splits that depends on data lyt <- set_def_child_ord(lyt, df) # With the data I set the same order for all splits lyt <- fix_analyze_vis(lyt) # Checks if the analyze last split should be visible # If there is only one you will not get the variable name, otherwise you get it if you # have multivar. Default is NA. You can do it now only because you are sure to # have the whole layout. df <- fix_split_vars(lyt, df, char_ok = is.null(col_counts)) # checks if split vars are present lyt[] # preserve names - warning if names longer, repeats the name value if only one lyt@.Data # might not preserve the names # it works only when it is another class that inherits from lists # We suggest doing extensive testing about these behaviors in order to do choose the appropriate one cinfo <- create_colinfo( lyt, # Main layout with col split info df, # df used for splits and col counts if no alt_counts_df is present rtpos, # TreePos (does not change out of this function) counts = col_counts, # If we want to overwrite the calculations with df/alt_counts_df alt_counts_df = alt_counts_df, # alternative data for col counts total = col_total, # calculated from build_table inputs (nrow of df or alt_counts_df) topleft # topleft information added into build_table ) clayout <- clayout(lyt) # Extracts column split and info if (is.null(topleft)) { topleft <- top_left(lyt) # If top_left is not present in build_table, it is taken from lyt } ctree <- coltree(clayout, df = df, rtpos = rtpos) # Main constructor of LayoutColTree # The above is referenced as generic and principally represented as # setMethod(\"coltree\", \"PreDataColLayout\", (located in `tree_accessor.R`). # This is a call that restructures information from clayout, df, and rtpos # to get a more compact column tree layout. Part of this design is related # to past implementations. cexprs <- make_col_subsets(ctree, df) # extracts expressions in a compact fashion. # WARNING: removing NAs at this step is automatic. This should # be coupled with a warning for NAs in the split (xxx) colextras <- col_extra_args(ctree) # retrieves extra_args from the tree. It may not be used # rtables 0.6.2 # A very simple layout lyt <- basic_table() %>% split_rows_by(\"STRATA1\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% split_cols_by(\"ARM\") %>% analyze(\"BMRKR1\") rlyt <- rtables:::rlayout(lyt) str(rlyt, max.level = 2) Formal class 'PreDataRowLayout' [package \"rtables\"] with 2 slots ..@ .Data :List of 2 # rlyt is a rtables object (PreDataRowLayout) that is also a list! ..@ root_split:Formal class 'RootSplit' [package \"rtables\"] with 17 slots # another object! # If you do summarize_row_groups before anything you act on the root split. We need this to # have a place for the content that is valid for the whole table. str(rtables:::root_spl(rlyt), max.level = 2) # it is still a split str(rlyt[[1]], max.level = 3) # still a rtables object (SplitVector) that is a list Formal class 'SplitVector' [package \"rtables\"] with 1 slot ..@ .Data:List of 3 .. ..$ :Formal class 'VarLevelSplit' [package \"rtables\"] with 20 slots .. ..$ :Formal class 'VarLevelSplit' [package \"rtables\"] with 20 slots .. ..$ :Formal class 'AnalyzeMultiVars' [package \"rtables\"] with 17 slots # rtables 0.6.2 str(rlyt[[2]], max.level = 5) Formal class 'SplitVector' [package \"rtables\"] with 1 slot ..@ .Data:List of 1 .. ..$ :Formal class 'AnalyzeVarSplit' [package \"rtables\"] with 21 slots .. .. .. ..@ analysis_fun :function (x, ...) .. .. .. .. ..- attr(*, \"srcref\")= 'srcref' int [1:8] 1723 5 1732 5 5 5 4198 4207 .. .. .. .. .. ..- attr(*, \"srcfile\")=Classes 'srcfilealias', 'srcfile' .. .. .. ..@ default_rowlabel : chr \"Var3 Counts\" .. .. .. ..@ include_NAs : logi FALSE .. .. .. ..@ var_label_position : chr \"default\" .. .. .. ..@ payload : chr \"VAR3\" .. .. .. ..@ name : chr \"VAR3\" .. .. .. ..@ split_label : chr \"Var3 Counts\" .. .. .. ..@ split_format : NULL .. .. .. ..@ split_na_str : chr NA .. .. .. ..@ split_label_position : chr(0) .. .. .. ..@ content_fun : NULL .. .. .. ..@ content_format : NULL .. .. .. ..@ content_na_str : chr(0) .. .. .. ..@ content_var : chr \"\" .. .. .. ..@ label_children : logi FALSE .. .. .. ..@ extra_args : list() .. .. .. ..@ indent_modifier : int 0 .. .. .. ..@ content_indent_modifier: int 0 .. .. .. ..@ content_extra_args : list() .. .. .. ..@ page_title_prefix : chr NA .. .. .. ..@ child_section_div : chr NA"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"vignette demonstrate complex analysis function can constructed order build highly-customized tables rtables. example detail steps creating analysis function calculate basic univariable Cox regression summary table analyze treatment effect ARM variable covariate/interaction effects survival analysis. Cox regression analysis function customization options capability fitting multivariable Cox regression models, see summarize_coxreg() function tern package, builds upon concepts used construction example. packages used vignette :","code":"library(rtables) library(dplyr)"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"data-pre-processing","dir":"Articles","previous_headings":"","what":"Data Pre-Processing","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"First, prepare data used generate table example. use example ADTTE (Time--Event Analysis) dataset ex_adtte formatters package, contains treatment variable ARM, several variables can chosen covariates, censor variable CNSR derive event variable EVENT required model. purpose example, use age (AGE) race (RACE) covariates. prepare data needed observe desired effects summary table. PARAMCD filtered records overall survival (OS) included, filter mutate include levels interest covariates. ARM variable mutated indicate \"B: Placebo\" used reference level treatment variable, EVENT variable derived CNSR.","code":"adtte <- ex_adtte anl <- adtte %>% dplyr::filter(PARAMCD == \"OS\") %>% dplyr::filter(ARM %in% c(\"A: Drug X\", \"B: Placebo\")) %>% dplyr::filter(RACE %in% c(\"ASIAN\", \"BLACK OR AFRICAN AMERICAN\", \"WHITE\")) %>% dplyr::mutate(RACE = droplevels(RACE)) %>% dplyr::mutate(ARM = droplevels(stats::relevel(ARM, \"B: Placebo\"))) %>% dplyr::mutate(EVENT = 1 - CNSR)"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"tidy-method-for-summary-coxph-objects-tidy-summary-coxph","dir":"Articles","previous_headings":"Creating Helper Functions: Cox Regression Model Calculations","what":"tidy Method for summary.coxph Objects: tidy.summary.coxph","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"method allows tidy function broom package operate summary.coxph output, extracting values interest analysis returning tidied tibble::tibble() object.","code":"tidy.summary.coxph <- function(x, ...) { is(x, \"summary.coxph\") pval <- x$coefficients confint <- x$conf.int levels <- rownames(pval) pval <- tibble::as_tibble(pval) confint <- tibble::as_tibble(confint) ret <- cbind(pval[, grepl(\"Pr\", names(pval))], confint) ret$level <- levels ret$n <- x[[\"n\"]] ret }"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"function-to-estimate-interaction-effects-h_coxreg_inter_effect","dir":"Articles","previous_headings":"Creating Helper Functions: Cox Regression Model Calculations","what":"Function to Estimate Interaction Effects: h_coxreg_inter_effect","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"h_coxreg_inter_effect helper function used within following helper function, h_coxreg_extract_interaction, estimate interaction effects given model given covariate. function calculates desired statistics given model returns data.frame label information row well statistics n, hr (hazard ratio), lcl (CI lower bound), ucl (CI upper bound), pval (effect p-value), pval_inter (interaction p-value). numeric covariate selected, median value used sole “level” interaction effect calculated. non-numeric covariates, interaction effect calculated level covariate, result returned separate row.","code":"h_coxreg_inter_effect <- function(x, effect, covar, mod, label, control, data) { if (is.numeric(x)) { betas <- stats::coef(mod) attrs <- attr(stats::terms(mod), \"term.labels\") term_indices <- grep(pattern = effect, x = attrs[!grepl(\"strata\\\\(\", attrs)]) betas <- betas[term_indices] betas_var <- diag(stats::vcov(mod))[term_indices] betas_cov <- stats::vcov(mod)[term_indices[1], term_indices[2]] xval <- stats::median(x) effect_index <- !grepl(covar, names(betas)) coef_hat <- betas[effect_index] + xval * betas[!effect_index] coef_se <- sqrt(betas_var[effect_index] + xval^2 * betas_var[!effect_index] + 2 * xval * betas_cov) q_norm <- stats::qnorm((1 + control$conf_level) / 2) } else { var_lvl <- paste0(effect, levels(data[[effect]])[-1]) # [-1]: reference level giv_lvl <- paste0(covar, levels(data[[covar]])) design_mat <- expand.grid(effect = var_lvl, covar = giv_lvl) design_mat <- design_mat[order(design_mat$effect, design_mat$covar), ] design_mat <- within(data = design_mat, expr = { inter <- paste0(effect, \":\", covar) rev_inter <- paste0(covar, \":\", effect) }) split_by_variable <- design_mat$effect interaction_names <- paste(design_mat$effect, design_mat$covar, sep = \"/\") mmat <- stats::model.matrix(mod)[1, ] mmat[!mmat == 0] <- 0 design_mat <- apply(X = design_mat, MARGIN = 1, FUN = function(x) { mmat[names(mmat) %in% x[-which(names(x) == \"covar\")]] <- 1 mmat }) colnames(design_mat) <- interaction_names coef <- stats::coef(mod) vcov <- stats::vcov(mod) betas <- as.matrix(coef) coef_hat <- t(design_mat) %*% betas dimnames(coef_hat)[2] <- \"coef\" coef_se <- apply(design_mat, 2, function(x) { vcov_el <- as.logical(x) y <- vcov[vcov_el, vcov_el] y <- sum(y) y <- sqrt(y) y }) q_norm <- stats::qnorm((1 + control$conf_level) / 2) y <- cbind(coef_hat, `se(coef)` = coef_se) y <- apply(y, 1, function(x) { x[\"hr\"] <- exp(x[\"coef\"]) x[\"lcl\"] <- exp(x[\"coef\"] - q_norm * x[\"se(coef)\"]) x[\"ucl\"] <- exp(x[\"coef\"] + q_norm * x[\"se(coef)\"]) x }) y <- t(y) y <- by(y, split_by_variable, identity) y <- lapply(y, as.matrix) attr(y, \"details\") <- paste0( \"Estimations of \", effect, \" hazard ratio given the level of \", covar, \" compared to \", effect, \" level \", levels(data[[effect]])[1], \".\" ) xval <- levels(data[[covar]]) } data.frame( effect = \"Covariate:\", term = rep(covar, length(xval)), term_label = as.character(paste0(\" \", xval)), level = as.character(xval), n = NA, hr = if (is.numeric(x)) exp(coef_hat) else y[[1]][, \"hr\"], lcl = if (is.numeric(x)) exp(coef_hat - q_norm * coef_se) else y[[1]][, \"lcl\"], ucl = if (is.numeric(x)) exp(coef_hat + q_norm * coef_se) else y[[1]][, \"ucl\"], pval = NA, pval_inter = NA, stringsAsFactors = FALSE ) }"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"function-to-extract-effect-information-h_coxreg_extract_interaction","dir":"Articles","previous_headings":"Creating Helper Functions: Cox Regression Model Calculations","what":"Function to Extract Effect Information: h_coxreg_extract_interaction","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"Using previous two helper functions, h_coxreg_extract_interaction uses ANOVA extract information given model given covariate. function extract different information depending whether effect interest treatment/main effect interaction effect, returns data.frame label information row (corresponding effect) well statistics n, hr, lcl, ucl, pval, pval_inter (interaction effects ). helper function used directly within analysis function analyze Cox regression model extract relevant information processed displayed within output table.","code":"h_coxreg_extract_interaction <- function(effect, covar, mod, data) { control <- list(pval_method = \"wald\", ties = \"exact\", conf_level = 0.95, interaction = FALSE) test_statistic <- c(wald = \"Wald\", likelihood = \"LR\")[control$pval_method] mod_aov <- withCallingHandlers( expr = car::Anova(mod, test.statistic = test_statistic, type = \"III\"), message = function(m) invokeRestart(\"muffleMessage\") ) msum <- if (!any(attr(stats::terms(mod), \"order\") == 2)) summary(mod, conf.int = control$conf_level) else mod_aov sum_anova <- broom::tidy(msum) if (!any(attr(stats::terms(mod), \"order\") == 2)) { effect_aov <- mod_aov[effect, , drop = TRUE] pval <- effect_aov[[grep(pattern = \"Pr\", x = names(effect_aov)), drop = TRUE]] sum_main <- sum_anova[grepl(effect, sum_anova$level), ] term_label <- if (effect == covar) { paste0(levels(data[[covar]])[2], \" vs control (\", levels(data[[covar]])[1], \")\") } else { unname(formatters::var_labels(data, fill = TRUE)[[covar]]) } y <- data.frame( effect = ifelse(covar == effect, \"Treatment:\", \"Covariate:\"), term = covar, term_label = term_label, level = levels(data[[effect]])[2], n = mod[[\"n\"]], hr = unname(sum_main[\"exp(coef)\"]), lcl = unname(sum_main[grep(\"lower\", names(sum_main))]), ucl = unname(sum_main[grep(\"upper\", names(sum_main))]), pval = pval, stringsAsFactors = FALSE ) y$pval_inter <- NA y } else { pval <- sum_anova[sum_anova$term == effect, ][[\"p.value\"]] ## Test the interaction effect pval_inter <- sum_anova[grep(\":\", sum_anova$term), ][[\"p.value\"]] covar_test <- data.frame( effect = \"Covariate:\", term = covar, term_label = unname(formatters::var_labels(data, fill = TRUE)[[covar]]), level = \"\", n = mod$n, hr = NA, lcl = NA, ucl = NA, pval = pval, pval_inter = pval_inter, stringsAsFactors = FALSE ) ## Estimate the interaction y <- h_coxreg_inter_effect( data[[covar]], covar = covar, effect = effect, mod = mod, label = unname(formatters::var_labels(data, fill = TRUE)[[covar]]), control = control, data = data ) rbind(covar_test, y) } }"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"creating-a-helper-function-cached_model","dir":"Articles","previous_headings":"","what":"Creating a Helper Function: cached_model","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"Next, create helper function, cached_model, used within analysis function cache return fitted Cox regression model current covariate. df argument directly inherited df argument passed analysis function, contains full dataset analyzed. cov argument covariate analyzed depending current row context. treatment effect currently analyzed, value empty string. cache_env parameter environment object used store model current covariate, also passed analysis function. course, function can also run outside analysis function still cache return Cox regression model. Using arguments, cached_model function first checks model given covariate cov already stored caching environment cache_env. , model retrieved returned cached_model. , model must constructed. done first constructing model formula, model_form, starting treatment effect (ARM) adding covariate effect one currently analyzed. Cox regression model fit using df model formula, model returned stored caching environment object cache_env[[cov]].","code":"cached_model <- function(df, cov, cache_env) { ## Check if a model already exists for ## `cov` in the caching environment if (!is.null(cache_env[[cov]])) { ## If model already exists, retrieve it from cache_env model <- cache_env[[cov]] } else { ## Build model formula model_form <- paste0(\"survival::Surv(AVAL, EVENT) ~ ARM\") if (length(cov) > 0) { model_form <- paste(c(model_form, cov), collapse = \" * \") } else { cov <- \"ARM\" } ## Calculate Cox regression model model <- survival::coxph( formula = stats::as.formula(model_form), data = df, ties = \"exact\" ) ## Store model in the caching environment cache_env[[cov]] <- model } model }"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"creating-the-analysis-function-a_cox_summary","dir":"Articles","previous_headings":"","what":"Creating the Analysis Function: a_cox_summary","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"data prepared helper function created, can proceed construct analysis function a_cox_summary, used populate rows table. order used generate data rows (interaction effects) content rows (main effects), must create function can used afun analyze cfun summarize_row_groups. Therefore, function must accept labelstr parameter. arguments analysis function follows: df - data.frame full dataset required fit Cox regression model. labelstr - string label variable analyzed current row/column split context. .spl_context - data.frame containing value column used analysis function determine name variable/covariate current split. details information stored .spl_context see ?analyze. stat format - strings indicate statistic column currently format applied print statistic. cache_env - environment object can used store cached models can prevent repeatedly fitting model. Instead, model generated per covariate reused. argument passed directly cached_model helper function defined previously. cov_main - logical value indicating whether current row summarizing covariate main effects. analysis function works within given row/column split context using current covariate (cov) cached_model function obtain desired Cox regression model. model, h_coxreg_extract_interaction function able extract information/statistics relevant analysis store data.frame. rows data.frame interest current row/column split context extracted statistic printed current column retrieved rows. Finally, formatted cells statistic returned VerticalRowsSection object. detail see commented function code , purpose line within a_cox_summary described.","code":"a_cox_summary <- function(df, labelstr = \"\", .spl_context, stat, format, cache_env, cov_main = FALSE) { ## Get current covariate (variable used in latest row split) cov <- tail(.spl_context$value, 1) ## If currently analyzing treatment effect (ARM) replace empty ## value of cov with \"ARM\" so the correct model row is analyzed if (length(cov) == 0) cov <- \"ARM\" ## Use cached_model to get the fitted Cox regression ## model for the current covariate model <- cached_model(df = df, cov = cov, cache_env = cache_env) ## Extract levels of cov to be used as row labels for interaction effects. ## If cov is numeric, the median value of cov is used as a row label instead cov_lvls <- if (is.factor(df[[cov]])) levels(df[[cov]]) else as.character(median(df[[cov]])) ## Use function to calculate and extract information relevant to cov from the model cov_rows <- h_coxreg_extract_interaction(effect = \"ARM\", covar = cov, mod = model, data = df) ## Effect p-value is only printed for treatment effect row if (!cov == \"ARM\") cov_rows[, \"pval\"] <- NA_real_ ## Extract rows containing statistics for cov from model information if (!cov_main) { ## Extract rows for main effect cov_rows <- cov_rows[cov_rows$level %in% cov_lvls, ] } else { ## Extract all non-main effect rows cov_rows <- cov_rows[nchar(cov_rows$level) == 0, ] } ## Extract value(s) of statistic for current column and variable/levels stat_vals <- as.list(apply(cov_rows[stat], 1, function(x) x, simplify = FALSE)) ## Assign labels: covariate name for main effect (content) rows, ARM comparison description ## for treatment effect (content) row, cov_lvls for interaction effect (data) rows nms <- if (cov_main) labelstr else if (cov == \"ARM\") cov_rows$term_label else cov_lvls ## Return formatted/labelled row in_rows( .list = stat_vals, .names = nms, .labels = nms, .formats = setNames(rep(format, length(nms)), nms), .format_na_strs = setNames(rep(\"\", length(nms)), nms) ) }"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"selecting-parameters","dir":"Articles","previous_headings":"","what":"Selecting Parameters","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"able customize Cox regression summary using analysis function selecting covariates (labels), statistics (labels), statistic formats use generating output table. also initialize new environment object used analysis function caching environment store models . purpose example, choose 5 possible statistics include table: n, hazard ratio, confidence interval, effect p-value, interaction p-value.","code":"my_covs <- c(\"AGE\", \"RACE\") ## Covariates my_cov_labs <- c(\"Age\", \"Race\") ## Covariate labels my_stats <- list(\"n\", \"hr\", c(\"lcl\", \"ucl\"), \"pval\", \"pval_inter\") ## Statistics my_stat_labs <- c(\"n\", \"Hazard Ratio\", \"95% CI\", \"p-value\\n(effect)\", \"p-value\\n(interaction)\") ## Statistic labels my_formats <- c( n = \"xx\", hr = \"xx.xx\", lcl = \"(xx.xx, xx.xx)\", pval = \"xx.xxxx\", pval_inter = \"xx.xxxx\" ## Statistic formats ) my_env <- new.env() ny_cache_env <- replicate(length(my_stats), list(my_env)) ## Caching environment"},{"path":"https://insightsengineering.github.io/rtables/articles/example_analysis_coxreg.html","id":"constructing-the-table","dir":"Articles","previous_headings":"","what":"Constructing the Table","title":"Example Complex Analysis Function: Modelling Cox Regression","text":"Finally, table layout can constructed used build desired table. first split basic_table using split_cols_by_multivar ensure statistic exists column. , choose variable (case STUDYID) shares value every row, use split variable every column full dataset used compute model every column. use extra_args argument list element’s element positions correspond children (columns generated ) split. arguments inherited following layout elements operating within split, use elements argument inputs. elaborate , three elements extra_args: stat, format, cache_env - arguments a_cox_summary length equal number columns (defined ). use analysis function following column split, depending current column context, corresponding element three list elements inherited extra_args used input. example, analyze_colvars called a_cox_summary afun performing calculations column 1, my_stats[1] (\"n\") given argument stat, my_formats[1] (\"xx\") argument format, my_cache_env[1] (my_env) cache_env. useful table since want column print values different statistic apply corresponding format. Next, can use summarize_row_groups generate content row treatment effect. first instance extra_args column split inherited used argument input cfun. generating treatment effect row, want add rows covariates. use split_rows_by_multivar split rows covariate apply appropriate labels. Following row split, use summarize_row_groups a_cox_summary cfun generate one content row covariate main effect. contents extra_args column split inherited input. specify cov_main = TRUE extra_args argument main effects rather interactions considered. Since split, instance extra_args inherited following layout elements. cov_main singular value, cov_main = TRUE used within every column context. last part table covariate interaction effects. use analyze_colvars a_cox_summary afun, inherit extra_args column split. Using rtables “analyze” function generates data rows, one row corresponding covariate level (median value, numeric covariates), nested content row (main effect) covariate. Using pre-processed anl dataset, can now build output final Cox regression summary table.","code":"lyt <- basic_table() %>% ## Column split: one column for each statistic split_cols_by_multivar( vars = rep(\"STUDYID\", length(my_stats)), varlabels = my_stat_labs, extra_args = list( stat = my_stats, format = my_formats, cache_env = ny_cache_env ) ) %>% ## Create content row for treatment effect summarize_row_groups(cfun = a_cox_summary) %>% ## Row split: one content row for each covariate split_rows_by_multivar( vars = my_covs, varlabels = my_cov_labs, split_label = \"Covariate:\", indent_mod = -1 ## Align split label left ) %>% ## Create content rows for covariate main effects summarize_row_groups( cfun = a_cox_summary, extra_args = list(cov_main = TRUE) ) %>% ## Create data rows for covariate interaction effects analyze_colvars(afun = a_cox_summary) cox_tbl <- build_table(lyt, anl) cox_tbl ## p-value p-value ## n Hazard Ratio 95% CI (effect) (interaction) ## ———————————————————————————————————————————————————————————————————————————————————————————————— ## A: Drug X vs control (B: Placebo) 247 0.97 (0.71, 1.32) 0.8243 ## Covariate: ## Age 247 0.7832 ## 34 0.92 (0.68, 1.26) ## Race 247 0.7441 ## ASIAN 1.03 (0.68, 1.57) ## BLACK OR AFRICAN AMERICAN 0.78 (0.41, 1.49) ## WHITE 1.06 (0.55, 2.04)"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Exploratory Analysis","text":"vignette, like introduce qtable() can used easily create cross tabulations exploratory data analysis. qtable() extension table() base R can much beyond creating two-way contingency tables. function simple use interface internally builds layouts using rtables framework.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"getting-started","dir":"Articles","previous_headings":"","what":"Getting Started","title":"Exploratory Analysis","text":"Load packages used vignette: Let’s start seeing table() can : can easily recreate cross-tables qtable() specifying data.frame variable(s) tabulate. col_vars row_vars arguments control split data across columns rows respectively. Aside display style, main difference qtable() add (N=xx) table header default. can removed show_colcounts. variables used row column facets empty strings (““). non empty values required labels generating table. code generate error.","code":"library(rtables) library(dplyr) table(ex_adsl$ARM) # # A: Drug X B: Placebo C: Combination # 134 134 132 table(ex_adsl$SEX, ex_adsl$ARM) # # A: Drug X B: Placebo C: Combination # F 79 77 66 # M 51 55 60 # U 3 2 4 # UNDIFFERENTIATED 1 0 2 qtable(ex_adsl, col_vars = \"ARM\") # A: Drug X B: Placebo C: Combination # (N=134) (N=134) (N=132) # ——————————————————————————————————————————————— # count 134 134 132 qtable(ex_adsl, col_vars = \"ARM\", row_vars = \"SEX\") # A: Drug X B: Placebo C: Combination # count (N=134) (N=134) (N=132) # —————————————————————————————————————————————————————————— # F 79 77 66 # M 51 55 60 # U 3 2 4 # UNDIFFERENTIATED 1 0 2 qtable(ex_adsl, \"ARM\", show_colcounts = FALSE) # count all obs # ———————————————————————— # A: Drug X 134 # B: Placebo 134 # C: Combination 132 tmp_adsl <- ex_adsl tmp_adsl$new <- rep_len(c(\"\", \"A\", \"B\"), nrow(tmp_adsl)) qtable(tmp_adsl, row_vars = \"new\")"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"nested-tables","dir":"Articles","previous_headings":"","what":"Nested Tables","title":"Exploratory Analysis","text":"Providing one variable name row column structure qtable() create nested table. Arbitrary nesting supported dimension. Note default, unobserved factor levels within facet included table. can modified drop_levels. code adds row 0s STRATA1 level “B” nested SEX level “UNDIFFERENTIATED”. contrast, table() return nested table. Rather produces list contingency tables two variables used inputs. help stats::ftable() nested structure can achieved two steps.","code":"qtable(ex_adsl, row_vars = c(\"SEX\", \"STRATA1\"), col_vars = c(\"ARM\", \"STRATA2\")) # A: Drug X B: Placebo C: Combination # S1 S2 S1 S2 S1 S2 # count (N=73) (N=61) (N=67) (N=67) (N=56) (N=76) # ———————————————————————————————————————————————————————————————————————— # F # A 12 9 11 13 7 11 # B 14 11 12 15 9 12 # C 17 16 13 13 14 13 # M # A 5 11 10 9 6 14 # B 13 8 7 10 9 12 # C 8 6 13 6 8 11 # U # A 1 0 1 0 1 0 # B 1 0 0 1 0 1 # C 1 0 0 0 1 1 # UNDIFFERENTIATED # A 0 0 0 0 0 1 # C 1 0 0 0 1 0 qtable( ex_adsl, row_vars = c(\"SEX\", \"STRATA1\"), col_vars = c(\"ARM\", \"STRATA2\"), drop_levels = FALSE ) # A: Drug X B: Placebo C: Combination # S1 S2 S1 S2 S1 S2 # count (N=73) (N=61) (N=67) (N=67) (N=56) (N=76) # ———————————————————————————————————————————————————————————————————————— # F # A 12 9 11 13 7 11 # B 14 11 12 15 9 12 # C 17 16 13 13 14 13 # M # A 5 11 10 9 6 14 # B 13 8 7 10 9 12 # C 8 6 13 6 8 11 # U # A 1 0 1 0 1 0 # B 1 0 0 1 0 1 # C 1 0 0 0 1 1 # UNDIFFERENTIATED # A 0 0 0 0 0 1 # B 0 0 0 0 0 0 # C 1 0 0 0 1 0 table(ex_adsl$SEX, ex_adsl$STRATA1, ex_adsl$ARM, ex_adsl$STRATA2) # , , = A: Drug X, = S1 # # # A B C # F 12 14 17 # M 5 13 8 # U 1 1 1 # UNDIFFERENTIATED 0 0 1 # # , , = B: Placebo, = S1 # # # A B C # F 11 12 13 # M 10 7 13 # U 1 0 0 # UNDIFFERENTIATED 0 0 0 # # , , = C: Combination, = S1 # # # A B C # F 7 9 14 # M 6 9 8 # U 1 0 1 # UNDIFFERENTIATED 0 0 1 # # , , = A: Drug X, = S2 # # # A B C # F 9 11 16 # M 11 8 6 # U 0 0 0 # UNDIFFERENTIATED 0 0 0 # # , , = B: Placebo, = S2 # # # A B C # F 13 15 13 # M 9 10 6 # U 0 1 0 # UNDIFFERENTIATED 0 0 0 # # , , = C: Combination, = S2 # # # A B C # F 11 12 13 # M 14 12 11 # U 0 1 1 # UNDIFFERENTIATED 1 0 0 t1 <- ftable(ex_adsl[, c(\"SEX\", \"STRATA1\", \"ARM\", \"STRATA2\")]) ftable(t1, row.vars = c(\"SEX\", \"STRATA1\")) # ARM A: Drug X B: Placebo C: Combination # STRATA2 S1 S2 S1 S2 S1 S2 # SEX STRATA1 # F A 12 9 11 13 7 11 # B 14 11 12 15 9 12 # C 17 16 13 13 14 13 # M A 5 11 10 9 6 14 # B 13 8 7 10 9 12 # C 8 6 13 6 8 11 # U A 1 0 1 0 1 0 # B 1 0 0 1 0 1 # C 1 0 0 0 1 1 # UNDIFFERENTIATED A 0 0 0 0 0 1 # B 0 0 0 0 0 0 # C 1 0 0 0 1 0"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"na-values","dir":"Articles","previous_headings":"","what":"NA Values","title":"Exploratory Analysis","text":"far examples seen, used counts summarize data table cell default analysis used qtable(). Internally, single analysis variable specified avar used generate counts table. default analysis variable first variable data. case ex_adsl “STUDYID”. Let’s see happens introduce NA values analysis variable: resulting table showing 0’s across cells values analysis variable NA. Keep behavior mind quick exploratory analysis using default counts aggregate function qtable. suit purpose, can either pre-process data recode NA values use another analysis function. see latter done Custom Aggregation section. addition, row column variables NA levels explicitly labelled . done, columns /rows reflect full data. Explicitly labeling NA levels column facet adds column table:","code":"tmp_adsl <- ex_adsl tmp_adsl[[1]] <- NA_character_ qtable(tmp_adsl, row_vars = \"ARM\", col_vars = \"SEX\") # F M U UNDIFFERENTIATED # count (N=222) (N=166) (N=9) (N=3) # ————————————————————————————————————————————————————————————— # A: Drug X 0 0 0 0 # B: Placebo 0 0 0 0 # C: Combination 0 0 0 0 # Recode NA values tmp_adsl[[1]] <- addNA(tmp_adsl[[1]]) qtable(tmp_adsl, row_vars = \"ARM\", col_vars = \"SEX\") # F M U UNDIFFERENTIATED # count (N=222) (N=166) (N=9) (N=3) # ————————————————————————————————————————————————————————————— # A: Drug X 79 51 3 1 # B: Placebo 77 55 2 0 # C: Combination 66 60 4 2 tmp_adsl$new1 <- factor(NA_character_, levels = c(\"X\", \"Y\", \"Z\")) qtable(tmp_adsl, row_vars = \"ARM\", col_vars = \"new1\") # X Y Z # count (N=0) (N=0) (N=0) # —————————————————————————————————————— # A: Drug X 0 0 0 # B: Placebo 0 0 0 # C: Combination 0 0 0 tmp_adsl$new2 <- addNA(tmp_adsl$new1) levels(tmp_adsl$new2)[4] <- \"\" # NA needs to be a recognizible string qtable(tmp_adsl, row_vars = \"ARM\", col_vars = \"new2\") # X Y Z # count (N=0) (N=0) (N=0) (N=400) # ———————————————————————————————————————————————— # A: Drug X 0 0 0 134 # B: Placebo 0 0 0 134 # C: Combination 0 0 0 132"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"custom-aggregation","dir":"Articles","previous_headings":"","what":"Custom Aggregation","title":"Exploratory Analysis","text":"powerful feature qtable() user can define type function used summarize data facet. can specify type analysis summary using afun argument: Note analysis variable AGE analysis function name included top right header table. analysis function returns vector 2 3 elements, result displayed multi-valued single cells. want use analysis function 3 summary elements, can use list. case, values displayed table multiple stacked cells within facet. list elements named, names used row labels. advanced formatting can controlled in_rows(). See function documentation details.","code":"qtable(ex_adsl, row_vars = \"STRATA2\", col_vars = \"ARM\", avar = \"AGE\", afun = mean) # A: Drug X B: Placebo C: Combination # AGE - mean (N=134) (N=134) (N=132) # ———————————————————————————————————————————————————— # S1 34.10 36.46 35.70 # S2 33.38 34.40 35.24 qtable(ex_adsl, row_vars = \"STRATA2\", col_vars = \"ARM\", avar = \"AGE\", afun = range) # A: Drug X B: Placebo C: Combination # AGE - range (N=134) (N=134) (N=132) # ———————————————————————————————————————————————————————— # S1 23.0 / 48.0 24.0 / 62.0 20.0 / 69.0 # S2 21.0 / 50.0 21.0 / 58.0 23.0 / 64.0 fivenum2 <- function(x) { setNames(as.list(fivenum(x)), c(\"min\", \"Q1\", \"MED\", \"Q3\", \"max\")) } qtable(ex_adsl, row_vars = \"STRATA2\", col_vars = \"ARM\", avar = \"AGE\", afun = fivenum2) # A: Drug X B: Placebo C: Combination # AGE - fivenum2 (N=134) (N=134) (N=132) # ———————————————————————————————————————————————————————— # S1 # min 23.00 24.00 20.00 # Q1 28.00 30.00 30.50 # MED 34.00 36.00 35.00 # Q3 39.00 40.50 40.00 # max 48.00 62.00 69.00 # S2 # min 21.00 21.00 23.00 # Q1 29.00 29.50 30.00 # MED 32.00 32.00 34.50 # Q3 38.00 39.50 38.00 # max 50.00 58.00 64.00 meansd_range <- function(x) { in_rows( \"Mean (sd)\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\"), \"Range\" = rcell(range(x), format = \"xx - xx\") ) } qtable(ex_adsl, row_vars = \"STRATA2\", col_vars = \"ARM\", avar = \"AGE\", afun = meansd_range) # A: Drug X B: Placebo C: Combination # AGE - meansd_range (N=134) (N=134) (N=132) # ————————————————————————————————————————————————————————————————— # S1 # Mean (sd) 34.10 (6.71) 36.46 (7.72) 35.70 (8.22) # Range 23 - 48 24 - 62 20 - 69 # S2 # Mean (sd) 33.38 (6.40) 34.40 (7.99) 35.24 (7.39) # Range 21 - 50 21 - 58 23 - 64"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"marginal-summaries","dir":"Articles","previous_headings":"","what":"Marginal Summaries","title":"Exploratory Analysis","text":"Another feature qtable() ability quickly add marginal summary rows summarize_groups argument. summary add table count non-NA records analysis variable level nesting. example, compare two tables: second table, marginal summary rows level two row facet variables: STRATA1 STRATA2. number 18 second row gives count observations part ARM level “: Drug X”, STRATA1 level “”, STRATA2 level “S1”. percent calculated cell count divided column count given table header. can see mean AGE 31.61 subgroup based 18 subjects correspond 13.4% subjects arm “: Drug X”. See ?summarize_row_groups add marginal summary rows using core rtables framework.","code":"qtable( ex_adsl, row_vars = c(\"STRATA1\", \"STRATA2\"), col_vars = \"ARM\", avar = \"AGE\", afun = mean ) # A: Drug X B: Placebo C: Combination # AGE - mean (N=134) (N=134) (N=132) # ———————————————————————————————————————————————————— # A # S1 31.61 36.68 34.00 # S2 34.40 33.55 34.35 # B # S1 34.57 37.68 35.83 # S2 32.79 34.77 36.68 # C # S1 35.26 35.38 36.58 # S2 32.95 34.89 34.72 qtable( ex_adsl, row_vars = c(\"STRATA1\", \"STRATA2\"), col_vars = \"ARM\", summarize_groups = TRUE, avar = \"AGE\", afun = mean ) # A: Drug X B: Placebo C: Combination # AGE - mean (N=134) (N=134) (N=132) # ————————————————————————————————————————————————————————— # A 38 (28.4%) 44 (32.8%) 40 (30.3%) # S1 18 (13.4%) 22 (16.4%) 14 (10.6%) # AGE - mean 31.61 36.68 34.00 # S2 20 (14.9%) 22 (16.4%) 26 (19.7%) # AGE - mean 34.40 33.55 34.35 # B 47 (35.1%) 45 (33.6%) 43 (32.6%) # S1 28 (20.9%) 19 (14.2%) 18 (13.6%) # AGE - mean 34.57 37.68 35.83 # S2 19 (14.2%) 26 (19.4%) 25 (18.9%) # AGE - mean 32.79 34.77 36.68 # C 49 (36.6%) 45 (33.6%) 49 (37.1%) # S1 27 (20.1%) 26 (19.4%) 24 (18.2%) # AGE - mean 35.26 35.38 36.58 # S2 22 (16.4%) 19 (14.2%) 25 (18.9%) # AGE - mean 32.95 34.89 34.72"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"table-decorations","dir":"Articles","previous_headings":"","what":"Table Decorations","title":"Exploratory Analysis","text":"Tables generated qtable() can include annotations titles, subtitles footnotes like :","code":"qtable( ex_adsl, row_vars = \"STRATA2\", col_vars = \"ARM\", title = \"Strata 2 Summary\", subtitle = paste0(\"STUDY \", ex_adsl$STUDYID[1]), main_footer = paste0(\"Date: \", as.character(Sys.Date())) ) # Strata 2 Summary # STUDY AB12345 # # ——————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # count (N=134) (N=134) (N=132) # ——————————————————————————————————————————————— # S1 73 67 56 # S2 61 67 76 # ——————————————————————————————————————————————— # # Date: 2023-11-24"},{"path":"https://insightsengineering.github.io/rtables/articles/exploratory_analysis.html","id":"summary","dir":"Articles","previous_headings":"","what":"Summary","title":"Exploratory Analysis","text":"learned vignette: qtable() can replace extend uses table() stats::ftable() qtable() useful exploratory data analysis intended use qtable() exploratory data analysis, limited functionality building complex tables. details get started core rtables layout functionality see introduction vignette.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"formats-precedence","dir":"Articles","previous_headings":"","what":"Formats Precedence","title":"Format Precedence and NA Handling","text":"Users rtables package can specify format numbers reporting tables printed. Formatting functionality provided formatters R package. See formatters::list_valid_format_labels() list available formats. format can specified user different places. may happen , single table layout, format specified one place. case, final format applied depends format precedence rules defined rtables. vignette, describe basic rules rtables format precedence. examples shown vignette utilize example ADSL dataset, demographic table summarizes variables content different population subsets (encoded columns). Note ex_* data currently attached rtables package provided formatters package created using publicly available random.cdisc.data R package.","code":"library(rtables) ADSL <- ex_adsl"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"format-precedence-and-inheritance-rules","dir":"Articles","previous_headings":"Formats Precedence","what":"Format Precedence and Inheritance Rules","title":"Format Precedence and NA Handling","text":"format numbers printed can specified user different places. context precedence, important level split hierarchy formats specified . general, two levels: cell level -called parent table level. concept cell parent table results way rtables package stores resulting tables. models resulting tables hierarchical, tree-like objects cells (leaves) containing multiple values. Particularly noteworthy context fact actual table splitting occurs row-dominant way (even column splitting present layout). rtables provides user-end function table_structure() prints structure given table object. simple illustration, consider following example: table, 4 sub-tables SEX table. : F, M, U, UNDIFFERENTIATED. sub-tables one sub-table AGE. example, first AGE sub-table, parent table F. concept hierarchical, tree-like representations resulting tables translates directly format precedence inheritance rules. general principle, format finally applied cell one specific, , one closest cell given path tree. Hence, precedence-inheritance chain looks like following: chain, outermost parent_table least specific place specify format, cell specific one. cases format specified user one place, one specific applied cell. specific format selected user split, default format applied. default format \"xx\" yields formatting .character() function. following sections vignette, illustrate format precedence rules examples.","code":"lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(vars = \"AGE\", afun = mean) adsl_analyzed <- build_table(lyt, ADSL) adsl_analyzed # A: Drug X B: Placebo C: Combination # ————————————————————————————————————————————————————————————————————————— # F # mean 32.7594936708861 34.1168831168831 35.1969696969697 # M # mean 35.5686274509804 37.4363636363636 35.3833333333333 # U # mean 31.6666666666667 31 35.25 # UNDIFFERENTIATED # mean 28 NA 45 table_structure(adsl_analyzed) # [TableTree] SEX # [TableTree] F # [ElementaryTable] AGE (1 x 3) # [TableTree] M # [ElementaryTable] AGE (1 x 3) # [TableTree] U # [ElementaryTable] AGE (1 x 3) # [TableTree] UNDIFFERENTIATED # [ElementaryTable] AGE (1 x 3) parent_table -> parent_table -> ... -> parent_table -> cell"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"standard-format","dir":"Articles","previous_headings":"Formats Precedence","what":"Standard Format","title":"Format Precedence and NA Handling","text":"simple layout explicitly set format output analysis function. case, default format applied.","code":"lyt0 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = \"AGE\", afun = mean) build_table(lyt0, ADSL) # A: Drug X B: Placebo C: Combination # ————————————————————————————————————————————————————————————— # mean 33.7686567164179 35.4328358208955 35.4318181818182"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"cell-format","dir":"Articles","previous_headings":"Formats Precedence","what":"Cell Format","title":"Format Precedence and NA Handling","text":"format cell can explicitly specified via rcell() in_rows() functions. former essentially collection data objects latter collection rcell() objects. previously mentioned, specific place format can specified user. format specified places time, one specified via in_rows() takes highest precedence. Technically, case, format defined rcell() simply overwritten one defined in_rows(). format specified in_rows() applied cells rows (overriding previously specified cell-specific values), indicates precedence rules described still place.","code":"lyt1 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = \"AGE\", afun = function(x) { rcell(mean(x), format = \"xx.xx\", label = \"Mean\") }) build_table(lyt1, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 33.77 35.43 35.43 lyt1a <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x)), .formats = \"xx.xx\" ) }) build_table(lyt1a, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 33.77 35.43 35.43 lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x), format = \"xx.xxx\"), .formats = \"xx.xx\" ) }) build_table(lyt2, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 33.77 35.43 35.43"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"parent-table-format-and-inheritance","dir":"Articles","previous_headings":"Formats Precedence","what":"Parent Table Format and Inheritance","title":"Format Precedence and NA Handling","text":"addition cell level, format can specified parent table level. format set user cell, specific format cell one defined innermost parent table split (). cell format also specified cell, parent table format ignored cell since cell format specific therefore takes precedence. following, slightly complicated, example, can observe partial inheritance. , SD cells inherit parent table’s format Mean cells .","code":"lyt3 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(vars = \"AGE\", mean, format = \"xx.x\") build_table(lyt3, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # mean 33.8 35.4 35.4 lyt4 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze( vars = \"AGE\", afun = function(x) { rcell(mean(x), format = \"xx.xx\", label = \"Mean\") }, format = \"xx.x\" ) build_table(lyt4, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 33.77 35.43 35.43 lyt4a <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze( vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x)), \"SD\" = rcell(sd(x)), .formats = \"xx.xx\" ) }, format = \"xx.x\" ) build_table(lyt4a, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 33.77 35.43 35.43 # SD 6.55 7.90 7.72 lyt5 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze( vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x), format = \"xx.xx\"), \"SD\" = rcell(sd(x)) ) }, format = \"xx.x\" ) build_table(lyt5, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————— # Mean 33.77 35.43 35.43 # SD 6.6 7.9 7.7"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"na-handling","dir":"Articles","previous_headings":"","what":"NA Handling","title":"Format Precedence and NA Handling","text":"Consider following layout resulting table created: output cell corresponding UNDIFFERENTIATED level SEX B: Placebo level ARM displayed NA. occurs non-NA values facet used compute mean. rtables allows user specify string display cell values NA. Similar formats numbers, user can specify string replace NA parameter format_na_str .format_na_str. can specified cell parent table level. NA string precedence inheritance rules number format precedence, described previous section vignette. illustrate examples.","code":"lyt6 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(vars = \"AGE\", afun = mean, format = \"xx.xx\") build_table(lyt6, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # mean 32.76 34.12 35.20 # M # mean 35.57 37.44 35.38 # U # mean 31.67 31.00 35.25 # UNDIFFERENTIATED # mean 28.00 NA 45.00"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"replacing-na-values-at-the-cell-level","dir":"Articles","previous_headings":"NA Handling","what":"Replacing NA Values at the Cell Level","title":"Format Precedence and NA Handling","text":"cell level, possible replace NA values custom string means format_na_str parameter rcell() .format_na_str parameter in_rows(). NA string specified places time, one specified in_rows() takes precedence. Technically, case NA replacement string defined rcell() simply overwritten one defined in_rows(). NA string specified in_rows() applied cells, rows (overriding previously specified cell specific values), means precedence rules described still place.","code":"lyt7 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(vars = \"AGE\", afun = function(x) { rcell(mean(x), format = \"xx.xx\", label = \"Mean\", format_na_str = \"\") }) build_table(lyt7, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # Mean 32.76 34.12 35.20 # M # Mean 35.57 37.44 35.38 # U # Mean 31.67 31.00 35.25 # UNDIFFERENTIATED # Mean 28.00 45.00 lyt7a <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x), format = \"xx.xx\"), .format_na_strs = \"\" ) }) build_table(lyt7a, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # Mean 32.76 34.12 35.20 # M # Mean 35.57 37.44 35.38 # U # Mean 31.67 31.00 35.25 # UNDIFFERENTIATED # Mean 28.00 45.00 lyt8 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x), format = \"xx.xx\", format_na_str = \"\"), .format_na_strs = \"\" ) }) build_table(lyt8, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # Mean 32.76 34.12 35.20 # M # Mean 35.57 37.44 35.38 # U # Mean 31.67 31.00 35.25 # UNDIFFERENTIATED # Mean 28.00 45.00"},{"path":"https://insightsengineering.github.io/rtables/articles/format_precedence.html","id":"parent-table-replacement-of-na-values-and-inheritance-principles","dir":"Articles","previous_headings":"NA Handling","what":"Parent Table Replacement of NA Values and Inheritance Principles","title":"Format Precedence and NA Handling","text":"addition cell level, string replacement NA values can specified parent table level. replacement string specified user cell, specific NA string cell one defined innermost parent table split (). NA value replacement string also specified cell level, one set parent table level ignored cell cell level format specific therefore takes precedence. following, slightly complicated example, can observe partial inheritance NA strings. , SD cells inherit parent table’s NA string, Mean cells .","code":"lyt9 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze(vars = \"AGE\", mean, format = \"xx.xx\", na_str = \"not available\") build_table(lyt9, ADSL) # A: Drug X B: Placebo C: Combination # ————————————————————————————————————————————————————————————— # F # mean 32.76 34.12 35.20 # M # mean 35.57 37.44 35.38 # U # mean 31.67 31.00 35.25 # UNDIFFERENTIATED # mean 28.00 not available 45.00 lyt10 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze( vars = \"AGE\", afun = function(x) { rcell(mean(x), format = \"xx.xx\", label = \"Mean\", format_na_str = \"\") }, na_str = \"not available\" ) build_table(lyt10, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # Mean 32.76 34.12 35.20 # M # Mean 35.57 37.44 35.38 # U # Mean 31.67 31.00 35.25 # UNDIFFERENTIATED # Mean 28.00 45.00 lyt10a <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze( vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x)), \"SD\" = rcell(sd(x)), .formats = \"xx.xx\", .format_na_strs = \"\" ) }, na_str = \"not available\" ) build_table(lyt10a, ADSL) # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # Mean 32.76 34.12 35.20 # SD 6.09 7.06 7.43 # M # Mean 35.57 37.44 35.38 # SD 7.08 8.69 8.24 # U # Mean 31.67 31.00 35.25 # SD 3.21 5.66 3.10 # UNDIFFERENTIATED # Mean 28.00 45.00 # SD 1.41 lyt11 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\") %>% analyze( vars = \"AGE\", afun = function(x) { in_rows( \"Mean\" = rcell(mean(x), format_na_str = \"\"), \"SD\" = rcell(sd(x)) ) }, format = \"xx.xx\", na_str = \"not available\" ) build_table(lyt11, ADSL) # A: Drug X B: Placebo C: Combination # ————————————————————————————————————————————————————————————————— # F # Mean 32.76 34.12 35.20 # SD 6.09 7.06 7.43 # M # Mean 35.57 37.44 35.38 # SD 7.08 8.69 8.24 # U # Mean 31.67 31.00 35.25 # SD 3.21 5.66 3.10 # UNDIFFERENTIATED # Mean 28.00 45.00 # SD not available not available 1.41"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Introduction to rtables","text":"rtables R package provides framework create, tabulate output tables R. design requirements rtables origin studying tables commonly used report analyses clinical trials; however, careful keep rtables general purpose toolkit. number table frameworks available R gt RStudio, xtable, tableone, tables name . number reasons implement rtables (yet another tables R package): output tables ASCII text files table rendering (ASCII, HTML, etc.) separate data model. Hence, one always access non-rounded/non-formatted numbers. pagination horizontal vertical directions meet health authority submission requirements cell, row, column, table reference system titles, footers, referential footnotes path based access cell content useful automated content generation remainder vignette, give short introduction rtables tabulating table. content based useR 2020 presentation Gabriel Becker. packages used vignette rtables dplyr:","code":"library(rtables) library(dplyr)"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"data","dir":"Articles","previous_headings":"","what":"Data","title":"Introduction to rtables","text":"data used vignette made using random number generators. data content relatively simple: one row per imaginary person one column per measurement: study arm, country origin, gender, handedness, age, weight. Note use factor variables level order represented row column order tabulate information df .","code":"n <- 400 set.seed(1) df <- tibble( arm = factor(sample(c(\"Arm A\", \"Arm B\"), n, replace = TRUE), levels = c(\"Arm A\", \"Arm B\")), country = factor(sample(c(\"CAN\", \"USA\"), n, replace = TRUE, prob = c(.55, .45)), levels = c(\"CAN\", \"USA\")), gender = factor(sample(c(\"Female\", \"Male\"), n, replace = TRUE), levels = c(\"Female\", \"Male\")), handed = factor(sample(c(\"Left\", \"Right\"), n, prob = c(.6, .4), replace = TRUE), levels = c(\"Left\", \"Right\")), age = rchisq(n, 30) + 10 ) %>% mutate( weight = 35 * rnorm(n, sd = .5) + ifelse(gender == \"Female\", 140, 180) ) head(df) # # A tibble: 6 × 6 # arm country gender handed age weight # # 1 Arm A USA Female Left 31.3 139. # 2 Arm B CAN Female Right 50.5 116. # 3 Arm A USA Male Right 32.4 186. # 4 Arm A USA Male Right 34.6 169. # 5 Arm B USA Female Right 43.0 160. # 6 Arm A USA Female Right 43.2 126."},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"building-a-table","dir":"Articles","previous_headings":"","what":"Building a Table","title":"Introduction to rtables","text":"aim vignette build following table step step:","code":"# Arm A Arm B # Female Male Female Male # (N=96) (N=105) (N=92) (N=107) # ———————————————————————————————————————————————————————————— # CAN 45 (46.9%) 64 (61.0%) 46 (50.0%) 62 (57.9%) # Left 32 (33.3%) 42 (40.0%) 26 (28.3%) 37 (34.6%) # mean 38.9 40.4 40.3 37.7 # Right 13 (13.5%) 22 (21.0%) 20 (21.7%) 25 (23.4%) # mean 36.6 40.2 40.2 40.6 # USA 51 (53.1%) 41 (39.0%) 46 (50.0%) 45 (42.1%) # Left 34 (35.4%) 19 (18.1%) 25 (27.2%) 25 (23.4%) # mean 40.4 39.7 39.2 40.1 # Right 17 (17.7%) 22 (21.0%) 21 (22.8%) 20 (18.7%) # mean 36.9 39.8 38.5 39.0"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"starting-simple","dir":"Articles","previous_headings":"","what":"Starting Simple","title":"Introduction to rtables","text":"rtables basic table defined 0 rows one column representing data. Analyzing variable one way adding row:","code":"lyt <- basic_table() %>% analyze(\"age\", mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # all obs # —————————————— # mean 39.4"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"layout-instructions","dir":"Articles","previous_headings":"Starting Simple","what":"Layout Instructions","title":"Introduction to rtables","text":"code first described table assigned description variable lyt. built table using actual data build_table(). description table called table layout. basic_table() start every table layout contains information one column representing data. analyze() instruction adds layout age variable analyzed mean() analysis function result rounded 1 decimal place. Hence, layout “pre-data”, , ’s description build table get data. can look layout isolated: general layouting instructions summarized : basic_table() layout representing table zero rows one column row space: split_rows_by(), split_rows_by_multivar(), split_rows_by_cuts(), split_rows_by_cutfun(), split_rows_by_quartiles() column space: split_cols_by(), split_cols_by_multivar(), split_cols_by_cuts(), split_cols_by_cutfun(), split_cols_by_quartiles() Summarizing Groups: summarize_row_groups() Analyzing Variables: analyze(), analyze_colvars() Using functions, possible create wide variety tables show document.","code":"lyt # A Pre-data Table Layout # # Column-Split Structure: # () # # Row-Split Structure: # age (** analysis **)"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"adding-column-structure","dir":"Articles","previous_headings":"Starting Simple","what":"Adding Column Structure","title":"Introduction to rtables","text":"now add structure columns adding column split based factor variable arm: resulting table one column per factor level arm. data represented first column df[df$arm == \"ARM \", ]. Hence, split_cols_by() partitions data among columns default. Column splitting can done recursive/nested manner adding sequential split_cols_by() layout instruction. ’s also possible add non-nested split. splitting arm gender: first column represents data df df$arm == \"\" & df$gender == \"Female\" second column data df df$arm == \"\" & df$gender == \"Male\", .","code":"lyt <- basic_table() %>% split_cols_by(\"arm\") %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # ———————————————————— # mean 39.5 39.4 lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # ———————————————————————————————————— # mean 38.8 40.1 39.6 39.2"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"adding-row-structure","dir":"Articles","previous_headings":"Starting Simple","what":"Adding Row Structure","title":"Introduction to rtables","text":"far, created layouts analysis column splitting instructions, .e. analyze() split_cols_by(), respectively. resulted table multiple columns one data row. add row structure stratifying mean analysis country (.e. adding split row space): table data used derive first data cell (average age female Canadians Arm ) df$country == \"CAN\" & df$arm == \"Arm \" & df$gender == \"Female\". cell value can also calculated manually: Row structure can also used group table titled groups pages rendering. via ‘page splits’, declared via page_by = TRUE within call split_rows_by: go detail page-splits control page-group specific titles Title footer vignette. Note print render table without pagination, page_by splits currently rendered normal row splits. may change future releases.","code":"lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # —————————————————————————————————————— # CAN # mean 38.2 40.3 40.3 38.9 # USA # mean 39.2 39.7 38.9 39.6 mean(df$age[df$country == \"CAN\" & df$arm == \"Arm A\" & df$gender == \"Female\"]) # [1] 38.22447 lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\", page_by = TRUE) %>% split_rows_by(\"handed\") %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) cat(export_as_txt(tbl, page_type = \"letter\", page_break = \"\\n\\n~~~~~~ Page Break ~~~~~~\\n\\n\")) # # country: CAN # # ———————————————————————————————————————— # Arm A Arm B # Female Male Female Male # ———————————————————————————————————————— # Left # mean 38.9 40.4 40.3 37.7 # Right # mean 36.6 40.2 40.2 40.6 # # # ~~~~~~ Page Break ~~~~~~ # # # country: USA # # ———————————————————————————————————————— # Arm A Arm B # Female Male Female Male # ———————————————————————————————————————— # Left # mean 40.4 39.7 39.2 40.1 # Right # mean 36.9 39.8 38.5 39.0"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"adding-group-information","dir":"Articles","previous_headings":"Starting Simple","what":"Adding Group Information","title":"Introduction to rtables","text":"adding row splits, get default label rows split level, example CAN USA table . Besides column space subsetting, now subsetted data cell. often useful defining row splitting display information row group. rtables referred content information, .e. mean() row 2 descendant CAN (visible via indenting, though table underlying tree structure importance vignette). order add content information turn CAN label row content row, summarize_row_groups() function required. default, count (nrows()) percentage data relative column associated data calculated: relative percentage average age female Canadians calculated follows: group percentages per row split sum 1 column. can split row space dividing country handedness: Next, add count percentage summary handedness within country:","code":"lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% summarize_row_groups() %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # —————————————————————————————————————————————————————————— # CAN 45 (46.9%) 64 (61.0%) 46 (50.0%) 62 (57.9%) # mean 38.2 40.3 40.3 38.9 # USA 51 (53.1%) 41 (39.0%) 46 (50.0%) 45 (42.1%) # mean 39.2 39.7 38.9 39.6 df_cell <- subset(df, df$country == \"CAN\" & df$arm == \"Arm A\" & df$gender == \"Female\") df_col_1 <- subset(df, df$arm == \"Arm A\" & df$gender == \"Female\") c(count = nrow(df_cell), percentage = nrow(df_cell) / nrow(df_col_1)) # count percentage # 45.00000 0.46875 lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% summarize_row_groups() %>% split_rows_by(\"handed\") %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # ———————————————————————————————————————————————————————————— # CAN 45 (46.9%) 64 (61.0%) 46 (50.0%) 62 (57.9%) # Left # mean 38.9 40.4 40.3 37.7 # Right # mean 36.6 40.2 40.2 40.6 # USA 51 (53.1%) 41 (39.0%) 46 (50.0%) 45 (42.1%) # Left # mean 40.4 39.7 39.2 40.1 # Right # mean 36.9 39.8 38.5 39.0 lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% summarize_row_groups() %>% split_rows_by(\"handed\") %>% summarize_row_groups() %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # ———————————————————————————————————————————————————————————— # CAN 45 (46.9%) 64 (61.0%) 46 (50.0%) 62 (57.9%) # Left 32 (33.3%) 42 (40.0%) 26 (28.3%) 37 (34.6%) # mean 38.9 40.4 40.3 37.7 # Right 13 (13.5%) 22 (21.0%) 20 (21.7%) 25 (23.4%) # mean 36.6 40.2 40.2 40.6 # USA 51 (53.1%) 41 (39.0%) 46 (50.0%) 45 (42.1%) # Left 34 (35.4%) 19 (18.1%) 25 (27.2%) 25 (23.4%) # mean 40.4 39.7 39.2 40.1 # Right 17 (17.7%) 22 (21.0%) 21 (22.8%) 20 (18.7%) # mean 36.9 39.8 38.5 39.0"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"introspecting-rtables-table-objects","dir":"Articles","previous_headings":"","what":"Introspecting rtables Table Objects","title":"Introduction to rtables","text":"created table, can inspect structure using number functions. table_structure() function prints summary table’s row structure one two levels detail. default, summarizes structure subtable level. detail argument set \"row\", however, provides detailed row-level summary, acts useful alternative might normally use str() function interrogate compound nested lists. make_row_df() make_col_df() functions create data.frame variety information table’s structure. useful introspection purposes label, name, abs_rownumber, path node_class columns (remainder information returned data.frame used pagination) default make_row_df() summarizes visible rows, setting visible_only FALSE gives us structural summary table, including full hierarchy subtables, including aren’t represented directly visible rows: make_col_df() similarly accepts visible_only, though meaning slightly different, indicating whether leaf columns summarized (TRUE, default) whether higher level groups columns, analogous subtables row space, summarized well. row_paths_summary() col_paths_summary() functions wrap respective make_*_df functions, printing name, node_class path information (row case), label path information (column case), indented illustrate table structure:","code":"table_structure(tbl) # [TableTree] country # [TableTree] CAN [cont: 1 x 4] # [TableTree] handed # [TableTree] Left [cont: 1 x 4] # [ElementaryTable] age (1 x 4) # [TableTree] Right [cont: 1 x 4] # [ElementaryTable] age (1 x 4) # [TableTree] USA [cont: 1 x 4] # [TableTree] handed # [TableTree] Left [cont: 1 x 4] # [ElementaryTable] age (1 x 4) # [TableTree] Right [cont: 1 x 4] # [ElementaryTable] age (1 x 4) table_structure(tbl, detail = \"row\") # TableTree: [country] (country) # labelrow: [country] (country) - # children: # TableTree: [CAN] (CAN) # labelrow: [CAN] (CAN) - # content: # ElementaryTable: [CAN@content] () # labelrow: [] () - # children: # ContentRow: [CAN] (CAN) # children: # TableTree: [handed] (handed) # labelrow: [handed] (handed) - # children: # TableTree: [Left] (Left) # labelrow: [Left] (Left) - # content: # ElementaryTable: [Left@content] () # labelrow: [] () - # children: # ContentRow: [Left] (Left) # children: # ElementaryTable: [age] (age) # labelrow: [age] (age) - # children: # DataRow: [mean] (mean) # TableTree: [Right] (Right) # labelrow: [Right] (Right) - # content: # ElementaryTable: [Right@content] () # labelrow: [] () - # children: # ContentRow: [Right] (Right) # children: # ElementaryTable: [age] (age) # labelrow: [age] (age) - # children: # DataRow: [mean] (mean) # TableTree: [USA] (USA) # labelrow: [USA] (USA) - # content: # ElementaryTable: [USA@content] () # labelrow: [] () - # children: # ContentRow: [USA] (USA) # children: # TableTree: [handed] (handed) # labelrow: [handed] (handed) - # children: # TableTree: [Left] (Left) # labelrow: [Left] (Left) - # content: # ElementaryTable: [Left@content] () # labelrow: [] () - # children: # ContentRow: [Left] (Left) # children: # ElementaryTable: [age] (age) # labelrow: [age] (age) - # children: # DataRow: [mean] (mean) # TableTree: [Right] (Right) # labelrow: [Right] (Right) - # content: # ElementaryTable: [Right@content] () # labelrow: [] () - # children: # ContentRow: [Right] (Right) # children: # ElementaryTable: [age] (age) # labelrow: [age] (age) - # children: # DataRow: [mean] (mean) make_row_df(tbl)[, c(\"label\", \"name\", \"abs_rownumber\", \"path\", \"node_class\")] # label name abs_rownumber path node_class # 1 CAN CAN 1 country,.... ContentRow # 2 Left Left 2 country,.... ContentRow # 3 mean mean 3 country,.... DataRow # 4 Right Right 4 country,.... ContentRow # 5 mean mean 5 country,.... DataRow # 6 USA USA 6 country,.... ContentRow # 7 Left Left 7 country,.... ContentRow # 8 mean mean 8 country,.... DataRow # 9 Right Right 9 country,.... ContentRow # 10 mean mean 10 country,.... DataRow make_row_df(tbl, visible_only = FALSE)[, c(\"label\", \"name\", \"abs_rownumber\", \"path\", \"node_class\")] # label name abs_rownumber path node_class # 1 country NA country TableTree # 2 CAN NA country, CAN TableTree # 3 CAN@content NA country,.... ElementaryTable # 4 CAN CAN 1 country,.... ContentRow # 5 handed NA country,.... TableTree # 6 Left NA country,.... TableTree # 7 Left@content NA country,.... ElementaryTable # 8 Left Left 2 country,.... ContentRow # 9 age NA country,.... ElementaryTable # 10 mean mean 3 country,.... DataRow # 11 Right NA country,.... TableTree # 12 Right@content NA country,.... ElementaryTable # 13 Right Right 4 country,.... ContentRow # 14 age NA country,.... ElementaryTable # 15 mean mean 5 country,.... DataRow # 16 USA NA country, USA TableTree # 17 USA@content NA country,.... ElementaryTable # 18 USA USA 6 country,.... ContentRow # 19 handed NA country,.... TableTree # 20 Left NA country,.... TableTree # 21 Left@content NA country,.... ElementaryTable # 22 Left Left 7 country,.... ContentRow # 23 age NA country,.... ElementaryTable # 24 mean mean 8 country,.... DataRow # 25 Right NA country,.... TableTree # 26 Right@content NA country,.... ElementaryTable # 27 Right Right 9 country,.... ContentRow # 28 age NA country,.... ElementaryTable # 29 mean mean 10 country,.... DataRow make_col_df(tbl) # name label abs_pos path pos_in_siblings n_siblings leaf_indices # 1 Female Female 1 arm, Arm.... 1 2 1 # 2 Male Male 2 arm, Arm.... 2 2 2 # 3 Female Female 3 arm, Arm.... 1 2 3 # 4 Male Male 4 arm, Arm.... 2 2 4 # total_span col_fnotes n_col_fnotes # 1 1 0 # 2 1 0 # 3 1 0 # 4 1 0 make_col_df(tbl, visible_only = FALSE) # name label abs_pos path pos_in_siblings n_siblings leaf_indices # 1 Arm A Arm A NA arm, Arm A 1 2 1, 2 # 2 Female Female 1 arm, Arm.... 1 2 1 # 3 Male Male 2 arm, Arm.... 2 2 2 # 4 Arm B Arm B NA arm, Arm B 2 2 3, 4 # 5 Female Female 3 arm, Arm.... 1 2 3 # 6 Male Male 4 arm, Arm.... 2 2 4 # total_span col_fnotes n_col_fnotes # 1 2 0 # 2 1 0 # 3 1 0 # 4 2 0 # 5 1 0 # 6 1 0 row_paths_summary(tbl) # rowname node_class path # —————————————————————————————————————————————————————————————————————— # CAN ContentRow country, CAN, @content, CAN # Left ContentRow country, CAN, handed, Left, @content, Left # mean DataRow country, CAN, handed, Left, age, mean # Right ContentRow country, CAN, handed, Right, @content, Right # mean DataRow country, CAN, handed, Right, age, mean # USA ContentRow country, USA, @content, USA # Left ContentRow country, USA, handed, Left, @content, Left # mean DataRow country, USA, handed, Left, age, mean # Right ContentRow country, USA, handed, Right, @content, Right # mean DataRow country, USA, handed, Right, age, mean col_paths_summary(tbl) # label path # —————————————————————————————————————— # Arm A arm, Arm A # Female arm, Arm A, gender, Female # Male arm, Arm A, gender, Male # Arm B arm, Arm B # Female arm, Arm B, gender, Female # Male arm, Arm B, gender, Male"},{"path":"https://insightsengineering.github.io/rtables/articles/introduction.html","id":"summary","dir":"Articles","previous_headings":"","what":"Summary","title":"Introduction to rtables","text":"vignette learned: means much tabulation splitting/subsetting data tables can described pre-data using layouts tables form visualization data vignettes rtables package provide detailed information rtables package. recommend continue tabulation_dplyr vignette compares information derived table vignette using dplyr.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/manual_table_construction.html","id":"overview","dir":"Articles","previous_headings":"","what":"Overview","title":"Constructing rtables Manually","text":"main functions currently associated rtables Tables rtables can constructed via layout rtabulate tabulation frameworks also manually. Currently manual table construction way define column spans. main functions manual table constructions : rtable(): collection rrow() objects, column header default format rrow(): collection rcell() objects default format rcell(): collection data objects cell format","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/manual_table_construction.html","id":"simple-example","dir":"Articles","previous_headings":"","what":"Simple Example","title":"Constructing rtables Manually","text":"go explaining individual components used create table continue html conversion rtable() object: Next, [ operator lets access cell content. format cell run format_rcell(tbl[1,1])=. Note tbl[6, 1] tbl[6, 2] display rcell colspan.","code":"library(rtables) tbl <- rtable( header = c(\"Treatement\\nN=100\", \"Comparison\\nN=300\"), format = \"xx (xx.xx%)\", rrow(\"A\", c(104, .2), c(100, .4)), rrow(\"B\", c(23, .4), c(43, .5)), rrow(), rrow(\"this is a very long section header\"), rrow(\"estimate\", rcell(55.23, \"xx.xx\", colspan = 2)), rrow(\"95% CI\", indent = 1, rcell(c(44.8, 67.4), format = \"(xx.x, xx.x)\", colspan = 2)) ) as_html(tbl, width = \"80%\") tbl[1, 1] # Treatement # N=100 # ———————————————— # A 104 (20.00%)"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Pruning and Sorting Tables","text":"Often want filter reorder elements table ways take account table structure. example: Sorting subtables corresponding factor levels commonly observed levels occur first table. Sorting rows within single subtable Removing subtables represent 0 observations filtering contain 0 rows.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"a-table-in-need-of-attention","dir":"Articles","previous_headings":"","what":"A Table In Need of Attention","title":"Pruning and Sorting Tables","text":"","code":"library(rtables) library(dplyr) raw_lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by(\"SEX\") %>% split_rows_by(\"RACE\") %>% summarize_row_groups() %>% split_rows_by(\"STRATA1\") %>% summarize_row_groups() %>% analyze(\"AGE\") raw_tbl <- build_table(raw_lyt, DM) raw_tbl # A: Drug X B: Placebo C: Combination # F M U UNDIFFERENTIATED F M U UNDIFFERENTIATED F M U UNDIFFERENTIATED # —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 0 (NA%) 0 (NA%) 37 (66.1%) 31 (62.0%) 0 (NA%) 0 (NA%) 40 (65.6%) 44 (64.7%) 0 (NA%) 0 (NA%) # A 15 (21.4%) 12 (23.5%) 0 (NA%) 0 (NA%) 14 (25.0%) 6 (12.0%) 0 (NA%) 0 (NA%) 15 (24.6%) 16 (23.5%) 0 (NA%) 0 (NA%) # Mean 30.40 34.42 NA NA 35.43 30.33 NA NA 37.40 36.25 NA NA # B 16 (22.9%) 8 (15.7%) 0 (NA%) 0 (NA%) 13 (23.2%) 16 (32.0%) 0 (NA%) 0 (NA%) 10 (16.4%) 12 (17.6%) 0 (NA%) 0 (NA%) # Mean 33.75 34.88 NA NA 32.46 30.94 NA NA 33.30 35.92 NA NA # C 13 (18.6%) 15 (29.4%) 0 (NA%) 0 (NA%) 10 (17.9%) 9 (18.0%) 0 (NA%) 0 (NA%) 15 (24.6%) 16 (23.5%) 0 (NA%) 0 (NA%) # Mean 36.92 35.60 NA NA 34.00 31.89 NA NA 33.47 31.38 NA NA # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 0 (NA%) 0 (NA%) 12 (21.4%) 12 (24.0%) 0 (NA%) 0 (NA%) 13 (21.3%) 14 (20.6%) 0 (NA%) 0 (NA%) # A 5 (7.1%) 1 (2.0%) 0 (NA%) 0 (NA%) 5 (8.9%) 2 (4.0%) 0 (NA%) 0 (NA%) 4 (6.6%) 4 (5.9%) 0 (NA%) 0 (NA%) # Mean 31.20 33.00 NA NA 28.00 30.00 NA NA 30.75 36.50 NA NA # B 7 (10.0%) 3 (5.9%) 0 (NA%) 0 (NA%) 3 (5.4%) 3 (6.0%) 0 (NA%) 0 (NA%) 6 (9.8%) 6 (8.8%) 0 (NA%) 0 (NA%) # Mean 36.14 34.33 NA NA 29.67 32.00 NA NA 36.33 31.00 NA NA # C 6 (8.6%) 6 (11.8%) 0 (NA%) 0 (NA%) 4 (7.1%) 7 (14.0%) 0 (NA%) 0 (NA%) 3 (4.9%) 4 (5.9%) 0 (NA%) 0 (NA%) # Mean 31.33 39.67 NA NA 34.50 34.00 NA NA 33.00 36.50 NA NA # WHITE 8 (11.4%) 6 (11.8%) 0 (NA%) 0 (NA%) 7 (12.5%) 7 (14.0%) 0 (NA%) 0 (NA%) 8 (13.1%) 10 (14.7%) 0 (NA%) 0 (NA%) # A 2 (2.9%) 1 (2.0%) 0 (NA%) 0 (NA%) 3 (5.4%) 3 (6.0%) 0 (NA%) 0 (NA%) 1 (1.6%) 5 (7.4%) 0 (NA%) 0 (NA%) # Mean 34.00 45.00 NA NA 29.33 33.33 NA NA 35.00 32.80 NA NA # B 4 (5.7%) 3 (5.9%) 0 (NA%) 0 (NA%) 1 (1.8%) 4 (8.0%) 0 (NA%) 0 (NA%) 3 (4.9%) 1 (1.5%) 0 (NA%) 0 (NA%) # Mean 37.00 43.67 NA NA 48.00 36.75 NA NA 34.33 36.00 NA NA # C 2 (2.9%) 2 (3.9%) 0 (NA%) 0 (NA%) 3 (5.4%) 0 (0.0%) 0 (NA%) 0 (NA%) 4 (6.6%) 4 (5.9%) 0 (NA%) 0 (NA%) # Mean 35.50 44.00 NA NA 44.67 NA NA NA 38.50 35.00 NA NA # AMERICAN INDIAN OR ALASKA NATIVE 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # A 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # B 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # C 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # MULTIPLE 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # A 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # B 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # C 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # A 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # B 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # C 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # OTHER 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # A 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # B 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # C 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # UNKNOWN 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # A 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # B 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA # C 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) 0 (0.0%) 0 (0.0%) 0 (NA%) 0 (NA%) # Mean NA NA NA NA NA NA NA NA NA NA NA NA"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"trimming-rows","dir":"Articles","previous_headings":"Trimming","what":"Trimming Rows","title":"Pruning and Sorting Tables","text":"Trimming represents convenience wrapper around simple, direct subsetting rows TableTree. use trim_rows() function table criteria function. rows criteria function returns TRUE removed, others retained. NOTE: row kept removed completely independently, awareness surrounding structure. means, example, subtree analysis rows removed removed . structure-aware filtering table, use pruning described next section. trimming function accepts TableRow object returns TRUE row removed. default trimming function removes rows columns values , .e. NA values 0 values:","code":"trim_rows(raw_tbl) # A: Drug X B: Placebo C: Combination # F M U UNDIFFERENTIATED F M U UNDIFFERENTIATED F M U UNDIFFERENTIATED # —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 0 (NA%) 0 (NA%) 37 (66.1%) 31 (62.0%) 0 (NA%) 0 (NA%) 40 (65.6%) 44 (64.7%) 0 (NA%) 0 (NA%) # A 15 (21.4%) 12 (23.5%) 0 (NA%) 0 (NA%) 14 (25.0%) 6 (12.0%) 0 (NA%) 0 (NA%) 15 (24.6%) 16 (23.5%) 0 (NA%) 0 (NA%) # Mean 30.40 34.42 NA NA 35.43 30.33 NA NA 37.40 36.25 NA NA # B 16 (22.9%) 8 (15.7%) 0 (NA%) 0 (NA%) 13 (23.2%) 16 (32.0%) 0 (NA%) 0 (NA%) 10 (16.4%) 12 (17.6%) 0 (NA%) 0 (NA%) # Mean 33.75 34.88 NA NA 32.46 30.94 NA NA 33.30 35.92 NA NA # C 13 (18.6%) 15 (29.4%) 0 (NA%) 0 (NA%) 10 (17.9%) 9 (18.0%) 0 (NA%) 0 (NA%) 15 (24.6%) 16 (23.5%) 0 (NA%) 0 (NA%) # Mean 36.92 35.60 NA NA 34.00 31.89 NA NA 33.47 31.38 NA NA # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 0 (NA%) 0 (NA%) 12 (21.4%) 12 (24.0%) 0 (NA%) 0 (NA%) 13 (21.3%) 14 (20.6%) 0 (NA%) 0 (NA%) # A 5 (7.1%) 1 (2.0%) 0 (NA%) 0 (NA%) 5 (8.9%) 2 (4.0%) 0 (NA%) 0 (NA%) 4 (6.6%) 4 (5.9%) 0 (NA%) 0 (NA%) # Mean 31.20 33.00 NA NA 28.00 30.00 NA NA 30.75 36.50 NA NA # B 7 (10.0%) 3 (5.9%) 0 (NA%) 0 (NA%) 3 (5.4%) 3 (6.0%) 0 (NA%) 0 (NA%) 6 (9.8%) 6 (8.8%) 0 (NA%) 0 (NA%) # Mean 36.14 34.33 NA NA 29.67 32.00 NA NA 36.33 31.00 NA NA # C 6 (8.6%) 6 (11.8%) 0 (NA%) 0 (NA%) 4 (7.1%) 7 (14.0%) 0 (NA%) 0 (NA%) 3 (4.9%) 4 (5.9%) 0 (NA%) 0 (NA%) # Mean 31.33 39.67 NA NA 34.50 34.00 NA NA 33.00 36.50 NA NA # WHITE 8 (11.4%) 6 (11.8%) 0 (NA%) 0 (NA%) 7 (12.5%) 7 (14.0%) 0 (NA%) 0 (NA%) 8 (13.1%) 10 (14.7%) 0 (NA%) 0 (NA%) # A 2 (2.9%) 1 (2.0%) 0 (NA%) 0 (NA%) 3 (5.4%) 3 (6.0%) 0 (NA%) 0 (NA%) 1 (1.6%) 5 (7.4%) 0 (NA%) 0 (NA%) # Mean 34.00 45.00 NA NA 29.33 33.33 NA NA 35.00 32.80 NA NA # B 4 (5.7%) 3 (5.9%) 0 (NA%) 0 (NA%) 1 (1.8%) 4 (8.0%) 0 (NA%) 0 (NA%) 3 (4.9%) 1 (1.5%) 0 (NA%) 0 (NA%) # Mean 37.00 43.67 NA NA 48.00 36.75 NA NA 34.33 36.00 NA NA # C 2 (2.9%) 2 (3.9%) 0 (NA%) 0 (NA%) 3 (5.4%) 0 (0.0%) 0 (NA%) 0 (NA%) 4 (6.6%) 4 (5.9%) 0 (NA%) 0 (NA%) # Mean 35.50 44.00 NA NA 44.67 NA NA NA 38.50 35.00 NA NA"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"trimming-columns","dir":"Articles","previous_headings":"Trimming","what":"Trimming Columns","title":"Pruning and Sorting Tables","text":"currently special utilities trimming columns can remove empty columns fairly straightforward column subsetting using col_counts() function: Now, interesting see table structured: deeper understanding fundamental structures rtables, suggest taking look slides 69-76 Slide deck. brief, important notice [TableTree] RACE root table split (split_rows_by(\"RACE\") %>%) two subtables: [TableTree] ASIAN [cont: 1 x 6] [TableTree] BLACK AFRICAN AMERICAN [cont: 1 x 6]. “described” summarize_row_groups() %>%, creates every split “content” table containing 1 row (1 cont: 1 x 6), rendered takes place LabelRow. two subtables contain STRATA1 table, representing split_rows_by(\"STRATA1\") layout, , similar RACE table, split subtables: one strata similar content tables; individual strata subtable, , contains ElementaryTable (whose children individual rows) generated analyze(\"AGE\") layout directive, .e. [ElementaryTable] AGE (1 x 6). subtable row structure important sorting pruning; values “content” (ContentRow) “value” (DataRow) rows use different access functions treated differently. Another interesting function can used understand connection row names representational path following:","code":"coltrimmed <- raw_tbl[, col_counts(raw_tbl) > 0] # Note: method with signature 'VTableTree#missing#ANY' chosen for function '[', # target signature 'TableTree#missing#logical'. # \"VTableTree#ANY#logical\" would also be valid h_coltrimmed <- head(coltrimmed, n = 14) h_coltrimmed # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 table_structure(h_coltrimmed) # [TableTree] RACE # [TableTree] ASIAN [cont: 1 x 6] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] B [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] C [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] BLACK OR AFRICAN AMERICAN [cont: 1 x 6] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] B [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] C [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) row_paths_summary(h_coltrimmed) # rowname node_class path # ——————————————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN ContentRow RACE, ASIAN, @content, ASIAN # A ContentRow RACE, ASIAN, STRATA1, A, @content, A # Mean DataRow RACE, ASIAN, STRATA1, A, AGE, Mean # B ContentRow RACE, ASIAN, STRATA1, B, @content, B # Mean DataRow RACE, ASIAN, STRATA1, B, AGE, Mean # C ContentRow RACE, ASIAN, STRATA1, C, @content, C # Mean DataRow RACE, ASIAN, STRATA1, C, AGE, Mean # BLACK OR AFRICAN AMERICAN ContentRow RACE, BLACK OR AFRICAN AMERICAN, @content, BLACK OR AFRICAN AMERICAN # A ContentRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, A, @content, A # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, A, AGE, Mean # B ContentRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, B, @content, B # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, B, AGE, Mean # C ContentRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, C, @content, C # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, C, AGE, Mean"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"pruning","dir":"Articles","previous_headings":"","what":"Pruning","title":"Pruning and Sorting Tables","text":"Pruning similar outcome trimming, powerful complex, takes structure account. Pruning applied recursively, structural unit (subtable, row) applies pruning function level ’s children (user-specifiable maximum depth). default pruning function, example, determines subtree empty : Removing children contain single content row contains zeros NAs Removing rows contain either zeros NAs Removing full subtree unpruned children remain can also use low_obs_pruner() pruning function constructor create pruning function removes subtrees content summaries whose first entries column sum average specified number. (default summaries first entry per column count). Note pruning applied recursively, ASIAN subtree remains even though full BLACK AFRICAN AMERICAN subtree encompassed enough observations, strata within . can take care setting stop_depth pruning 1. can also see pruning lower number observations, say, total 16, stop_depth removes strata third race (WHITE).","code":"pruned <- prune_table(coltrimmed) pruned # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 pruned2 <- prune_table(coltrimmed, low_obs_pruner(10, \"mean\")) pruned2 # A: Drug X B: Placebo C: Combination # F M F M F M # —————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 pruned3 <- prune_table(coltrimmed, low_obs_pruner(10, \"sum\"), stop_depth = 1) pruned3 # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 pruned4 <- prune_table(coltrimmed, low_obs_pruner(16, \"sum\")) pruned4 # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"sorting-fundamentals","dir":"Articles","previous_headings":"Sorting","what":"Sorting Fundamentals","title":"Pruning and Sorting Tables","text":"Sorting rtables table done path, meaning sort operation occur particular location within table, direct children element path reordered. occurs whether children subtables , individual rows. Sorting done via sort_at_path() function, accepts (row) path scoring function. score function accepts subtree TableRow returns single orderable (typically numeric) value. Within subtable currently sorted, children reordered value score function. Importantly, “content” (ContentRow) “values” (DataRow) need treated differently scoring function retrieved: content subtable retrieved via content _table accessor. cont_n_allcols() scoring function provided rtables, works scoring subtables sum first elements first row subtable’s content table. Note function fails child scored content function (.e., summarize_row_groups() used corresponding point layout). can see ’s definition, : Therefore, fundamental difference pruning sorting sorting occurs particular places table, defined path. example, can sort strata values (ContentRow) observation counts within just ASIAN subtable:","code":"cont_n_allcols # function (tt) # { # ctab <- content_table(tt) # if (NROW(ctab) == 0) { # stop(\"cont_n_allcols score function used at subtable [\", # obj_name(tt), \"] that has no content table.\") # } # sum(sapply(row_values(tree_children(ctab)[[1]]), function(cv) cv[1])) # } # # sort_at_path(pruned, path = c(\"RACE\", \"ASIAN\", \"STRATA1\"), scorefun = cont_n_allcols) # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 # B and C are swapped as the global count (sum of all column counts) of strata C is higher than the one of strata B"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"wildcards-in-sort-paths","dir":"Articles","previous_headings":"Sorting","what":"Wildcards in Sort Paths","title":"Pruning and Sorting Tables","text":"Unlike uses pathing (currently), sorting path can contain “*“. indicates children subtable matching * element path sorted separately indicated remainder path * score function. Thus can extend sorting strata within ASIAN subtable race-specific subtables using wildcard: equivalent separately calling following: possible understand better pathing table_structure() highlights tree-like structure node names: row_paths_summary: Note latter see content rows paths following @content, e.g., ASIAN, @content, ASIAN. first given path (.e., , @content, <> rows used scoring functions begin cont_. can directly sort ethnicity observations increasing order: Within ethnicity separately, sort strata number females arm C (.e. column position 5):","code":"sort_at_path(pruned, path = c(\"RACE\", \"*\", \"STRATA1\"), scorefun = cont_n_allcols) # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 # All subtables, i.e. ASIAN, BLACK..., and WHITE, are reordered separately tmptbl <- sort_at_path(pruned, path = c(\"RACE\", \"ASIAN\", \"STRATA1\"), scorefun = cont_n_allcols) tmptbl <- sort_at_path(tmptbl, path = c(\"RACE\", \"BLACK OR AFRICAN AMERICAN\", \"STRATA1\"), scorefun = cont_n_allcols) tmptbl <- sort_at_path(tmptbl, path = c(\"RACE\", \"WHITE\", \"STRATA1\"), scorefun = cont_n_allcols) tmptbl # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 table_structure(pruned) # [TableTree] RACE # [TableTree] ASIAN [cont: 1 x 6] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] B [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] C [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] BLACK OR AFRICAN AMERICAN [cont: 1 x 6] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] B [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] C [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] WHITE [cont: 1 x 6] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] B [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) # [TableTree] C [cont: 1 x 6] # [ElementaryTable] AGE (1 x 6) row_paths_summary(pruned) # rowname node_class path # ——————————————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN ContentRow RACE, ASIAN, @content, ASIAN # A ContentRow RACE, ASIAN, STRATA1, A, @content, A # Mean DataRow RACE, ASIAN, STRATA1, A, AGE, Mean # B ContentRow RACE, ASIAN, STRATA1, B, @content, B # Mean DataRow RACE, ASIAN, STRATA1, B, AGE, Mean # C ContentRow RACE, ASIAN, STRATA1, C, @content, C # Mean DataRow RACE, ASIAN, STRATA1, C, AGE, Mean # BLACK OR AFRICAN AMERICAN ContentRow RACE, BLACK OR AFRICAN AMERICAN, @content, BLACK OR AFRICAN AMERICAN # A ContentRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, A, @content, A # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, A, AGE, Mean # B ContentRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, B, @content, B # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, B, AGE, Mean # C ContentRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, C, @content, C # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, C, AGE, Mean # WHITE ContentRow RACE, WHITE, @content, WHITE # A ContentRow RACE, WHITE, STRATA1, A, @content, A # Mean DataRow RACE, WHITE, STRATA1, A, AGE, Mean # B ContentRow RACE, WHITE, STRATA1, B, @content, B # Mean DataRow RACE, WHITE, STRATA1, B, AGE, Mean # C ContentRow RACE, WHITE, STRATA1, C, @content, C # Mean DataRow RACE, WHITE, STRATA1, C, AGE, Mean ethsort <- sort_at_path(pruned, path = c(\"RACE\"), scorefun = cont_n_allcols, decreasing = FALSE) ethsort # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 sort_at_path(pruned, path = c(\"RACE\", \"*\", \"STRATA1\"), cont_n_onecol(5)) # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"sorting-within-an-analysis-subtable","dir":"Articles","previous_headings":"Sorting","what":"Sorting Within an Analysis Subtable","title":"Pruning and Sorting Tables","text":"sorting within analysis subtable (e.g., subtable generated analysis function generates one row per group data), name subtable (generally name variable analyzed) must appear path, even variable label displayed table printed. show differences sorting analysis subtable (DataRow), content subtable (ContentRow), modify prune () similar raw table : now want sort median mean strata variables? need write custom score function ready-made ones moment work content nodes (content_table() access function cont_n_allcols() cont_n_onecol(), talk moment). , need think ordering, .e. need specify right path. suggest looking structure first table_structure() row_paths_summary(). see order AGE nodes need get something like : RACE, ASIAN, STRATA1, , AGE next level need sort. see now path sort first group. need wildcards: RACE, *, STRATA1, *, AGE. Now, found way select relevant paths want sort. want construct scoring function works median mean sort . , may want enter scoring function browser() see fed try retrieve single value returned sorting. allow user experiment , show possible solution considers summing column values retrieved row_values(tt) subtable fed function . Note score function defined subtable tt unique input parameter single numeric value output. help user visualize happening score function show example exploration debugging: can see powerful pragmatic might change sorting principles within custom scoring function. show selecting specific column sort. Looking pre-defined function cont_n_onecol() gives us insight proceed. see similar function cont_n_allcols() wrapped one allows parameter j used select specific column. selecting column want sort. table see mean median rows reordered values first column, compared raw table, desired. function can also columns nested within larger splits:","code":"more_analysis_fnc <- function(x) { in_rows( \"median\" = median(x), \"mean\" = mean(x), .formats = \"xx.x\" ) } raw_lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by( \"RACE\", split_fun = drop_and_remove_levels(\"WHITE\") # dropping WHITE levels ) %>% summarize_row_groups() %>% split_rows_by(\"STRATA1\") %>% summarize_row_groups() %>% analyze(\"AGE\", afun = more_analysis_fnc) tbl <- build_table(raw_lyt, DM) %>% prune_table() %>% print() # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————————— # ASIAN 79 (65.3%) 68 (64.2%) 84 (65.1%) # A 27 (22.3%) 20 (18.9%) 31 (24.0%) # median 30.0 33.0 36.0 # mean 32.2 33.9 36.8 # B 24 (19.8%) 29 (27.4%) 22 (17.1%) # median 32.5 32.0 34.0 # mean 34.1 31.6 34.7 # C 28 (23.1%) 19 (17.9%) 31 (24.0%) # median 36.5 34.0 33.0 # mean 36.2 33.0 32.4 # BLACK OR AFRICAN AMERICAN 28 (23.1%) 24 (22.6%) 27 (20.9%) # A 6 (5.0%) 7 (6.6%) 8 (6.2%) # median 32.0 29.0 32.5 # mean 31.5 28.6 33.6 # B 10 (8.3%) 6 (5.7%) 12 (9.3%) # median 33.0 30.0 33.5 # mean 35.6 30.8 33.7 # C 12 (9.9%) 11 (10.4%) 7 (5.4%) # median 33.0 36.0 32.0 # mean 35.5 34.2 35.0 table_structure(tbl) # Direct inspection into the tree-like structure of rtables # [TableTree] RACE # [TableTree] ASIAN [cont: 1 x 3] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 3] # [ElementaryTable] AGE (2 x 3) # [TableTree] B [cont: 1 x 3] # [ElementaryTable] AGE (2 x 3) # [TableTree] C [cont: 1 x 3] # [ElementaryTable] AGE (2 x 3) # [TableTree] BLACK OR AFRICAN AMERICAN [cont: 1 x 3] # [TableTree] STRATA1 # [TableTree] A [cont: 1 x 3] # [ElementaryTable] AGE (2 x 3) # [TableTree] B [cont: 1 x 3] # [ElementaryTable] AGE (2 x 3) # [TableTree] C [cont: 1 x 3] # [ElementaryTable] AGE (2 x 3) scorefun <- function(tt) { # Here we could use browser() sum(unlist(row_values(tt))) } sort_at_path(tbl, c(\"RACE\", \"*\", \"STRATA1\", \"*\", \"AGE\"), scorefun) # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————————— # ASIAN 79 (65.3%) 68 (64.2%) 84 (65.1%) # A 27 (22.3%) 20 (18.9%) 31 (24.0%) # mean 32.2 33.9 36.8 # median 30.0 33.0 36.0 # B 24 (19.8%) 29 (27.4%) 22 (17.1%) # mean 34.1 31.6 34.7 # median 32.5 32.0 34.0 # C 28 (23.1%) 19 (17.9%) 31 (24.0%) # median 36.5 34.0 33.0 # mean 36.2 33.0 32.4 # BLACK OR AFRICAN AMERICAN 28 (23.1%) 24 (22.6%) 27 (20.9%) # A 6 (5.0%) 7 (6.6%) 8 (6.2%) # mean 31.5 28.6 33.6 # median 32.0 29.0 32.5 # B 10 (8.3%) 6 (5.7%) 12 (9.3%) # mean 35.6 30.8 33.7 # median 33.0 30.0 33.5 # C 12 (9.9%) 11 (10.4%) 7 (5.4%) # mean 35.5 34.2 35.0 # median 33.0 36.0 32.0 > sort_at_path(tbl, c(\"RACE\", \"*\", \"STRATA1\", \"*\", \"AGE\"), scorefun) Called from: scorefun(x) Browse[1]> tt ### THIS IS THE LEAF LEVEL -> DataRow ### [DataRow indent_mod 0]: median 30.0 33.0 36.0 Browse[1]> row_values(tt) ### Extraction of values -> It will be a named list! ### $`A: Drug X` [1] 30 $`B: Placebo` [1] 33 $`C: Combination` [1] 36 Browse[1]> sum(unlist(row_values(tt))) ### Final value we want to give back to sort_at_path ### [1] 99 cont_n_onecol # function (j) # { # function(tt) { # ctab <- content_table(tt) # if (NROW(ctab) == 0) { # stop(\"cont_n_allcols score function used at subtable [\", # obj_name(tt), \"] that has no content table.\") # } # row_values(tree_children(ctab)[[1]])[[j]][1] # } # } # # scorefun_onecol <- function(colpath) { function(tt) { # Here we could use browser() unlist(cell_values(tt, colpath = colpath), use.names = FALSE)[1] # Modified to lose the list names } } sort_at_path(tbl, c(\"RACE\", \"*\", \"STRATA1\", \"*\", \"AGE\"), scorefun_onecol(colpath = c(\"ARM\", \"A: Drug X\"))) # A: Drug X B: Placebo C: Combination # ———————————————————————————————————————————————————————————————————— # ASIAN 79 (65.3%) 68 (64.2%) 84 (65.1%) # A 27 (22.3%) 20 (18.9%) 31 (24.0%) # mean 32.2 33.9 36.8 # median 30.0 33.0 36.0 # B 24 (19.8%) 29 (27.4%) 22 (17.1%) # mean 34.1 31.6 34.7 # median 32.5 32.0 34.0 # C 28 (23.1%) 19 (17.9%) 31 (24.0%) # median 36.5 34.0 33.0 # mean 36.2 33.0 32.4 # BLACK OR AFRICAN AMERICAN 28 (23.1%) 24 (22.6%) 27 (20.9%) # A 6 (5.0%) 7 (6.6%) 8 (6.2%) # median 32.0 29.0 32.5 # mean 31.5 28.6 33.6 # B 10 (8.3%) 6 (5.7%) 12 (9.3%) # mean 35.6 30.8 33.7 # median 33.0 30.0 33.5 # C 12 (9.9%) 11 (10.4%) 7 (5.4%) # mean 35.5 34.2 35.0 # median 33.0 36.0 32.0 # Simpler table tbl <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by(\"SEX\", split_fun = drop_and_remove_levels(c(\"U\", \"UNDIFFERENTIATED\")) ) %>% analyze(\"AGE\", afun = more_analysis_fnc) %>% build_table(DM) %>% prune_table() %>% print() # A: Drug X B: Placebo C: Combination # F M F M F M # ————————————————————————————————————————————————————————— # median 32.0 35.0 33.0 31.0 35.0 32.0 # mean 33.7 36.5 33.8 32.1 34.9 34.3 sort_at_path(tbl, c(\"AGE\"), scorefun_onecol(colpath = c(\"ARM\", \"B: Placebo\", \"SEX\", \"F\"))) # A: Drug X B: Placebo C: Combination # F M F M F M # ————————————————————————————————————————————————————————— # mean 33.7 36.5 33.8 32.1 34.9 34.3 # median 32.0 35.0 33.0 31.0 35.0 32.0"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"writing-custom-pruning-criteria-and-scoring-functions","dir":"Articles","previous_headings":"","what":"Writing Custom Pruning Criteria and Scoring Functions","title":"Pruning and Sorting Tables","text":"Pruning criteria scoring functions map TableTree TableRow objects Boolean value (pruning criteria) sortable scalar value (scoring functions). currently need interact structure objects usual. Indeed, showed already sorting can complicated concept tree-like structure pathing well understood. important though mind following functions can used pruning sorting function retrieve relevant information table.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"useful-functions-and-accessors","dir":"Articles","previous_headings":"Writing Custom Pruning Criteria and Scoring Functions","what":"Useful Functions and Accessors","title":"Pruning and Sorting Tables","text":"accepts rowpath colpath restrict cell values returned obj_name() - Retrieves name object. Note can differ label displayed () printing. match element path. obj_label() - Retrieves display label object. Note can differ name appears path. content_table() - Retrieves TableTree object’s content table (contains summary rows). tree_children() - Retrieves TableTree object’s direct children (either subtables, rows possibly mix thereof, though happen practice)","code":""},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"sort-by-a-character-score","dir":"Articles","previous_headings":"Writing Custom Pruning Criteria and Scoring Functions > Example Custom Scoring Functions","what":"Sort by a character “score”","title":"Pruning and Sorting Tables","text":"case, convenience/simplicity, use name table element logic returns single string used . sort ethnicity alphabetical order (practice undoing previous sorting ethnicity ). NOTE: Generally appropriately done using reorder_split_levels() function within layout rather sort post-processing step, character scorers may may map easily layouting directives.","code":"silly_name_scorer <- function(tt) { nm <- obj_name(tt) print(nm) nm } sort_at_path(ethsort, \"RACE\", silly_name_scorer) # Now, it is sorted alphabetically! # [1] \"WHITE\" # [1] \"BLACK OR AFRICAN AMERICAN\" # [1] \"ASIAN\" # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00"},{"path":"https://insightsengineering.github.io/rtables/articles/sorting_pruning.html","id":"sort-by-the-percent-difference-in-counts-between-genders-in-arm-c","dir":"Articles","previous_headings":"Writing Custom Pruning Criteria and Scoring Functions > Example Custom Scoring Functions","what":"Sort by the Percent Difference in Counts Between Genders in Arm C","title":"Pruning and Sorting Tables","text":"need F M percents, Arm C (.e. columns 5 6), differenced. sort strata within ethnicity percent difference counts males females arm C. Note: statistically meaningful , fact terrible idea reorders strata seemingly () random within race, illustrates various things need inside custom sorting functions.","code":"silly_gender_diffcount <- function(tt) { ## (1st) content row has same name as object (STRATA1 level) rpath <- c(obj_name(tt), \"@content\", obj_name(tt)) ## the [1] below is cause these are count (pct%) cells ## and we only want the count part! mcount <- unlist(cell_values( tt, rowpath = rpath, colpath = c(\"ARM\", \"C: Combination\", \"SEX\", \"M\") ))[1] fcount <- unlist(cell_values( tt, rowpath = rpath, colpath = c(\"ARM\", \"C: Combination\", \"SEX\", \"F\") ))[1] (mcount - fcount) / fcount } sort_at_path(pruned, c(\"RACE\", \"*\", \"STRATA1\"), silly_gender_diffcount) # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 44 (62.9%) 35 (68.6%) 37 (66.1%) 31 (62.0%) 40 (65.6%) 44 (64.7%) # B 16 (22.9%) 8 (15.7%) 13 (23.2%) 16 (32.0%) 10 (16.4%) 12 (17.6%) # Mean 33.75 34.88 32.46 30.94 33.30 35.92 # A 15 (21.4%) 12 (23.5%) 14 (25.0%) 6 (12.0%) 15 (24.6%) 16 (23.5%) # Mean 30.40 34.42 35.43 30.33 37.40 36.25 # C 13 (18.6%) 15 (29.4%) 10 (17.9%) 9 (18.0%) 15 (24.6%) 16 (23.5%) # Mean 36.92 35.60 34.00 31.89 33.47 31.38 # BLACK OR AFRICAN AMERICAN 18 (25.7%) 10 (19.6%) 12 (21.4%) 12 (24.0%) 13 (21.3%) 14 (20.6%) # C 6 (8.6%) 6 (11.8%) 4 (7.1%) 7 (14.0%) 3 (4.9%) 4 (5.9%) # Mean 31.33 39.67 34.50 34.00 33.00 36.50 # A 5 (7.1%) 1 (2.0%) 5 (8.9%) 2 (4.0%) 4 (6.6%) 4 (5.9%) # Mean 31.20 33.00 28.00 30.00 30.75 36.50 # B 7 (10.0%) 3 (5.9%) 3 (5.4%) 3 (6.0%) 6 (9.8%) 6 (8.8%) # Mean 36.14 34.33 29.67 32.00 36.33 31.00 # WHITE 8 (11.4%) 6 (11.8%) 7 (12.5%) 7 (14.0%) 8 (13.1%) 10 (14.7%) # A 2 (2.9%) 1 (2.0%) 3 (5.4%) 3 (6.0%) 1 (1.6%) 5 (7.4%) # Mean 34.00 45.00 29.33 33.33 35.00 32.80 # C 2 (2.9%) 2 (3.9%) 3 (5.4%) 0 (0.0%) 4 (6.6%) 4 (5.9%) # Mean 35.50 44.00 44.67 NA 38.50 35.00 # B 4 (5.7%) 3 (5.9%) 1 (1.8%) 4 (8.0%) 3 (4.9%) 1 (1.5%) # Mean 37.00 43.67 48.00 36.75 34.33 36.00"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"provided-functions","dir":"Articles","previous_headings":"Controlling Facet Levels","what":"Provided Functions","title":"Controlling Splitting Behavior","text":"default, split_*_by(varname, ...) generates facet level variable varname takes data - including unobserved ones factor case. behavior can customized various ways. straightforward way customize facets generated split one split functions split function families provided rtables. predefined split functions function factories implement commonly desired customization patterns splitting behavior (.e., faceting behavior). include: remove_split_levels - remove specified levels data facet generation. keep_split_levels - keep specified levels data facet generation (removing others). drop_split_levels - drop levels unobserved within data split, .e., associated parent facet. reorder_split_levels - reorder levels (thus generated facets) specified order. trim_levels_in_group - drop unobserved levels another variable independently within data associated facet generated current split. add_overall_level, add_combo_levels - add additional “virtual” levels combine two levels variable split. See following section. trim_levels_to_map - trim levels multiple variables pre-specified set value combinations. See following section. first four fairly self-describing brevity, refer readers ?split_funcs details including working examples.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"controlling-combinations-of-levels-across-multiple-variables","dir":"Articles","previous_headings":"Controlling Facet Levels","what":"Controlling Combinations of Levels Across Multiple Variables","title":"Controlling Splitting Behavior","text":"Often nested splitting involving multiple variables, values variables question logically nested; meaning certain values inner variable coherent combination specific value values outer variable. example, suppose variable vehicle_class, can take values \"automobile\", \"boat\", variable vehicle_type, can take values \"car\", \"truck\", \"suv\",\"sailboat\", \"cruiseliner\". combination (\"automobile\", \"cruiseliner\") make sense never occur (correctly cleaned) data set; combination (\"boat\", \"truck\"). showcase strategies deal next sections using following artificial data:","code":"set.seed(0) levs_type <- c(\"car\", \"truck\", \"suv\", \"sailboat\", \"cruiseliner\") vclass <- sample(c(\"auto\", \"boat\"), 1000, replace = TRUE) auto_inds <- which(vclass == \"auto\") vtype <- rep(NA_character_, 1000) vtype[auto_inds] <- sample( c(\"car\", \"truck\"), ## suv missing on purpose length(auto_inds), replace = TRUE ) vtype[-auto_inds] <- sample( c(\"sailboat\", \"cruiseliner\"), 1000 - length(auto_inds), replace = TRUE ) vehic_data <- data.frame( vehicle_class = factor(vclass), vehicle_type = factor(vtype, levels = levs_type), color = sample( c(\"white\", \"black\", \"red\"), 1000, prob = c(1, 2, 1), replace = TRUE ), cost = ifelse( vclass == \"boat\", rnorm(1000, 100000, sd = 5000), rnorm(1000, 40000, sd = 5000) ) ) head(vehic_data) ## vehicle_class vehicle_type color cost ## 1 boat sailboat black 100393.81 ## 2 auto car white 38150.17 ## 3 boat sailboat white 98696.13 ## 4 auto truck white 37677.16 ## 5 auto truck black 38489.27 ## 6 boat cruiseliner black 108709.72"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"trim_levels_in_group","dir":"Articles","previous_headings":"Controlling Facet Levels > Controlling Combinations of Levels Across Multiple Variables","what":"trim_levels_in_group","title":"Controlling Splitting Behavior","text":"trim_levels_in_group split function factory creates split functions deal issue empirically; combination observed data tabulated appear nested facets within table, , . use default level-based faceting, get several logically incoherent cells within table: obviously table want, majority space taken meaningless combinations. use trim_levels_in_group trim levels vehicle_type separately within level vehicle_class, get table meaningful combinations: Note, however, contain meaningful combinations, actually observed data; happens include perfectly valid \"auto\", \"suv\" combination. restrict level combinations valid regardless whether combination observed, must use trim_levels_to_map() instead.","code":"library(rtables) lyt <- basic_table() %>% split_cols_by(\"color\") %>% split_rows_by(\"vehicle_class\") %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt, vehic_data) ## black white red ## ———————————————————————————————————————————————— ## auto ## car ## Mean 40431.92 40518.92 38713.14 ## truck ## Mean 40061.70 40635.74 40024.41 ## suv ## Mean NA NA NA ## sailboat ## Mean NA NA NA ## cruiseliner ## Mean NA NA NA ## boat ## car ## Mean NA NA NA ## truck ## Mean NA NA NA ## suv ## Mean NA NA NA ## sailboat ## Mean 99349.69 99996.54 101865.73 ## cruiseliner ## Mean 100212.00 99340.25 100363.52 lyt2 <- basic_table() %>% split_cols_by(\"color\") %>% split_rows_by(\"vehicle_class\", split_fun = trim_levels_in_group(\"vehicle_type\")) %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt2, vehic_data) ## black white red ## ———————————————————————————————————————————————— ## auto ## car ## Mean 40431.92 40518.92 38713.14 ## truck ## Mean 40061.70 40635.74 40024.41 ## boat ## sailboat ## Mean 99349.69 99996.54 101865.73 ## cruiseliner ## Mean 100212.00 99340.25 100363.52"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"trim_levels_to_map","dir":"Articles","previous_headings":"Controlling Facet Levels > Controlling Combinations of Levels Across Multiple Variables","what":"trim_levels_to_map","title":"Controlling Splitting Behavior","text":"trim_levels_to_map similar trim_levels_in_group purpose avoid combinatorial explosion nesting splitting logically nested variables. Unlike sibling function, however, trim_levels_to_map define exact set allowed combinations priori, exact set combinations produced resulting table, regardless whether observed . Now see \"auto\", \"suv\" combination present, even though populated NAs (data category), logically invalid combinations still absent.","code":"library(tibble) map <- tribble( ~vehicle_class, ~vehicle_type, \"auto\", \"truck\", \"auto\", \"suv\", \"auto\", \"car\", \"boat\", \"sailboat\", \"boat\", \"cruiseliner\" ) lyt3 <- basic_table() %>% split_cols_by(\"color\") %>% split_rows_by(\"vehicle_class\", split_fun = trim_levels_to_map(map)) %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt3, vehic_data) ## black white red ## ———————————————————————————————————————————————— ## auto ## car ## Mean 40431.92 40518.92 38713.14 ## truck ## Mean 40061.70 40635.74 40024.41 ## suv ## Mean NA NA NA ## boat ## sailboat ## Mean 99349.69 99996.54 101865.73 ## cruiseliner ## Mean 100212.00 99340.25 100363.52"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"combining-levels","dir":"Articles","previous_headings":"Controlling Facet Levels","what":"Combining Levels","title":"Controlling Splitting Behavior","text":"Another common manipulation faceting table context introduction combination levels explicitly modeled data. often, involves addition “overall” category, principle practice can involve arbitrary combination levels. rtables explicitly supports via add_overall_level (case) add_combo_levels split function factories.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"add_overall_level","dir":"Articles","previous_headings":"Controlling Facet Levels > Combining Levels","what":"add_overall_level","title":"Controlling Splitting Behavior","text":"add_overall_level accepts valname name new level, well label, first (whether come first, TRUE, last, FALSE, ordering). Building arbitrary vehicles table, can use create “colors” category: column counts turned , can see “Colors” column encompasses full 1000 (completely fake) vehicles data set. add arbitrary combinations, use add_combo_levels.","code":"lyt4 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"color\", split_fun = add_overall_level(\"allcolors\", label = \"All Colors\")) %>% split_rows_by(\"vehicle_class\", split_fun = trim_levels_to_map(map)) %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt4, vehic_data) ## All Colors black white red ## (N=1000) (N=521) (N=251) (N=228) ## ————————————————————————————————————————————————————————————— ## auto ## car ## Mean 40095.49 40431.92 40518.92 38713.14 ## truck ## Mean 40194.68 40061.70 40635.74 40024.41 ## suv ## Mean NA NA NA NA ## boat ## sailboat ## Mean 100133.22 99349.69 99996.54 101865.73 ## cruiseliner ## Mean 100036.76 100212.00 99340.25 100363.52"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"add_combo_levels","dir":"Articles","previous_headings":"Controlling Facet Levels > Combining Levels","what":"add_combo_levels","title":"Controlling Splitting Behavior","text":"add_combo_levels allows us add one arbitrary combination levels faceting structure table. defining combination data.frame describes levels want add. combination data.frame following columns one row combination add: valname - string indicating name value, appear paths. label - string indicating label displayed rendering. levelcombo - character vector individual levels combined combination level. exargs - list (usually list()) extra arguments passed analysis content functions tabulated within column row. Suppose wanted combinations levels non-white colors, white black colors. like :","code":"combodf <- tribble( ~valname, ~label, ~levelcombo, ~exargs, \"non-white\", \"Non-White\", c(\"black\", \"red\"), list(), \"blackwhite\", \"Black or White\", c(\"black\", \"white\"), list() ) lyt5 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"color\", split_fun = add_combo_levels(combodf)) %>% split_rows_by(\"vehicle_class\", split_fun = trim_levels_to_map(map)) %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt5, vehic_data) ## black white red Non-White Black or White ## (N=521) (N=251) (N=228) (N=749) (N=772) ## ————————————————————————————————————————————————————————————————————————————— ## auto ## car ## Mean 40431.92 40518.92 38713.14 39944.93 40460.77 ## truck ## Mean 40061.70 40635.74 40024.41 40050.66 40243.57 ## suv ## Mean NA NA NA NA NA ## boat ## sailboat ## Mean 99349.69 99996.54 101865.73 100179.72 99567.50 ## cruiseliner ## Mean 100212.00 99340.25 100363.52 100258.56 99937.47"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"fully-customizing-split-facet-behavior","dir":"Articles","previous_headings":"","what":"Fully Customizing Split (Facet) Behavior","title":"Controlling Splitting Behavior","text":"Beyond ability select common splitting customizations split functions split function factories rtables provides, can also fully customize every aspect splitting behavior creating split functions. possible hand, primary way via make_split_fun() function, accepts functions implementing different component behaviors combines split function can used layout. Splitting, faceting done rtables, can thought combination 3 steps: preprocessing - transformation incoming data faceted e.g., dropping unused factor levels, etc. splitting - mapping incoming data set 1 subsets representing individual facets. postprocessing - operations facets - e.g., combining , removing , etc. make_split_fun() function allows us specify custom behaviors steps independently defining custom splitting behavior via pre, core_split, post arguments, dictate steps, respectively. pre argument accepts zero pre-processing functions, must accept: df, spl, vals, labels, can optionally accept .spl_context. manipulate df (incoming data split) return modified data.frame. modified data.frame must contain columns present incoming data.frame, can add columns necessary. Although, note new columns used layout split analysis variables, present validity checking done. pre-processing component useful things manipulating factor levels, e.g., trim unobserved ones reorder levels based observed counts, etc. detailed discussion custom split functions , example custom split function implemented via make_split_fun(), see ?custom_split_funs.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"an-example-custom-split-function","dir":"Articles","previous_headings":"Fully Customizing Split (Facet) Behavior","what":"An Example Custom Split Function","title":"Controlling Splitting Behavior","text":"implement arbitrary, custom split function specify pre- post-processing instructions. unusual users need override core splitting logic - , fact, supported row space currently - leave example provide another narrow example usage .","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"an-illustrative-example-of-a-custom-split-function","dir":"Articles","previous_headings":"Fully Customizing Split (Facet) Behavior > An Example Custom Split Function","what":"An Illustrative Example of A Custom Split Function","title":"Controlling Splitting Behavior","text":"First, define two aspects ‘pre-processing step’ behavior: function reverses order levels variable (retaining level associated observation), function factory creates function removes level data associated . Finally implement post-processing function. reorder facets based amount data represents. Finally, construct custom split function use create table:","code":"## reverse order of levels rev_lev <- function(df, spl, vals, labels, ...) { ## in the split_rows_by() and split_cols_by() cases, ## spl_variable() gives us the variable var <- spl_variable(spl) vec <- df[[var]] levs <- if (is.character(vec)) unique(vec) else levels(vec) df[[var]] <- factor(vec, levels = rev(levs)) df } rem_lev_facet <- function(torem) { function(df, spl, vals, labels, ...) { var <- spl_variable(spl) vec <- df[[var]] bad <- vec == torem df <- df[!bad, ] levs <- if (is.character(vec)) unique(vec) else levels(vec) df[[var]] <- factor(as.character(vec[!bad]), levels = setdiff(levs, torem)) df } } sort_them_facets <- function(splret, spl, fulldf, ...) { ord <- order(sapply(splret$datasplit, nrow)) make_split_result( splret$values[ord], splret$datasplit[ord], splret$labels[ord] ) } silly_splfun1 <- make_split_fun( pre = list( rev_lev, rem_lev_facet(\"white\") ), post = list(sort_them_facets) ) lyt6 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"color\", split_fun = silly_splfun1) %>% split_rows_by(\"vehicle_class\", split_fun = trim_levels_to_map(map)) %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt6, vehic_data) ## red black ## (N=228) (N=521) ## ————————————————————————————————————— ## auto ## car ## Mean 38713.14 40431.92 ## truck ## Mean 40024.41 40061.70 ## suv ## Mean NA NA ## boat ## sailboat ## Mean 101865.73 99349.69 ## cruiseliner ## Mean 100363.52 100212.00"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"overriding-the-core-split-function","dir":"Articles","previous_headings":"Fully Customizing Split (Facet) Behavior > An Example Custom Split Function","what":"Overriding the Core Split Function","title":"Controlling Splitting Behavior","text":"Currently, overriding core split behavior supported functions used row splits. Next, write custom core-splitting function divides observations 4 groups: first 100, observations 101-500, observations 501-900, last hundred. claim test structural bias first last observations, really simply illustrate overriding core splitting machinery meaningful statistical purpose. can use construct splitting function. can combined pre- post-processing functions, stages performed independently, case, won’t, core splitting behavior pre- post-processing make much sense.","code":"silly_core_split <- function(spl, df, vals, labels, .spl_context) { make_split_result( c(\"first\", \"lowmid\", \"highmid\", \"last\"), datasplit = list( df[1:100, ], df[101:500, ], df[501:900, ], df[901:1000, ] ), labels = c( \"first 100\", \"obs 101-500\", \"obs 501-900\", \"last 100\" ) ) } even_sillier_splfun <- make_split_fun(core_split = silly_core_split) lyt7 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"color\") %>% split_rows_by(\"vehicle_class\", split_fun = even_sillier_splfun) %>% split_rows_by(\"vehicle_type\") %>% analyze(\"cost\") build_table(lyt7, vehic_data) ## black white red ## (N=521) (N=251) (N=228) ## ————————————————————————————————————————————————— ## first 100 ## car ## Mean 40496.05 37785.41 37623.17 ## truck ## Mean 41094.17 40437.29 37866.81 ## suv ## Mean NA NA NA ## sailboat ## Mean 100560.80 102017.05 101185.96 ## cruiseliner ## Mean 100838.12 96952.27 100610.71 ## obs 101-500 ## car ## Mean 39350.88 41185.98 37978.72 ## truck ## Mean 40166.87 41385.32 39885.72 ## suv ## Mean NA NA NA ## sailboat ## Mean 98845.47 99563.02 101462.79 ## cruiseliner ## Mean 101558.62 99039.91 97335.05 ## obs 501-900 ## car ## Mean 40721.82 40379.48 38681.26 ## truck ## Mean 39951.92 39846.89 39840.39 ## suv ## Mean NA NA NA ## sailboat ## Mean 99533.20 100347.18 102732.12 ## cruiseliner ## Mean 99140.43 100074.43 101994.99 ## last 100 ## car ## Mean 45204.44 40626.95 41214.33 ## truck ## Mean 38920.70 40620.47 42899.14 ## suv ## Mean NA NA NA ## sailboat ## Mean 99380.21 97644.77 101691.92 ## cruiseliner ## Mean 100017.53 99581.94 100751.30"},{"path":"https://insightsengineering.github.io/rtables/articles/split_functions.html","id":"design-of-pre--and-post-processing-functions-for-use-in-make_split_fun","dir":"Articles","previous_headings":"Fully Customizing Split (Facet) Behavior > An Example Custom Split Function","what":"Design of Pre- and Post-Processing Functions For Use in make_split_fun","title":"Controlling Splitting Behavior","text":"Pre-processing post-processing functions custom-splitting context best thought (implemented ) independent, atomic building blocks desired overall behavior. allows reused flexible mix--match way. rtables provides several behavior components implemented either functions function factories: drop_facet_levels - drop unobserved levels variable split trim_levels_in_facets - provides trim_levels_in_group behavior add_overall_facet - add combination facet full data add_combo_facet - add single combination facet (can used single make_split_fun call)","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/subsetting_tables.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Subsetting and Manipulating Table Contents","text":"TableTree objects based tree data structure name indicates. package written user need walk trees many basic table manipulations. Walking trees still necessary certain manipulation subject different vignette. vignette show methods subset tables extract cell values. use following table illustrative purposes:","code":"library(rtables) library(dplyr) lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% analyze(c(\"AGE\", \"STRATA1\")) tbl <- build_table(lyt, ex_adsl %>% filter(SEX %in% c(\"M\", \"F\"))) tbl # A: Drug X B: Placebo C: Combination # ——————————————————————————————————————————————————— # F # AGE # Mean 32.76 34.12 35.20 # STRATA1 # A 21 24 18 # B 25 27 21 # C 33 26 27 # M # AGE # Mean 35.57 37.44 35.38 # STRATA1 # A 16 19 20 # B 21 17 21 # C 14 19 19"},{"path":"https://insightsengineering.github.io/rtables/articles/subsetting_tables.html","id":"traditional-subsetting-and-modification-with","dir":"Articles","previous_headings":"","what":"Traditional Subsetting and modification with [","title":"Subsetting and Manipulating Table Contents","text":"[ [<- accessor functions operate largely data.frame cousins: particular means label rows treated rows empty cell values, rather rows without cells multiple values can specified row column position negative numeric positions supported, though like [.data.frame mixed positive ones [ always returns class object subset unless drop = TRUE [ , drop = TRUE returns raw (possibly multi-element) value associated cell. Known Differences [.data.frame - absolute position currently used reorder columns rows. Note general result ordering unlikely structurally valid. change order values, please read sorting pruning vignette relevant function (sort_at_path()). - character indices treated paths, vectors names [ [<- [ accessor function always returns TableTree object drop=TRUE set. first argument row indices second argument column indices. Alternatively logical subsetting can used. indices based visible rows tree structure. : table empty cell first row label row. need access cell actual cell data: retrieve value, use drop = TRUE: One can access multiple rows columns: Note repeat label rows descending children, e.g. show first row derived AGE. order repeat content/label information, one use pagination feature. Please read related vignette. Character indices interpreted paths (see ), elements matched names(tbl):","code":"tbl[1, 1] # A: Drug X # ————————————— # F tbl[3, 1] # A: Drug X # ———————————————— # Mean 32.76 tbl[3, 1, drop = TRUE] # [1] 32.75949 tbl[1:3, 1:2] # A: Drug X B: Placebo # ————————————————————————————————— # F # AGE # Mean 32.76 34.12 tbl[2:4, ] # A: Drug X B: Placebo C: Combination # ————————————————————————————————————————————————— # AGE # Mean 32.76 34.12 35.20 # STRATA1 tbl[, c(\"ARM\", \"A: Drug X\")] # Note: method with signature 'VTableTree#missing#ANY' chosen for function '[', # target signature 'TableTree#missing#character'. # \"VTableTree#ANY#character\" would also be valid # A: Drug X # ————————————————————— # F # AGE # Mean 32.76 # STRATA1 # A 21 # B 25 # C 33 # M # AGE # Mean 35.57 # STRATA1 # A 16 # B 21 # C 14"},{"path":"https://insightsengineering.github.io/rtables/articles/subsetting_tables.html","id":"dealing-with-titles-foot-notes-and-top-left-information","dir":"Articles","previous_headings":"Traditional Subsetting and modification with [","what":"Dealing with titles, foot notes, and top left information","title":"Subsetting and Manipulating Table Contents","text":"standard additional information kept subsetting. , show complete table still possible keep (possibly) relevant information. Normal subsetting loses information showed . rows kept, top left information also kept. can also imposed adding keep_topleft = TRUE subsetting follows: referenced entry present subsetting, also referential footnote appear. Please consider reading relevant vignette referential footnotes. case subsetting, referential footnotes default indexed , produced table new one. Similar used keep top left information, can specify keep information original table. standard foot notes always present titles kept.","code":"top_left(tbl) <- \"SEX\" main_title(tbl) <- \"Table 1\" subtitles(tbl) <- c(\"Authors:\", \" - Abcd Zabcd\", \" - Cde Zbcd\") main_footer(tbl) <- \"Please regard this table as an example of smart subsetting\" prov_footer(tbl) <- \"Do remember where you read this though\" fnotes_at_path(tbl, rowpath = c(\"M\", \"AGE\", \"Mean\"), colpath = c(\"ARM\", \"A: Drug X\")) <- \"Very important mean\" tbl[3, 3] # C: Combination # ————————————————————— # Mean 35.20 tbl[, 2:3] # SEX B: Placebo C: Combination # ——————————————————————————————————————— # F # AGE # Mean 34.12 35.20 # STRATA1 # A 24 18 # B 27 21 # C 26 27 # M # AGE # Mean 37.44 35.38 # STRATA1 # A 19 20 # B 17 21 # C 19 19 tbl[1:3, 3, keep_topleft = TRUE] # SEX C: Combination # ————————————————————————— # F # AGE # Mean 35.20 tbl[10, 1] # A: Drug X # ———————————————— # Mean 35.57 {1} # ———————————————— # # {1} - Very important mean # ———————————————— col_paths_summary(tbl) # Use these to find the right path to value or label # label path # ————————————————————————————————————— # A: Drug X ARM, A: Drug X # B: Placebo ARM, B: Placebo # C: Combination ARM, C: Combination row_paths_summary(tbl) # # rowname node_class path # ————————————————————————————————————————————— # F LabelRow SEX, F # AGE LabelRow SEX, F, AGE # Mean DataRow SEX, F, AGE, Mean # STRATA1 LabelRow SEX, F, STRATA1 # A DataRow SEX, F, STRATA1, A # B DataRow SEX, F, STRATA1, B # C DataRow SEX, F, STRATA1, C # M LabelRow SEX, M # AGE LabelRow SEX, M, AGE # Mean DataRow SEX, M, AGE, Mean # STRATA1 LabelRow SEX, M, STRATA1 # A DataRow SEX, M, STRATA1, A # B DataRow SEX, M, STRATA1, B # C DataRow SEX, M, STRATA1, C # To select column value, use `NULL` for `rowpath` fnotes_at_path(tbl, rowpath = NULL, colpath = c(\"ARM\", \"A: Drug X\")) <- \"Interesting\" tbl[3, 1] # A: Drug X {1} # ———————————————————— # Mean 32.76 # ———————————————————— # # {1} - Interesting # ———————————————————— # reindexing of {2} as {1} fnotes_at_path(tbl, rowpath = c(\"M\", \"AGE\", \"Mean\"), colpath = NULL) <- \"THIS mean\" tbl # {1}, {2}, and {3} are present # Table 1 # Authors: # - Abcd Zabcd # - Cde Zbcd # # —————————————————————————————————————————————————————————— # SEX A: Drug X {1} B: Placebo C: Combination # —————————————————————————————————————————————————————————— # F # AGE # Mean 32.76 34.12 35.20 # STRATA1 # A 21 24 18 # B 25 27 21 # C 33 26 27 # M # AGE # Mean {2} 35.57 {3} 37.44 35.38 # STRATA1 # A 16 19 20 # B 21 17 21 # C 14 19 19 # —————————————————————————————————————————————————————————— # # {1} - Interesting # {2} - THIS mean # {3} - Very important mean # —————————————————————————————————————————————————————————— # # Please regard this table as an example of smart subsetting # # Do remember where you read this though tbl[10, 2] # only {1} which was previously {2} # B: Placebo # ————————————————————— # Mean {1} 37.44 # ————————————————————— # # {1} - THIS mean # ————————————————————— tbl[1:3, 2:3, keep_titles = TRUE] # Table 1 # Authors: # - Abcd Zabcd # - Cde Zbcd # # —————————————————————————————————————— # B: Placebo C: Combination # —————————————————————————————————————— # F # AGE # Mean 34.12 35.20 # —————————————————————————————————————— # # Please regard this table as an example of smart subsetting # # Do remember where you read this though tbl[1:3, 2:3, keep_titles = FALSE, keep_footers = TRUE] # B: Placebo C: Combination # —————————————————————————————————————— # F # AGE # Mean 34.12 35.20 # —————————————————————————————————————— # # Please regard this table as an example of smart subsetting # # Do remember where you read this though # Referential footnotes are not influenced by `keep_footers = FALSE` tbl[1:3, keep_titles = TRUE, keep_footers = FALSE] # Table 1 # Authors: # - Abcd Zabcd # - Cde Zbcd # # —————————————————————————————————————————————————————— # A: Drug X {1} B: Placebo C: Combination # —————————————————————————————————————————————————————— # F # AGE # Mean 32.76 34.12 35.20 # —————————————————————————————————————————————————————— # # {1} - Interesting # ——————————————————————————————————————————————————————"},{"path":"https://insightsengineering.github.io/rtables/articles/subsetting_tables.html","id":"path-based-cell-value-accessing","dir":"Articles","previous_headings":"","what":"Path Based Cell Value Accessing:","title":"Subsetting and Manipulating Table Contents","text":"Tables can subset modified structurally aware manner via pathing. Paths define semantically meaningful positions within constructed table correspond logic layout used create . path ordered set split names, names subgroups generated split, @content directive, steps position’s content (row group summary) table. can see row column paths existing table via row_paths(), col_paths(), row_paths_summary(), col_paths_summary(), functions, portion general make_row_df() function output. column paths follows: row paths follows: get semantically meaningful subset table, , can use [ (tt_at_path() underlies ) can also retrieve individual cell-values via value_at() convenience function, takes pair row column paths resolve together individual cell, e.g. average age Asian female patients arm : can also request information non-cell specific paths cell_values() function: Note return value cell_values() always list even specify path cell:","code":"lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by(\"SEX\", split_fun = drop_split_levels) %>% split_rows_by(\"RACE\", split_fun = drop_split_levels) %>% summarize_row_groups() %>% analyze(c(\"AGE\", \"STRATA1\")) tbl2 <- build_table(lyt2, ex_adsl %>% filter(SEX %in% c(\"M\", \"F\") & RACE %in% (levels(RACE)[1:3]))) tbl2 # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN 41 (53.9%) 25 (54.3%) 36 (52.2%) 30 (60.0%) 39 (60.9%) 32 (57.1%) # AGE # Mean 31.22 34.60 35.06 38.63 36.44 37.66 # STRATA1 # A 11 10 14 10 11 7 # B 11 9 15 7 11 14 # C 19 6 7 13 17 11 # BLACK OR AFRICAN AMERICAN 18 (23.7%) 12 (26.1%) 16 (23.2%) 12 (24.0%) 14 (21.9%) 14 (25.0%) # AGE # Mean 34.06 34.58 33.88 36.33 33.21 34.21 # STRATA1 # A 5 2 5 6 3 7 # B 6 5 3 4 4 4 # C 7 5 8 2 7 3 # WHITE 17 (22.4%) 9 (19.6%) 17 (24.6%) 8 (16.0%) 11 (17.2%) 10 (17.9%) # AGE # Mean 34.12 40.00 32.41 34.62 33.00 30.80 # STRATA1 # A 5 3 3 3 3 5 # B 5 4 8 4 5 2 # C 7 2 6 1 3 3 col_paths_summary(tbl2) # label path # ————————————————————————————————————————————— # A: Drug X ARM, A: Drug X # F ARM, A: Drug X, SEX, F # M ARM, A: Drug X, SEX, M # B: Placebo ARM, B: Placebo # F ARM, B: Placebo, SEX, F # M ARM, B: Placebo, SEX, M # C: Combination ARM, C: Combination # F ARM, C: Combination, SEX, F # M ARM, C: Combination, SEX, M row_paths_summary(tbl2) # rowname node_class path # ——————————————————————————————————————————————————————————————————————————————————————————————————————————————— # ASIAN ContentRow RACE, ASIAN, @content, ASIAN # AGE LabelRow RACE, ASIAN, AGE # Mean DataRow RACE, ASIAN, AGE, Mean # STRATA1 LabelRow RACE, ASIAN, STRATA1 # A DataRow RACE, ASIAN, STRATA1, A # B DataRow RACE, ASIAN, STRATA1, B # C DataRow RACE, ASIAN, STRATA1, C # BLACK OR AFRICAN AMERICAN ContentRow RACE, BLACK OR AFRICAN AMERICAN, @content, BLACK OR AFRICAN AMERICAN # AGE LabelRow RACE, BLACK OR AFRICAN AMERICAN, AGE # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, AGE, Mean # STRATA1 LabelRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1 # A DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, A # B DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, B # C DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, C # WHITE ContentRow RACE, WHITE, @content, WHITE # AGE LabelRow RACE, WHITE, AGE # Mean DataRow RACE, WHITE, AGE, Mean # STRATA1 LabelRow RACE, WHITE, STRATA1 # A DataRow RACE, WHITE, STRATA1, A # B DataRow RACE, WHITE, STRATA1, B # C DataRow RACE, WHITE, STRATA1, C tbl2[c(\"RACE\", \"ASIAN\"), c(\"ARM\", \"C: Combination\")] # C: Combination # F M # ——————————————————————————————————— # ASIAN 39 (60.9%) 32 (57.1%) # AGE # Mean 36.44 37.66 # STRATA1 # A 11 7 # B 11 14 # C 17 11 value_at(tbl2, c(\"RACE\", \"ASIAN\", \"AGE\", \"Mean\"), c(\"ARM\", \"A: Drug X\", \"SEX\", \"F\")) # [1] 31.21951 cell_values(tbl2, c(\"RACE\", \"ASIAN\", \"AGE\", \"Mean\"), c(\"ARM\", \"A: Drug X\")) # $`A: Drug X.F` # [1] 31.21951 # # $`A: Drug X.M` # [1] 34.6 cell_values(tbl2, c(\"RACE\", \"ASIAN\", \"AGE\", \"Mean\"), c(\"ARM\", \"A: Drug X\", \"SEX\", \"F\")) # $`A: Drug X.F` # [1] 31.21951"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_concepts.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Tabulation Concepts","text":"vignette introduce theory behind using layouts table creation. Much theory also holds true using table packages. vignette use following packages: data use following, created random number generators:","code":"library(dplyr) library(tibble) library(rtables) add_subgroup <- function(x) paste0(tolower(x), sample(1:3, length(x), TRUE)) set.seed(1) df <- tibble( x = rnorm(100), c1 = factor(sample(c(\"A\", \"B\", \"C\"), 100, replace = TRUE), levels = c(\"A\", \"B\", \"C\")), r1 = factor(sample(c(\"U\", \"V\", \"W\"), 100, replace = TRUE), levels = c(\"U\", \"V\", \"W\")) ) %>% mutate( c2 = add_subgroup(c1), r2 = add_subgroup(r1), y = as.numeric(2 * as.numeric(c1) - 3 * as.numeric(r1)) ) %>% select(c1, c2, r1, r2, x, y) df # # A tibble: 100 × 6 # c1 c2 r1 r2 x y # # 1 B b2 U u3 -0.626 1 # 2 A a3 V v2 0.184 -4 # 3 B b1 V v2 -0.836 -2 # 4 B b3 V v2 1.60 -2 # 5 B b1 U u1 0.330 1 # 6 C c1 U u3 -0.820 3 # 7 A a3 U u3 0.487 -1 # 8 B b1 U u3 0.738 1 # 9 C c3 V v2 0.576 0 # 10 C c3 U u2 -0.305 3 # # ℹ 90 more rows"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_concepts.html","id":"building-a-table-row-by-row","dir":"Articles","previous_headings":"","what":"Building A Table Row By Row","title":"Tabulation Concepts","text":"Let’s look table 3 columns 3 rows. row represents different analysis (functions foo, bar, zoo return rcell() object): data passed analysis functions subset defined respective column : Let’s concrete data analyze(): wanted x variable instead data frame: : function passed afun evaluated using argument matching. afun argument x analysis variable specified vars analyze() passed function, afun argument df subset dataset passed afun: Note also possible function returns multiple rows in_rows(): recommend specify row names explicitly.","code":"A B C ------------------------------------------------ foo_label foo(df_A) foo(df_B) foo(df_C) bar_label bar(df_A) bar(df_B) bar(df_C) zoo_label zoo(df_A) zoo(df_B) zoo(df_C) df_A <- df %>% filter(c1 == \"A\") df_B <- df %>% filter(c1 == \"B\") df_C <- df %>% filter(c1 == \"C\") foo <- prod bar <- sum zoo <- mean lyt <- basic_table() %>% split_cols_by(\"c1\") %>% analyze(\"x\", function(df) foo(df$x), var_labels = \"foo label\", format = \"xx.xx\") %>% analyze(\"x\", function(df) bar(df$x), var_labels = \"bar label\", format = \"xx.xx\") %>% analyze(\"x\", function(df) zoo(df$x), var_labels = \"zoo label\", format = \"xx.xx\") tbl <- build_table(lyt, df) # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: root tbl # A B C # —————————————————————————————————— # foo label # foo label 0.00 -0.00 -0.00 # bar label # bar label 1.87 4.37 4.64 # zoo label # zoo label 0.05 0.13 0.18 A B C ------------------------------------------------ foo_label foo(x_A) foo(x_B) foo(x_C) bar_label bar(x_A) bar(x_B) bar(x_C) zoo_label zoo(x_A) zoo(x_B) zoo(x_C) x_A <- df_A$x x_B <- df_B$x x_C <- df_C$x lyt2 <- basic_table() %>% split_cols_by(\"c1\") %>% analyze(\"x\", foo, var_labels = \"foo label\", format = \"xx.xx\") %>% analyze(\"x\", bar, var_labels = \"bar label\", format = \"xx.xx\") %>% analyze(\"x\", zoo, var_labels = \"zoo label\", format = \"xx.xx\") tbl2 <- build_table(lyt2, df) # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: root tbl2 # A B C # ———————————————————————————————— # foo label # foo 0.00 -0.00 -0.00 # bar label # bar 1.87 4.37 4.64 # zoo label # zoo 0.05 0.13 0.18 lyt3 <- basic_table() %>% split_cols_by(\"c1\") %>% analyze(\"x\", function(x) { in_rows( \"row 1\" = rcell(mean(x), format = \"xx.xx\"), \"row 2\" = rcell(sd(x), format = \"xx.xxx\") ) }, var_labels = \"foo label\") %>% analyze(\"x\", function(x) { in_rows( \"more rows 1\" = rcell(median(x), format = \"xx.x\"), \"even more rows 1\" = rcell(IQR(x), format = \"xx.xx\") ) }, var_labels = \"bar label\", format = \"xx.xx\") tbl3 <- build_table(lyt3, df) # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: root tbl3 # A B C # —————————————————————————————————————————— # foo label # row 1 0.05 0.13 0.18 # row 2 0.985 0.815 0.890 # bar label # more rows 1 -0.0 0.2 0.3 # even more rows 1 1.20 1.15 1.16"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_concepts.html","id":"tabulation-with-row-structure","dir":"Articles","previous_headings":"","what":"Tabulation With Row Structure","title":"Tabulation Concepts","text":"Let’s say like create following table: df_* subsets df follows: note df_* class df, .e. tibbles. Hence foo aggregates subset data cell value. Given function foo (ignore ... now): can start calculating cell values individually: Now still missing table structure: rtables type tabulation done layouts: want see foo label use: now row labels disappeared. cfun needs define row label. let’s redefine foo:","code":"A B C -------------------------------------- U foo(df_UA) foo(df_UB) foo(df_UC) V foo(df_VA) foo(df_VB) foo(df_VC) W foo(df_WA) foo(df_WB) foo(df_WC) df_UA <- df %>% filter(r1 == \"U\", c1 == \"A\") df_VA <- df %>% filter(r1 == \"V\", c1 == \"A\") df_WA <- df %>% filter(r1 == \"W\", c1 == \"A\") df_UB <- df %>% filter(r1 == \"U\", c1 == \"B\") df_VB <- df %>% filter(r1 == \"V\", c1 == \"B\") df_WB <- df %>% filter(r1 == \"W\", c1 == \"C\") df_UC <- df %>% filter(r1 == \"U\", c1 == \"C\") df_VC <- df %>% filter(r1 == \"V\", c1 == \"C\") df_WC <- df %>% filter(r1 == \"W\", c1 == \"C\") foo <- function(df, labelstr = \"\", ...) { paste(dim(df), collapse = \" x \") } foo(df_UA) # [1] \"17 x 6\" foo(df_VA) # [1] \"9 x 6\" foo(df_WA) # [1] \"14 x 6\" foo(df_UB) # [1] \"13 x 6\" foo(df_VB) # [1] \"15 x 6\" foo(df_WB) # [1] \"11 x 6\" foo(df_UC) # [1] \"10 x 6\" foo(df_VC) # [1] \"5 x 6\" foo(df_WC) # [1] \"11 x 6\" matrix( list( foo(df_UA), foo(df_VA), foo(df_WA), foo(df_UB), foo(df_VB), foo(df_WB), foo(df_UC), foo(df_VC), foo(df_WC) ), byrow = FALSE, ncol = 3 ) # [,1] [,2] [,3] # [1,] \"17 x 6\" \"13 x 6\" \"10 x 6\" # [2,] \"9 x 6\" \"15 x 6\" \"5 x 6\" # [3,] \"14 x 6\" \"11 x 6\" \"11 x 6\" lyt4 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% analyze(\"x\", foo) tbl4 <- build_table(lyt4, df) tbl4 # A B C # ———————————————————————————————— # U # foo 17 x 6 13 x 6 10 x 6 # V # foo 9 x 6 15 x 6 5 x 6 # W # foo 14 x 6 6 x 6 11 x 6 lyt5 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% summarize_row_groups(cfun = foo, format = \"xx\") tbl5 <- build_table(lyt5, df) tbl5 # A B C # ——————————————————————————— # 17 x 6 13 x 6 10 x 6 # 9 x 6 15 x 6 5 x 6 # 14 x 6 6 x 6 11 x 6 foo <- function(df, labelstr) { rcell(paste(dim(df), collapse = \" x \"), format = \"xx\", label = labelstr) } lyt6 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% summarize_row_groups(cfun = foo) tbl6 <- build_table(lyt6, df) tbl6 # A B C # ———————————————————————————— # U 17 x 6 13 x 6 10 x 6 # V 9 x 6 15 x 6 5 x 6 # W 14 x 6 6 x 6 11 x 6"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_concepts.html","id":"calculating-the-mean","dir":"Articles","previous_headings":"Tabulation With Row Structure","what":"Calculating the Mean","title":"Tabulation Concepts","text":"Now let’s calculate mean df$y pattern : Note foo variable information hard-encoded function body. Let’s try alternatives returning analyze(): Note subset y variable passed x argument mean(). also get data.frame instead variable: contrast : function receives subset y.","code":"foo <- function(df, labelstr) { rcell(mean(df$y), label = labelstr, format = \"xx.xx\") } lyt7 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% summarize_row_groups(cfun = foo) tbl7 <- build_table(lyt7, df) tbl7 # A B C # ————————————————————————— # U -1.00 1.00 3.00 # V -4.00 -2.00 0.00 # W -7.00 -5.00 -3.00 lyt8 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% analyze(\"y\", afun = mean) tbl8 <- build_table(lyt8, df) tbl8 # A B C # ————————————————————— # U # mean -1 1 3 # V # mean -4 -2 0 # W # mean -7 -5 -3 lyt9 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% analyze(\"y\", afun = function(df) mean(df$y)) tbl9 <- build_table(lyt9, df) tbl9 # A B C # —————————————————— # U # y -1 1 3 # V # y -4 -2 0 # W # y -7 -5 -3 lyt10 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% analyze(\"y\", afun = function(x) mean(x)) tbl10 <- build_table(lyt10, df) tbl10 # A B C # —————————————————— # U # y -1 1 3 # V # y -4 -2 0 # W # y -7 -5 -3"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_concepts.html","id":"group-summaries","dir":"Articles","previous_headings":"Tabulation With Row Structure","what":"Group Summaries","title":"Tabulation Concepts","text":"Pattern interesting one can add row structure (splits). Consider following table: <> represents data represented cell. cell U > u1, subset: . can get table follows: , wanted calculate two summaries per row split: following structure: rows U, u1, u2, …, W, w1, w2, w3 label rows rows (mean_sd range) data rows. Currently content rows table. Content rows summarize data defined splitting (.e. V > v1, B). wanted add content rows r2 split level get: s_cfun_2 content function either returns one row via rcell() multiple rows via in_rows(). data represented <> content rows data ’s descendant, .e. U > u1, content row cell df %>% filter(r1 == \"U\", r2 == \"u1\", c1 == \"\"). Note content functions cfun operate data frames vectors/variables must take df argument. , cfun must also labelstr argument split level. way, cfun can define row name. order get table can use layout framework follows: manner, want content rows r1 split can follows: pagination, content rows label rows get repeated page split descendant content row. , example, split following table ***: get following two tables: ","code":"A B C -------------------------------------- U u1 foo(<>) foo(<>) foo(<>) u2 foo(<>) foo(<>) foo(<>) u3 foo(<>) foo(<>) foo(<>) V v1 foo(<>) foo(<>) foo(<>) v2 foo(<>) foo(<>) foo(<>) v3 foo(<>) foo(<>) foo(<>) W w1 foo(<>) foo(<>) foo(<>) w2 foo(<>) foo(<>) foo(<>) w3 foo(<>) foo(<>) foo(<>) df %>% filter(r1 == \"U\", r2 == \"u1\", c1 == \"A\") # # A tibble: 2 × 6 # c1 c2 r1 r2 x y # # 1 A a2 U u1 1.12 -1 # 2 A a1 U u1 0.594 -1 lyt11 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% split_rows_by(\"r2\") %>% summarize_row_groups(cfun = function(df, labelstr) { rcell(mean(df$x), format = \"xx.xx\", label = paste(\"mean x for\", labelstr)) }) tbl11 <- build_table(lyt11, df) tbl11 # A B C # ——————————————————————————————————————— # U # mean x for u3 -0.04 0.36 -0.25 # mean x for u1 0.86 0.32 NA # mean x for u2 -0.28 0.38 0.08 # V # mean x for v2 0.01 0.55 0.60 # mean x for v3 -0.03 -0.30 1.06 # mean x for v1 0.56 -0.27 -0.54 # W # mean x for w1 -0.58 0.42 0.67 # mean x for w3 0.56 0.69 -0.39 # mean x for w2 -1.99 -0.10 0.53 s_mean_sd <- function(x) { in_rows(\"mean (sd)\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\")) } s_range <- function(x) { in_rows(\"range\" = rcell(range(x), format = \"xx.xx - xx.xx\")) } lyt12 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% split_rows_by(\"r2\") %>% analyze(\"x\", s_mean_sd, show_labels = \"hidden\") %>% analyze(\"x\", s_range, show_labels = \"hidden\") tbl12 <- build_table(lyt12, df) # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u3] # Warning in min(x): no non-missing arguments to min; returning Inf # Warning in max(x): no non-missing arguments to max; returning -Inf # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u2] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v2] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v3] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w3] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w2] tbl12 # A B C # ——————————————————————————————————————————————————————————— # U # u3 # mean (sd) -0.04 (1.18) 0.36 (1.41) -0.25 (0.72) # range -1.80 - 1.47 -1.28 - 2.40 -0.82 - 0.56 # u1 # mean (sd) 0.86 (0.38) 0.32 (0.51) NA # range 0.59 - 1.12 -0.48 - 0.94 Inf - -Inf # u2 # mean (sd) -0.28 (0.96) 0.38 (0.67) 0.08 (0.91) # range -1.52 - 1.43 -0.39 - 0.82 -0.93 - 1.51 # V # v2 # mean (sd) 0.01 (0.25) 0.55 (1.14) 0.60 (0.03) # range -0.16 - 0.18 -0.84 - 1.60 0.58 - 0.62 # v3 # mean (sd) -0.03 (0.37) -0.30 (0.36) 1.06 (NA) # range -0.41 - 0.33 -0.62 - 0.03 1.06 - 1.06 # v1 # mean (sd) 0.56 (1.10) -0.27 (0.73) -0.54 (1.18) # range -0.16 - 2.17 -1.22 - 0.59 -1.38 - 0.29 # W # w1 # mean (sd) -0.58 (0.85) 0.42 (NA) 0.67 (0.39) # range -1.25 - 0.61 0.42 - 0.42 0.37 - 1.21 # w3 # mean (sd) 0.56 (0.85) 0.69 (NA) -0.39 (1.68) # range -0.71 - 1.98 0.69 - 0.69 -2.21 - 1.10 # w2 # mean (sd) -1.99 (NA) -0.10 (0.47) 0.53 (0.60) # range -1.99 - -1.99 -0.61 - 0.39 -0.10 - 1.16 A B C --------------------------------------------------------- U u1 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) u2 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) u3 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) V v1 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) v2 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) v3 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) W w1 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) w2 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) w3 mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) A B C --------------------------------------------------------- U u1 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) u2 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) u3 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) V v1 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) v2 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) v3 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) W w1 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) w2 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) w3 s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) s_mean_sd <- function(x) { in_rows(\"mean (sd)\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\")) } s_range <- function(x) { in_rows(\"range\" = rcell(range(x), format = \"xx.xx - xx.xx\")) } s_cfun_2 <- function(df, labelstr) { rcell(nrow(df), format = \"xx\", label = paste(labelstr, \"(n)\")) } lyt13 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% split_rows_by(\"r2\") %>% summarize_row_groups(cfun = s_cfun_2) %>% analyze(\"x\", s_mean_sd, show_labels = \"hidden\") %>% analyze(\"x\", s_range, show_labels = \"hidden\") tbl13 <- build_table(lyt13, df) # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u3] # Warning in min(x): no non-missing arguments to min; returning Inf # Warning in max(x): no non-missing arguments to max; returning -Inf # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u2] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v2] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v3] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w3] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w2] tbl13 # A B C # ——————————————————————————————————————————————————————————— # U # u3 (n) 6 5 3 # mean (sd) -0.04 (1.18) 0.36 (1.41) -0.25 (0.72) # range -1.80 - 1.47 -1.28 - 2.40 -0.82 - 0.56 # u1 (n) 2 5 0 # mean (sd) 0.86 (0.38) 0.32 (0.51) NA # range 0.59 - 1.12 -0.48 - 0.94 Inf - -Inf # u2 (n) 9 3 7 # mean (sd) -0.28 (0.96) 0.38 (0.67) 0.08 (0.91) # range -1.52 - 1.43 -0.39 - 0.82 -0.93 - 1.51 # V # v2 (n) 2 4 2 # mean (sd) 0.01 (0.25) 0.55 (1.14) 0.60 (0.03) # range -0.16 - 0.18 -0.84 - 1.60 0.58 - 0.62 # v3 (n) 3 4 1 # mean (sd) -0.03 (0.37) -0.30 (0.36) 1.06 (NA) # range -0.41 - 0.33 -0.62 - 0.03 1.06 - 1.06 # v1 (n) 4 7 2 # mean (sd) 0.56 (1.10) -0.27 (0.73) -0.54 (1.18) # range -0.16 - 2.17 -1.22 - 0.59 -1.38 - 0.29 # W # w1 (n) 4 1 4 # mean (sd) -0.58 (0.85) 0.42 (NA) 0.67 (0.39) # range -1.25 - 0.61 0.42 - 0.42 0.37 - 1.21 # w3 (n) 9 1 3 # mean (sd) 0.56 (0.85) 0.69 (NA) -0.39 (1.68) # range -0.71 - 1.98 0.69 - 0.69 -2.21 - 1.10 # w2 (n) 1 4 4 # mean (sd) -1.99 (NA) -0.10 (0.47) 0.53 (0.60) # range -1.99 - -1.99 -0.61 - 0.39 -0.10 - 1.16 lyt14 <- basic_table() %>% split_cols_by(\"c1\") %>% split_rows_by(\"r1\") %>% summarize_row_groups(cfun = s_cfun_2) %>% split_rows_by(\"r2\") %>% summarize_row_groups(cfun = s_cfun_2) %>% analyze(\"x\", s_mean_sd, show_labels = \"hidden\") %>% analyze(\"x\", s_range, show_labels = \"hidden\") tbl14 <- build_table(lyt14, df) # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u3] # Warning in min(x): no non-missing arguments to min; returning Inf # Warning in max(x): no non-missing arguments to max; returning -Inf # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[U]->r2[u2] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v2] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v3] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[V]->r2[v1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w1] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w3] # Warning: Non-unique sibling analysis table names. Using Labels instead. Use the table_names argument to analyze to avoid this when analyzing the same variable multiple times. # occured at (row) path: r1[W]->r2[w2] tbl14 # A B C # ——————————————————————————————————————————————————————————— # U (n) 17 13 10 # u3 (n) 6 5 3 # mean (sd) -0.04 (1.18) 0.36 (1.41) -0.25 (0.72) # range -1.80 - 1.47 -1.28 - 2.40 -0.82 - 0.56 # u1 (n) 2 5 0 # mean (sd) 0.86 (0.38) 0.32 (0.51) NA # range 0.59 - 1.12 -0.48 - 0.94 Inf - -Inf # u2 (n) 9 3 7 # mean (sd) -0.28 (0.96) 0.38 (0.67) 0.08 (0.91) # range -1.52 - 1.43 -0.39 - 0.82 -0.93 - 1.51 # V (n) 9 15 5 # v2 (n) 2 4 2 # mean (sd) 0.01 (0.25) 0.55 (1.14) 0.60 (0.03) # range -0.16 - 0.18 -0.84 - 1.60 0.58 - 0.62 # v3 (n) 3 4 1 # mean (sd) -0.03 (0.37) -0.30 (0.36) 1.06 (NA) # range -0.41 - 0.33 -0.62 - 0.03 1.06 - 1.06 # v1 (n) 4 7 2 # mean (sd) 0.56 (1.10) -0.27 (0.73) -0.54 (1.18) # range -0.16 - 2.17 -1.22 - 0.59 -1.38 - 0.29 # W (n) 14 6 11 # w1 (n) 4 1 4 # mean (sd) -0.58 (0.85) 0.42 (NA) 0.67 (0.39) # range -1.25 - 0.61 0.42 - 0.42 0.37 - 1.21 # w3 (n) 9 1 3 # mean (sd) 0.56 (0.85) 0.69 (NA) -0.39 (1.68) # range -0.71 - 1.98 0.69 - 0.69 -2.21 - 1.10 # w2 (n) 1 4 4 # mean (sd) -1.99 (NA) -0.10 (0.47) 0.53 (0.60) # range -1.99 - -1.99 -0.61 - 0.39 -0.10 - 1.16 A B C --------------------------------------------------------- U u1 (n) s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) *** range s_range(<>) s_range(<>) s_range(<>) u2 (n) s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>) A B C --------------------------------------------------------- U u1 (n) s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) A B C --------------------------------------------------------- U u1 (n) s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) range s_range(<>) s_range(<>) s_range(<>) u2 (n) s_cfun_2(<>) s_cfun_2(<>) s_cfun_2(<>) mean_sd s_mean_sd(<>) s_mean_sd(<>) s_mean_sd(<>) range s_range(<>) s_range(<>) s_range(<>)"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_concepts.html","id":"pattern-iii","dir":"Articles","previous_headings":"Tabulation With Row Structure","what":"Pattern III","title":"Tabulation Concepts","text":"Let’s consider following tabulation pattern: discuss future release rtables.","code":"A B C ------------------------------------------------ label 1 foo(x_A) bar(x_B) zoo(x_C) label 2 foo(x_A) bar(x_B) zoo(x_C) label 3 foo(x_A) bar(x_B) zoo(x_C)"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_dplyr.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"Comparison with dplyr Tabulation","text":"vignette, like discuss similarities differences dplyr rtable. Much rtables framework focuses tabulation/summarizing data visualization table. vignette, focus summarizing data using dplyr contrast rtables. won’t pay attention table visualization/markup just derive cell content. Using dplyr summarize data gt visualize table good way tabulation certain nature complexity. However, tables table created introduction vignette take effort create dplyr. Part effort due fact using dplyr table data stored data.frames tibbles natural way represent table show vignette. know elegant way deriving table content dplyr please let us know update vignette. table data used introduction vignette:","code":"library(rtables) library(dplyr) n <- 400 set.seed(1) df <- tibble( arm = factor(sample(c(\"Arm A\", \"Arm B\"), n, replace = TRUE), levels = c(\"Arm A\", \"Arm B\")), country = factor(sample(c(\"CAN\", \"USA\"), n, replace = TRUE, prob = c(.55, .45)), levels = c(\"CAN\", \"USA\")), gender = factor(sample(c(\"Female\", \"Male\"), n, replace = TRUE), levels = c(\"Female\", \"Male\")), handed = factor(sample(c(\"Left\", \"Right\"), n, prob = c(.6, .4), replace = TRUE), levels = c(\"Left\", \"Right\")), age = rchisq(n, 30) + 10 ) %>% mutate( weight = 35 * rnorm(n, sd = .5) + ifelse(gender == \"Female\", 140, 180) ) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% summarize_row_groups() %>% split_rows_by(\"handed\") %>% summarize_row_groups() %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # (N=96) (N=105) (N=92) (N=107) # ———————————————————————————————————————————————————————————— # CAN 45 (46.9%) 64 (61.0%) 46 (50.0%) 62 (57.9%) # Left 32 (33.3%) 42 (40.0%) 26 (28.3%) 37 (34.6%) # mean 38.9 40.4 40.3 37.7 # Right 13 (13.5%) 22 (21.0%) 20 (21.7%) 25 (23.4%) # mean 36.6 40.2 40.2 40.6 # USA 51 (53.1%) 41 (39.0%) 46 (50.0%) 45 (42.1%) # Left 34 (35.4%) 19 (18.1%) 25 (27.2%) 25 (23.4%) # mean 40.4 39.7 39.2 40.1 # Right 17 (17.7%) 22 (21.0%) 21 (22.8%) 20 (18.7%) # mean 36.9 39.8 38.5 39.0"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_dplyr.html","id":"getting-started","dir":"Articles","previous_headings":"","what":"Getting Started","title":"Comparison with dplyr Tabulation","text":"start deriving first data cell row 3 (note, row 1 2 content cells, see introduction vignette). Cell 3,1 contains mean age left handed & female Canadians “Arm ”: dplyr: , dplyr gives us verbs easily get average age left handed Canadians group defined 4 columns: can get average age cell values : rtable syntax, need following code get content: mentioned introduction vignette, please ignore difference arranging formatting data: ’s possible condense rtable possible make tibble look like reference table using gt R package. terms tabulation example arguably much added rtables dplyr.","code":"mean(df$age[df$country == \"CAN\" & df$arm == \"Arm A\" & df$gender == \"Female\" & df$handed == \"Left\"]) # [1] 38.86979 df %>% filter(country == \"CAN\", arm == \"Arm A\", gender == \"Female\", handed == \"Left\") %>% summarise(mean_age = mean(age)) # # A tibble: 1 × 1 # mean_age # # 1 38.9 df %>% group_by(arm, gender) %>% filter(country == \"CAN\", handed == \"Left\") %>% summarise(mean_age = mean(age)) # `summarise()` has grouped output by 'arm'. You can override using the `.groups` # argument. # # A tibble: 4 × 3 # # Groups: arm [2] # arm gender mean_age # # 1 Arm A Female 38.9 # 2 Arm A Male 40.4 # 3 Arm B Female 40.3 # 4 Arm B Male 37.7 average_age <- df %>% group_by(arm, gender, country, handed) %>% summarise(mean_age = mean(age)) # `summarise()` has grouped output by 'arm', 'gender', 'country'. You can # override using the `.groups` argument. average_age # # A tibble: 16 × 5 # # Groups: arm, gender, country [8] # arm gender country handed mean_age # # 1 Arm A Female CAN Left 38.9 # 2 Arm A Female CAN Right 36.6 # 3 Arm A Female USA Left 40.4 # 4 Arm A Female USA Right 36.9 # 5 Arm A Male CAN Left 40.4 # 6 Arm A Male CAN Right 40.2 # 7 Arm A Male USA Left 39.7 # 8 Arm A Male USA Right 39.8 # 9 Arm B Female CAN Left 40.3 # 10 Arm B Female CAN Right 40.2 # 11 Arm B Female USA Left 39.2 # 12 Arm B Female USA Right 38.5 # 13 Arm B Male CAN Left 37.7 # 14 Arm B Male CAN Right 40.6 # 15 Arm B Male USA Left 40.1 # 16 Arm B Male USA Right 39.0 lyt <- basic_table() %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% split_rows_by(\"handed\") %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # ———————————————————————————————————————— # CAN # Left # mean 38.9 40.4 40.3 37.7 # Right # mean 36.6 40.2 40.2 40.6 # USA # Left # mean 40.4 39.7 39.2 40.1 # Right # mean 36.9 39.8 38.5 39.0"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_dplyr.html","id":"content-information","dir":"Articles","previous_headings":"","what":"Content Information","title":"Comparison with dplyr Tabulation","text":"Unlike rtables different levels summarization discrete computations dplyr need combine first focus count percentage information handedness within country (arm-gender pair), along analysis row mean values: 16 rows (cells) like average_age data frame defined . Next, derive group information countries: Finally, left_join() two levels summary get data.frame containing full set values make body table (note, however, order): Alternatively, calculate counts c_h_df, use mutate() left_join() divide counts n_col values naturally calculated within c_df. simplify c_h_df’s creation somewhat requiring explicit ungroup(), prevents level summarization self-contained set computations. rtables call contrast : can now spot check values , rtable syntax hopefully also become bit straightforward derive cell values dplyr particular table.","code":"c_h_df <- df %>% group_by(arm, gender, country, handed) %>% summarize(mean = mean(age), c_h_count = n()) %>% ## we need the sum below to *not* be by country, so that we're dividing by the column counts ungroup(country) %>% # now the `handed` grouping has been removed, therefore we can calculate percent now: mutate(n_col = sum(c_h_count), c_h_percent = c_h_count / n_col) # `summarise()` has grouped output by 'arm', 'gender', 'country'. You can # override using the `.groups` argument. c_h_df # # A tibble: 16 × 8 # # Groups: arm, gender [4] # arm gender country handed mean c_h_count n_col c_h_percent # # 1 Arm A Female CAN Left 38.9 32 96 0.333 # 2 Arm A Female CAN Right 36.6 13 96 0.135 # 3 Arm A Female USA Left 40.4 34 96 0.354 # 4 Arm A Female USA Right 36.9 17 96 0.177 # 5 Arm A Male CAN Left 40.4 42 105 0.4 # 6 Arm A Male CAN Right 40.2 22 105 0.210 # 7 Arm A Male USA Left 39.7 19 105 0.181 # 8 Arm A Male USA Right 39.8 22 105 0.210 # 9 Arm B Female CAN Left 40.3 26 92 0.283 # 10 Arm B Female CAN Right 40.2 20 92 0.217 # 11 Arm B Female USA Left 39.2 25 92 0.272 # 12 Arm B Female USA Right 38.5 21 92 0.228 # 13 Arm B Male CAN Left 37.7 37 107 0.346 # 14 Arm B Male CAN Right 40.6 25 107 0.234 # 15 Arm B Male USA Left 40.1 25 107 0.234 # 16 Arm B Male USA Right 39.0 20 107 0.187 c_df <- df %>% group_by(arm, gender, country) %>% summarize(c_count = n()) %>% # now the `handed` grouping has been removed, therefore we can calculate percent now: mutate(n_col = sum(c_count), c_percent = c_count / n_col) # `summarise()` has grouped output by 'arm', 'gender'. You can override using the # `.groups` argument. c_df # # A tibble: 8 × 6 # # Groups: arm, gender [4] # arm gender country c_count n_col c_percent # # 1 Arm A Female CAN 45 96 0.469 # 2 Arm A Female USA 51 96 0.531 # 3 Arm A Male CAN 64 105 0.610 # 4 Arm A Male USA 41 105 0.390 # 5 Arm B Female CAN 46 92 0.5 # 6 Arm B Female USA 46 92 0.5 # 7 Arm B Male CAN 62 107 0.579 # 8 Arm B Male USA 45 107 0.421 full_dplyr <- left_join(c_h_df, c_df) %>% ungroup() # Joining with `by = join_by(arm, gender, country, n_col)` lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"arm\") %>% split_cols_by(\"gender\") %>% split_rows_by(\"country\") %>% summarize_row_groups() %>% split_rows_by(\"handed\") %>% summarize_row_groups() %>% analyze(\"age\", afun = mean, format = \"xx.x\") tbl <- build_table(lyt, df) tbl # Arm A Arm B # Female Male Female Male # (N=96) (N=105) (N=92) (N=107) # ———————————————————————————————————————————————————————————— # CAN 45 (46.9%) 64 (61.0%) 46 (50.0%) 62 (57.9%) # Left 32 (33.3%) 42 (40.0%) 26 (28.3%) 37 (34.6%) # mean 38.9 40.4 40.3 37.7 # Right 13 (13.5%) 22 (21.0%) 20 (21.7%) 25 (23.4%) # mean 36.6 40.2 40.2 40.6 # USA 51 (53.1%) 41 (39.0%) 46 (50.0%) 45 (42.1%) # Left 34 (35.4%) 19 (18.1%) 25 (27.2%) 25 (23.4%) # mean 40.4 39.7 39.2 40.1 # Right 17 (17.7%) 22 (21.0%) 21 (22.8%) 20 (18.7%) # mean 36.9 39.8 38.5 39.0 frm_rtables_h <- cell_values( tbl, rowpath = c(\"country\", \"CAN\", \"handed\", \"Right\", \"@content\"), colpath = c(\"arm\", \"Arm B\", \"gender\", \"Female\") )[[1]] frm_rtables_h # [1] 20.0000000 0.2173913 frm_dplyr_h <- full_dplyr %>% filter(country == \"CAN\" & handed == \"Right\" & arm == \"Arm B\" & gender == \"Female\") %>% select(c_h_count, c_h_percent) frm_dplyr_h # # A tibble: 1 × 2 # c_h_count c_h_percent # # 1 20 0.217 frm_rtables_c <- cell_values( tbl, rowpath = c(\"country\", \"CAN\", \"@content\"), colpath = c(\"arm\", \"Arm A\", \"gender\", \"Male\") )[[1]] frm_rtables_c # [1] 64.0000000 0.6095238 frm_dplyr_c <- full_dplyr %>% filter(country == \"CAN\" & arm == \"Arm A\" & gender == \"Male\") %>% select(c_count, c_percent) frm_dplyr_c # # A tibble: 2 × 2 # c_count c_percent # # 1 64 0.610 # 2 64 0.610"},{"path":"https://insightsengineering.github.io/rtables/articles/tabulation_dplyr.html","id":"summary","dir":"Articles","previous_headings":"","what":"Summary","title":"Comparison with dplyr Tabulation","text":"vignette learned : dplyr keeps simple things simple tables group summaries repeating information required rtables streamlines construction complex tables recommend continue reading clinical_trials vignette create number advanced tables using layouts.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/title_footer.html","id":"titles-and-non-referential-footer-materials","dir":"Articles","previous_headings":"","what":"Titles and Non-Referential Footer Materials","title":"Titles, Footers, and Referential Footnotes","text":"rtables table can annotated three types header (title) information, well three types footer information. Header information comes two forms specified directly (main title subtitles), well one populated automatically necessary (page title, see next section). Similarly, footer materials come two directly specified components: main footer provenance footer, addition one computed necessary: referential footnotes. basic_table() accepts values static title footer element layout construction:","code":"library(rtables) library(dplyr) lyt <- basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", split_fun = drop_split_levels) %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\", mean, format = \"xx.x\") tbl <- build_table(lyt, DM) cat(export_as_txt(tbl, paginate = TRUE, page_break = \"\\n\\n\\n\")) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # F # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 # C # mean 35.2 36.0 34.3 # M # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 # C # mean 37.4 32.8 32.8 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a"},{"path":"https://insightsengineering.github.io/rtables/articles/title_footer.html","id":"page-by-splitting","dir":"Articles","previous_headings":"","what":"Page-by splitting","title":"Titles, Footers, and Referential Footnotes","text":"often want split tables based values one variables (e.g., lab measurement) paginate separately within table subsections. rtables via page row splits. Row splits can declared page splits setting page_by = TRUE split_rows_by*() call, . page splits present, page titles generated automatically appending split value (typically factor level, though need ), page_prefix, separated :. default, page_prefix name variable split. Page row splits can nested, within page_by splits, nested within traditional row splits. case, page title page split present every resulting page, seen :","code":"lyt2 <- basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", page_by = TRUE, page_prefix = \"Patient Subset - Gender\", split_fun = drop_split_levels) %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\", mean, format = \"xx.x\") tbl2 <- build_table(lyt2, DM) cat(export_as_txt(tbl2, paginate = TRUE, page_break = \"\\n\\n~~~~ Page Break ~~~~\\n\\n\")) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: F # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 # C # mean 35.2 36.0 34.3 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # ~~~~ Page Break ~~~~ # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: M # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 # C # mean 37.4 32.8 32.8 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a lyt3 <- basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", page_by = TRUE, page_prefix = \"Patient Subset - Gender\", split_fun = drop_split_levels) %>% split_rows_by(\"STRATA1\", page_by = TRUE, page_prefix = \"Stratification - Strata\") %>% analyze(\"AGE\", mean, format = \"xx.x\") tbl3 <- build_table(lyt3, DM) cat(export_as_txt(tbl3, paginate = TRUE, page_break = \"\\n\\n~~~~ Page Break ~~~~\\n\\n\")) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: F # Stratification - Strata: A # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # mean 30.9 32.9 36.0 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # ~~~~ Page Break ~~~~ # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: F # Stratification - Strata: B # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # mean 34.9 32.9 34.4 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # ~~~~ Page Break ~~~~ # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: F # Stratification - Strata: C # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # mean 35.2 36.0 34.3 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # ~~~~ Page Break ~~~~ # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: M # Stratification - Strata: A # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # mean 35.1 31.1 35.6 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # ~~~~ Page Break ~~~~ # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: M # Stratification - Strata: B # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # mean 36.6 32.1 34.4 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # ~~~~ Page Break ~~~~ # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: M # Stratification - Strata: C # # —————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————— # mean 37.4 32.8 32.8 # —————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a"},{"path":"https://insightsengineering.github.io/rtables/articles/title_footer.html","id":"referential-footnotes","dir":"Articles","previous_headings":"","what":"Referential Footnotes","title":"Titles, Footers, and Referential Footnotes","text":"Referential footnotes footnotes associated particular component table: column, row, cell. can added tabulation via analysis functions, can also added post-hoc table created. rendered number within curly braces within table body, row, column labels, followed message associated number printed table rendering.","code":""},{"path":"https://insightsengineering.github.io/rtables/articles/title_footer.html","id":"adding-cell--and-analysis-row-referential-footnotes-at-tabulation-time","dir":"Articles","previous_headings":"Referential Footnotes","what":"Adding Cell- and Analysis-row Referential Footnotes At Tabulation Time","title":"Titles, Footers, and Referential Footnotes","text":"note typically type footnote added within analysis function dependent computations done calculate cell value(s), e.g., model converging. simply use context information illustrative proxy . procedure adding footnotes content (summary row) rows cells identical , done within content function.","code":"afun <- function(df, .var, .spl_context) { val <- .spl_context$value[NROW(.spl_context)] rw_fnotes <- if (val == \"C\") list(\"This is strata level C for these patients\") else list() cl_fnotes <- if (val == \"B\" && df[1, \"ARM\", drop = TRUE] == \"C: Combination\") { list(\"these Strata B patients got the drug combination\") } else { list() } in_rows( mean = mean(df[[.var]]), .row_footnotes = rw_fnotes, .cell_footnotes = cl_fnotes, .formats = c(mean = \"xx.x\") ) } lyt <- basic_table( title = \"Study XXXXXXXX\", subtitles = c(\"subtitle YYYYYYYYYY\", \"subtitle2 ZZZZZZZZZ\"), main_footer = \"Analysis was done using cool methods that are correct\", prov_footer = \"file: /path/to/stuff/that/lives/there HASH:1ac41b242a\" ) %>% split_cols_by(\"ARM\") %>% split_rows_by(\"SEX\", page_by = TRUE, page_prefix = \"Patient Subset - Gender\", split_fun = drop_split_levels) %>% split_rows_by(\"STRATA1\") %>% analyze(\"AGE\", afun, format = \"xx.x\") tbl <- build_table(lyt, DM) cat(export_as_txt(tbl, paginate = TRUE, page_break = \"\\n\\n\\n\")) # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: F # # —————————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————— # A # mean 30.9 32.9 36.0 # B # mean 34.9 32.9 34.4 {1} # C # mean {2} 35.2 36.0 34.3 # —————————————————————————————————————————————————————— # # {1} - these Strata B patients got the drug combination # {2} - This is strata level C for these patients # —————————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a # # # # Study XXXXXXXX # subtitle YYYYYYYYYY # subtitle2 ZZZZZZZZZ # Patient Subset - Gender: M # # —————————————————————————————————————————————————————— # A: Drug X B: Placebo C: Combination # —————————————————————————————————————————————————————— # A # mean 35.1 31.1 35.6 # B # mean 36.6 32.1 34.4 {1} # C # mean {2} 37.4 32.8 32.8 # —————————————————————————————————————————————————————— # # {1} - these Strata B patients got the drug combination # {2} - This is strata level C for these patients # —————————————————————————————————————————————————————— # # Analysis was done using cool methods that are correct # # file: /path/to/stuff/that/lives/there HASH:1ac41b242a"},{"path":"https://insightsengineering.github.io/rtables/articles/title_footer.html","id":"annotating-an-existing-table-with-referential-footnotes","dir":"Articles","previous_headings":"Referential Footnotes","what":"Annotating an Existing Table with Referential Footnotes","title":"Titles, Footers, and Referential Footnotes","text":"addition inserting referential footnotes tabulation time within analysis functions, can also annotate tables post-hoc. also way add footnotes column labels, controlled within analysis content function. fnotes_at_path<- function accepts row path, column path, value full set footnotes defined locations (NULL character vector). non-NULL row path NULL column path specifies footnote(s) attached row, NULL row path non-NULL column path indicates go column. non-NULL indicates cell (must resolve individual cell). Note step content row must add path, even though didn’t need put footnote full row. Currently, content rows default named label rather name corresponding facet. reflected output , e.g., row_paths_summary. can add footnotes cell like :","code":"## from ?tolower example slightly modified .simpleCap <- function(x) { if (length(x) > 1) { return(sapply(x, .simpleCap)) } s <- strsplit(tolower(x), \" \")[[1]] paste(toupper(substring(s, 1, 1)), substring(s, 2), sep = \"\", collapse = \" \") } adsl2 <- ex_adsl %>% filter(SEX %in% c(\"M\", \"F\") & RACE %in% (levels(RACE)[1:3])) %>% ## we trim the level names here solely due to space considerations mutate(ethnicity = .simpleCap(gsub(\"(.*)OR.*\", \"\\\\1\", RACE)), RACE = factor(RACE)) lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by(\"SEX\", split_fun = drop_split_levels) %>% split_rows_by(\"RACE\", labels_var = \"ethnicity\", split_fun = drop_split_levels) %>% summarize_row_groups() %>% analyze(c(\"AGE\", \"STRATA1\")) tbl2 <- build_table(lyt2, adsl2) tbl2 # A: Drug X B: Placebo C: Combination # F M F M F M # ——————————————————————————————————————————————————————————————————————————————————————— # Asian 41 (53.9%) 25 (54.3%) 36 (52.2%) 30 (60.0%) 39 (60.9%) 32 (57.1%) # AGE # Mean 31.22 34.60 35.06 38.63 36.44 37.66 # STRATA1 # A 11 10 14 10 11 7 # B 11 9 15 7 11 14 # C 19 6 7 13 17 11 # Black 18 (23.7%) 12 (26.1%) 16 (23.2%) 12 (24.0%) 14 (21.9%) 14 (25.0%) # AGE # Mean 34.06 34.58 33.88 36.33 33.21 34.21 # STRATA1 # A 5 2 5 6 3 7 # B 6 5 3 4 4 4 # C 7 5 8 2 7 3 # White 17 (22.4%) 9 (19.6%) 17 (24.6%) 8 (16.0%) 11 (17.2%) 10 (17.9%) # AGE # Mean 34.12 40.00 32.41 34.62 33.00 30.80 # STRATA1 # A 5 3 3 3 3 5 # B 5 4 8 4 5 2 # C 7 2 6 1 3 3 fnotes_at_path(tbl2, c(\"RACE\", \"ASIAN\")) <- c(\"hi\", \"there\") tbl2 # A: Drug X B: Placebo C: Combination # F M F M F M # —————————————————————————————————————————————————————————————————————————————————————————— # Asian {1, 2} 41 (53.9%) 25 (54.3%) 36 (52.2%) 30 (60.0%) 39 (60.9%) 32 (57.1%) # AGE # Mean 31.22 34.60 35.06 38.63 36.44 37.66 # STRATA1 # A 11 10 14 10 11 7 # B 11 9 15 7 11 14 # C 19 6 7 13 17 11 # Black 18 (23.7%) 12 (26.1%) 16 (23.2%) 12 (24.0%) 14 (21.9%) 14 (25.0%) # AGE # Mean 34.06 34.58 33.88 36.33 33.21 34.21 # STRATA1 # A 5 2 5 6 3 7 # B 6 5 3 4 4 4 # C 7 5 8 2 7 3 # White 17 (22.4%) 9 (19.6%) 17 (24.6%) 8 (16.0%) 11 (17.2%) 10 (17.9%) # AGE # Mean 34.12 40.00 32.41 34.62 33.00 30.80 # STRATA1 # A 5 3 3 3 3 5 # B 5 4 8 4 5 2 # C 7 2 6 1 3 3 # —————————————————————————————————————————————————————————————————————————————————————————— # # {1} - hi # {2} - there # —————————————————————————————————————————————————————————————————————————————————————————— fnotes_at_path(tbl2, rowpath = NULL, c(\"ARM\", \"B: Placebo\")) <- c(\"this is a placebo\") tbl2 # A: Drug X B: Placebo {NA} C: Combination # F M F M F M # —————————————————————————————————————————————————————————————————————————————————————————— # Asian {1, 2} 41 (53.9%) 25 (54.3%) 36 (52.2%) 30 (60.0%) 39 (60.9%) 32 (57.1%) # AGE # Mean 31.22 34.60 35.06 38.63 36.44 37.66 # STRATA1 # A 11 10 14 10 11 7 # B 11 9 15 7 11 14 # C 19 6 7 13 17 11 # Black 18 (23.7%) 12 (26.1%) 16 (23.2%) 12 (24.0%) 14 (21.9%) 14 (25.0%) # AGE # Mean 34.06 34.58 33.88 36.33 33.21 34.21 # STRATA1 # A 5 2 5 6 3 7 # B 6 5 3 4 4 4 # C 7 5 8 2 7 3 # White 17 (22.4%) 9 (19.6%) 17 (24.6%) 8 (16.0%) 11 (17.2%) 10 (17.9%) # AGE # Mean 34.12 40.00 32.41 34.62 33.00 30.80 # STRATA1 # A 5 3 3 3 3 5 # B 5 4 8 4 5 2 # C 7 2 6 1 3 3 # —————————————————————————————————————————————————————————————————————————————————————————— # # {1} - hi # {2} - there # {NA} - this is a placebo # —————————————————————————————————————————————————————————————————————————————————————————— row_paths_summary(tbl2) # rowname node_class path # ——————————————————————————————————————————————————————————————————————————— # Asian ContentRow RACE, ASIAN, @content, Asian # AGE LabelRow RACE, ASIAN, AGE # Mean DataRow RACE, ASIAN, AGE, Mean # STRATA1 LabelRow RACE, ASIAN, STRATA1 # A DataRow RACE, ASIAN, STRATA1, A # B DataRow RACE, ASIAN, STRATA1, B # C DataRow RACE, ASIAN, STRATA1, C # Black ContentRow RACE, BLACK OR AFRICAN AMERICAN, @content, Black # AGE LabelRow RACE, BLACK OR AFRICAN AMERICAN, AGE # Mean DataRow RACE, BLACK OR AFRICAN AMERICAN, AGE, Mean # STRATA1 LabelRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1 # A DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, A # B DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, B # C DataRow RACE, BLACK OR AFRICAN AMERICAN, STRATA1, C # White ContentRow RACE, WHITE, @content, White # AGE LabelRow RACE, WHITE, AGE # Mean DataRow RACE, WHITE, AGE, Mean # STRATA1 LabelRow RACE, WHITE, STRATA1 # A DataRow RACE, WHITE, STRATA1, A # B DataRow RACE, WHITE, STRATA1, B # C DataRow RACE, WHITE, STRATA1, C fnotes_at_path( tbl2, rowpath = c(\"RACE\", \"ASIAN\", \"@content\", \"Asian\"), colpath = c(\"ARM\", \"B: Placebo\", \"SEX\", \"F\") ) <- \"These asian women got placebo treatments\" tbl2 # A: Drug X B: Placebo {NA} C: Combination # F M F M F M # —————————————————————————————————————————————————————————————————————————————————————————————— # Asian {1, 2} 41 (53.9%) 25 (54.3%) 36 (52.2%) {3} 30 (60.0%) 39 (60.9%) 32 (57.1%) # AGE # Mean 31.22 34.60 35.06 38.63 36.44 37.66 # STRATA1 # A 11 10 14 10 11 7 # B 11 9 15 7 11 14 # C 19 6 7 13 17 11 # Black 18 (23.7%) 12 (26.1%) 16 (23.2%) 12 (24.0%) 14 (21.9%) 14 (25.0%) # AGE # Mean 34.06 34.58 33.88 36.33 33.21 34.21 # STRATA1 # A 5 2 5 6 3 7 # B 6 5 3 4 4 4 # C 7 5 8 2 7 3 # White 17 (22.4%) 9 (19.6%) 17 (24.6%) 8 (16.0%) 11 (17.2%) 10 (17.9%) # AGE # Mean 34.12 40.00 32.41 34.62 33.00 30.80 # STRATA1 # A 5 3 3 3 3 5 # B 5 4 8 4 5 2 # C 7 2 6 1 3 3 # —————————————————————————————————————————————————————————————————————————————————————————————— # # {1} - hi # {2} - there # {3} - These asian women got placebo treatments # {NA} - this is a placebo # ——————————————————————————————————————————————————————————————————————————————————————————————"},{"path":"https://insightsengineering.github.io/rtables/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"define authors actively maintaining code base, contributors made significant contribution past. acknowledgements, see eponymous section Home Page. Gabriel Becker. Author. Original creator package Adrian Waddell. Author. Daniel Sabanés Bové. Contributor. Maximilian Mordig. Contributor. Davide Garolini. Contributor. Emily de la Rua. Contributor. Abinaya Yogasekaram. Contributor. Joe Zhu. Contributor, maintainer. F. Hoffmann-La Roche AG. Copyright holder, funder.","code":""},{"path":"https://insightsengineering.github.io/rtables/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Becker G, Waddell (2023). rtables: Reporting Tables. R package version 0.6.5.9013, https://insightsengineering.github.io/rtables/, https://github.com/insightsengineering/rtables.","code":"@Manual{, title = {rtables: Reporting Tables}, author = {Gabriel Becker and Adrian Waddell}, year = {2023}, note = {R package version 0.6.5.9013, https://insightsengineering.github.io/rtables/}, url = {https://github.com/insightsengineering/rtables}, }"},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"reporting-tables-with-r","dir":"","previous_headings":"","what":"Reporting tables with R","title":"Reporting Tables","text":"rtables R package designed create display complex tables R. cells rtable may contain high-dimensional data structure can displayed cell-specific formatting instructions. Currently, rtables can outputted ascii html, pdf, well Power Point (via conversion flextable objects). rtf support development future release. rtables developed copy written F. Hoffmann-La Roche released open source Apache License Version 2. rtables development driven need create regulatory ready tables health authority review. key requirements undertaking listed : values need programmatically accessible non-rounded state cross-checking multiple values displayed within cell flexible tabulation framework flexible formatting (cell spans, rounding, alignment, etc.) multiple output formats (html, ascii, latex, pdf, xml) flexible pagination horizontal vertical directions distinguish name label data structure work CDISC standards title, footnotes, cell cell/row/column references rtables currently covers virtually requirements, advances remain active development.","code":""},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"Reporting Tables","text":"rtables available CRAN can install latest released version : can install latest development version directly GitHub : Note might need set GITHUB_PAT environment variable order able install GitHub. Packaged releases (CRAN official CRAN releases) can found releases list.","code":"install.packages(\"rtables\") remotes::install_github(\"insightsengineering/formatters\") remotes::install_github(\"insightsengineering/rtables\")"},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"usage","dir":"","previous_headings":"","what":"Usage","title":"Reporting Tables","text":"first demonstrate demographic table-like example show creation complex table.","code":"library(rtables) lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(c(\"AGE\", \"BMRKR1\", \"BMRKR2\"), function(x, ...) { if (is.numeric(x)) { in_rows( \"Mean (sd)\" = c(mean(x), sd(x)), \"Median\" = median(x), \"Min - Max\" = range(x), .formats = c(\"xx.xx (xx.xx)\", \"xx.xx\", \"xx.xx - xx.xx\") ) } else if (is.factor(x) || is.character(x)) { in_rows(.list = list_wrap_x(table)(x)) } else { stop(\"type not supported\") } }) build_table(lyt, ex_adsl) #> A: Drug X B: Placebo C: Combination #> ———————————————————————————————————————————————————————————— #> AGE #> Mean (sd) 33.77 (6.55) 35.43 (7.90) 35.43 (7.72) #> Median 33.00 35.00 35.00 #> Min - Max 21.00 - 50.00 21.00 - 62.00 20.00 - 69.00 #> BMRKR1 #> Mean (sd) 5.97 (3.55) 5.70 (3.31) 5.62 (3.49) #> Median 5.39 4.81 4.61 #> Min - Max 0.41 - 17.67 0.65 - 14.24 0.17 - 21.39 #> BMRKR2 #> LOW 50 45 40 #> MEDIUM 37 56 42 #> HIGH 47 33 50 library(rtables) library(dplyr) ## for simplicity grab non-sparse subset ADSL <- ex_adsl %>% filter(RACE %in% levels(RACE)[1:3]) biomarker_ave <- function(x, ...) { val <- if(length(x) > 0) round(mean(x), 2) else \"no data\" in_rows( \"Biomarker 1 (mean)\" = rcell(val) ) } basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\") %>% split_cols_by(\"BMRKR2\") %>% split_rows_by(\"RACE\", split_fun = trim_levels_in_group(\"SEX\")) %>% split_rows_by(\"SEX\") %>% summarize_row_groups() %>% analyze(\"BMRKR1\", biomarker_ave) %>% build_table(ADSL) #> A: Drug X B: Placebo C: Combination #> LOW MEDIUM HIGH LOW MEDIUM HIGH LOW MEDIUM HIGH #> (N=45) (N=35) (N=46) (N=42) (N=48) (N=31) (N=40) (N=39) (N=47) #> ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— #> ASIAN #> F 13 (28.9%) 9 (25.7%) 19 (41.3%) 9 (21.4%) 18 (37.5%) 9 (29.0%) 13 (32.5%) 9 (23.1%) 17 (36.2%) #> Biomarker 1 (mean) 5.23 6.17 5.38 5.64 5.55 4.33 5.46 5.48 5.19 #> M 8 (17.8%) 7 (20.0%) 10 (21.7%) 12 (28.6%) 10 (20.8%) 8 (25.8%) 5 (12.5%) 11 (28.2%) 16 (34.0%) #> Biomarker 1 (mean) 6.77 6.06 5.54 4.9 4.98 6.81 6.53 5.47 4.98 #> U 1 (2.2%) 1 (2.9%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 1 (3.2%) 0 (0.0%) 1 (2.6%) 1 (2.1%) #> Biomarker 1 (mean) 4.68 7.7 no data no data no data 6.97 no data 11.93 9.01 #> BLACK OR AFRICAN AMERICAN #> F 6 (13.3%) 3 (8.6%) 9 (19.6%) 6 (14.3%) 8 (16.7%) 2 (6.5%) 7 (17.5%) 4 (10.3%) 3 (6.4%) #> Biomarker 1 (mean) 5.01 7.2 6.79 6.15 5.26 8.57 5.72 5.76 4.58 #> M 5 (11.1%) 5 (14.3%) 2 (4.3%) 3 (7.1%) 5 (10.4%) 4 (12.9%) 4 (10.0%) 5 (12.8%) 5 (10.6%) #> Biomarker 1 (mean) 6.92 5.82 11.66 4.46 6.14 8.47 6.16 5.25 4.83 #> U 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 1 (2.5%) 1 (2.6%) 0 (0.0%) #> Biomarker 1 (mean) no data no data no data no data no data no data 2.79 9.82 no data #> UNDIFFERENTIATED 1 (2.2%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 2 (5.0%) 0 (0.0%) 0 (0.0%) #> Biomarker 1 (mean) 9.48 no data no data no data no data no data 6.46 no data no data #> WHITE #> F 6 (13.3%) 7 (20.0%) 4 (8.7%) 5 (11.9%) 6 (12.5%) 6 (19.4%) 6 (15.0%) 3 (7.7%) 2 (4.3%) #> Biomarker 1 (mean) 4.43 7.83 4.52 6.42 5.07 7.83 6.71 5.87 10.7 #> M 4 (8.9%) 3 (8.6%) 2 (4.3%) 6 (14.3%) 1 (2.1%) 1 (3.2%) 2 (5.0%) 5 (12.8%) 3 (6.4%) #> Biomarker 1 (mean) 5.81 7.23 1.39 4.72 4.58 12.87 2.3 5.1 5.98 #> U 1 (2.2%) 0 (0.0%) 0 (0.0%) 1 (2.4%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) 0 (0.0%) #> Biomarker 1 (mean) 3.94 no data no data 3.77 no data no data no data no data no data"},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"acknowledgments","dir":"","previous_headings":"","what":"Acknowledgments","title":"Reporting Tables","text":"like thank everyone made rtables better project providing feedback improving examples & vignettes. following list contributors alphabetical: Maximo Carreras, Francois Collins, Saibah Chohan, Tadeusz Lewandowski, Nick Paszty, Nina Qi, Jana Stoilova, Heng Wang, Godwin Yung","code":""},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"advanced-rtables-training","dir":"","previous_headings":"Presentations","what":"Advanced rtables Training","title":"Reporting Tables","text":"Part 1 Slides Part 2 Slides","code":""},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"rinpharma-workshop-creating-submission-quality-clinical-trial-reporting-tables-in-r-with-rtables","dir":"","previous_headings":"Presentations","what":"RinPharma Workshop: Creating Submission-Quality Clinical Trial Reporting Tables in R with rtables","title":"Reporting Tables","text":"Slides","code":""},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"r-adoption-series","dir":"","previous_headings":"Presentations","what":"R Adoption Series","title":"Reporting Tables","text":"R Adoption Series presentation 2022 Slides","code":""},{"path":"https://insightsengineering.github.io/rtables/index.html","id":"new-current-layouting-and-tabulation-framework-v03","dir":"","previous_headings":"Presentations","what":"New (Current) Layouting and Tabulation Framework (v.0.3+)","title":"Reporting Tables","text":"useR!2020 Presentation (v0.3.1.1) July 2020","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/CellValue.html","id":null,"dir":"Reference","previous_headings":"","what":"Cell Value constructor — CellValue","title":"Cell Value constructor — CellValue","text":"Cell Value constructor","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/CellValue.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Cell Value constructor — CellValue","text":"","code":"CellValue( val, format = NULL, colspan = 1L, label = NULL, indent_mod = NULL, footnotes = NULL, align = NULL, format_na_str = NULL )"},{"path":"https://insightsengineering.github.io/rtables/reference/CellValue.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Cell Value constructor — CellValue","text":"val . value cell exactly passed formatter returned extracted format FormatSpec. Format associated split. Formats can declared via strings (\"xx.x\") function. cases analyze calls, can character vectors lists functions. colspan integer(1). Column span value. label character(1). label (confused name) object/structure. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior. footnotes list NULL. Referential footnote messages cell. align character(1) NULL. Alignment value rendered . defaults \"center\" NULL used. See formatters::list_valid_aligns() currently supported alignments. format_na_str character(1). String displayed formatted cell's value(s) NA.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/CellValue.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Cell Value constructor — CellValue","text":"object representing value within single cell within populated table. underlying structure object implementation detail relied upon beyond calling accessors class.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/EmptyColInfo.html","id":null,"dir":"Reference","previous_headings":"","what":"Empty table, column, split objects — EmptyColInfo","title":"Empty table, column, split objects — EmptyColInfo","text":"Empty objects various types compare efficiently.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/ManualSplit.html","id":null,"dir":"Reference","previous_headings":"","what":"Manually defined split — ManualSplit","title":"Manually defined split — ManualSplit","text":"Manually defined split","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/ManualSplit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Manually defined split — ManualSplit","text":"","code":"ManualSplit( levels, label, name = \"manual\", extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = \"\", cextra_args = list(), label_pos = \"visible\", page_prefix = NA_character_, section_div = NA_character_ )"},{"path":"https://insightsengineering.github.io/rtables/reference/ManualSplit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Manually defined split — ManualSplit","text":"levels character. Levels split (.e. children manual split) label character(1). label (confused name) object/structure. name character(1). Name split/table/row created. Defaults corresponding label, required . extra_args list. Extra arguments passed tabulation function. Element position list corresponds children split. Named elements child-specific lists ignored match formal argument tabulation function. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior. cindent_mod numeric(1). indent modifier content tables generated split. cvar character(1). variable, , content function accept. Defaults NA. cextra_args list. Extra arguments passed content function tabulating row group summaries. label_pos character(1). Location variable label displayed, Accepts \"hidden\" (default non-analyze row splits), \"visible\", \"topleft\", - analyze splits - \"default\". analyze calls, \"default\" indicates variable visible multiple variables analyzed level nesting. page_prefix character(1). Prefix, appended split value, forcing pagination children split/table section_div character(1). String repeated section divider group defined split instruction, NA_character_ (default) section divider.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/ManualSplit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Manually defined split — ManualSplit","text":"ManualSplit object.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/ManualSplit.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Manually defined split — ManualSplit","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/MultiVarSplit.html","id":null,"dir":"Reference","previous_headings":"","what":"Split between two or more different variables — MultiVarSplit","title":"Split between two or more different variables — MultiVarSplit","text":"Split two different variables","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/MultiVarSplit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Split between two or more different variables — MultiVarSplit","text":"","code":"MultiVarSplit( vars, split_label = \"\", varlabels = NULL, varnames = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_format = NULL, split_na_str = NA_character_, split_name = \"multivars\", child_labels = c(\"default\", \"visible\", \"hidden\"), extra_args = list(), indent_mod = 0L, cindent_mod = 0L, cvar = \"\", cextra_args = list(), label_pos = \"visible\", split_fun = NULL, page_prefix = NA_character_, section_div = NA_character_ )"},{"path":"https://insightsengineering.github.io/rtables/reference/MultiVarSplit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Split between two or more different variables — MultiVarSplit","text":"vars character vector. Multiple variable names. split_label string. Label string associated table generated split. confused labels assigned child (based data type split tabulation). varlabels character vector. Labels vars varnames character vector. Names vars appear pathing. vars unique variable names. , variable names suffixes necessary enforce uniqueness. cfun list/function/NULL. tabulation function(s) creating content rows. Must accept x df first parameter. Must accept labelstr second argument. Can optionally accept optional arguments accepted analysis functions. See analyze. cformat format spec. Format content rows cna_str character. NA string use cformat content table. split_format FormatSpec. Default format associated split created. split_na_str character. NA string vector use split_format. split_name string. Name associated split (pathing, etc) child_labels string. One \"default\", \"visible\", \"hidden\". display behavior labels (.e. label rows) children split. Defaults \"default\" flags label row visible child 0 content rows. extra_args list. Extra arguments passed tabulation function. Element position list corresponds children split. Named elements child-specific lists ignored match formal argument tabulation function. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior. cindent_mod numeric(1). indent modifier content tables generated split. cvar character(1). variable, , content function accept. Defaults NA. cextra_args list. Extra arguments passed content function tabulating row group summaries. label_pos character(1). Location variable label displayed, Accepts \"hidden\" (default non-analyze row splits), \"visible\", \"topleft\", - analyze splits - \"default\". analyze calls, \"default\" indicates variable visible multiple variables analyzed level nesting. split_fun function/NULL. custom splitting function See custom_split_funs page_prefix character(1). Prefix, appended split value, forcing pagination children split/table section_div character(1). String repeated section divider group defined split instruction, NA_character_ (default) section divider.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/MultiVarSplit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Split between two or more different variables — MultiVarSplit","text":"MultiVarSplit object.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/MultiVarSplit.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Split between two or more different variables — MultiVarSplit","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/VarLevelSplit.html","id":null,"dir":"Reference","previous_headings":"","what":"Split on levels within a variable — VarLevelSplit-class","title":"Split on levels within a variable — VarLevelSplit-class","text":"Split levels within variable","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/VarLevelSplit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Split on levels within a variable — VarLevelSplit-class","text":"","code":"VarLevelSplit( var, split_label, labels_var = NULL, cfun = NULL, cformat = NULL, cna_str = NA_character_, split_fun = NULL, split_format = NULL, split_na_str = NA_character_, valorder = NULL, split_name = var, child_labels = c(\"default\", \"visible\", \"hidden\"), extra_args = list(), indent_mod = 0L, label_pos = c(\"topleft\", \"hidden\", \"visible\"), cindent_mod = 0L, cvar = \"\", cextra_args = list(), page_prefix = NA_character_, section_div = NA_character_ ) VarLevWBaselineSplit( var, ref_group, labels_var = var, split_label, split_fun = NULL, label_fstr = \"%s - %s\", cfun = NULL, cformat = NULL, cna_str = NA_character_, cvar = \"\", split_format = NULL, split_na_str = NA_character_, valorder = NULL, split_name = var, extra_args = list() )"},{"path":"https://insightsengineering.github.io/rtables/reference/VarLevelSplit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Split on levels within a variable — VarLevelSplit-class","text":"var string, variable name split_label string. Label string associated table generated split. confused labels assigned child (based data type split tabulation). labels_var string, name variable containing labels displayed values var cfun list/function/NULL. tabulation function(s) creating content rows. Must accept x df first parameter. Must accept labelstr second argument. Can optionally accept optional arguments accepted analysis functions. See analyze. cformat format spec. Format content rows cna_str character. NA string use cformat content table. split_fun function/NULL. custom splitting function See custom_split_funs split_format FormatSpec. Default format associated split created. split_na_str character. NA string vector use split_format. valorder character vector. Order split children appear resulting table. split_name string. Name associated split (pathing, etc) child_labels string. One \"default\", \"visible\", \"hidden\". display behavior labels (.e. label rows) children split. Defaults \"default\" flags label row visible child 0 content rows. extra_args list. Extra arguments passed tabulation function. Element position list corresponds children split. Named elements child-specific lists ignored match formal argument tabulation function. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior. label_pos character(1). Location variable label displayed, Accepts \"hidden\" (default non-analyze row splits), \"visible\", \"topleft\", - analyze splits - \"default\". analyze calls, \"default\" indicates variable visible multiple variables analyzed level nesting. cindent_mod numeric(1). indent modifier content tables generated split. cvar character(1). variable, , content function accept. Defaults NA. cextra_args list. Extra arguments passed content function tabulating row group summaries. page_prefix character(1). Prefix, appended split value, forcing pagination children split/table section_div character(1). String repeated section divider group defined split instruction, NA_character_ (default) section divider. ref_group character. Value var taken ref_group/control compared . label_fstr string. sprintf style format string containing. non-comparison splits, can contain one \"%s\" takes current split value generates row/column label. Comparison-based splits can contain two \"%s\".","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/VarLevelSplit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Split on levels within a variable — VarLevelSplit-class","text":"VarLevelSplit object.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/VarLevelSplit.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Split on levels within a variable — VarLevelSplit-class","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/Viewer.html","id":null,"dir":"Reference","previous_headings":"","what":"Display an rtable object in the Viewer pane in RStudio or in a browser — Viewer","title":"Display an rtable object in the Viewer pane in RStudio or in a browser — Viewer","text":"table displayed using bootstrap styling tables.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/Viewer.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Display an rtable object in the Viewer pane in RStudio or in a browser — Viewer","text":"","code":"Viewer(x, y = NULL, ...)"},{"path":"https://insightsengineering.github.io/rtables/reference/Viewer.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Display an rtable object in the Viewer pane in RStudio or in a browser — Viewer","text":"x object class rtable shiny.tag (defined htmltools package) y optional second argument type x ... arguments passed as_html","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/Viewer.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Display an rtable object in the Viewer pane in RStudio or in a browser — Viewer","text":"meaningful. Called side effect opening browser viewer pane.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/Viewer.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Display an rtable object in the Viewer pane in RStudio or in a browser — Viewer","text":"","code":"if (interactive()) { sl5 <- factor(iris$Sepal.Length > 5, levels = c(TRUE, FALSE), labels = c(\"S.L > 5\", \"S.L <= 5\") ) df <- cbind(iris, sl5 = sl5) lyt <- basic_table() %>% split_cols_by(\"sl5\") %>% analyze(\"Sepal.Length\") tbl <- build_table(lyt, df) Viewer(tbl) Viewer(tbl, tbl) tbl2 <- htmltools::tags$div( class = \"table-responsive\", as_html(tbl, class_table = \"table\") ) Viewer(tbl, tbl2) }"},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":null,"dir":"Reference","previous_headings":"","what":"Add the column population counts to the header — add_colcounts","title":"Add the column population counts to the header — add_colcounts","text":"Add data derived column counts.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add the column population counts to the header — add_colcounts","text":"","code":"add_colcounts(lyt, format = \"(N=xx)\")"},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add the column population counts to the header — add_colcounts","text":"lyt layout object pre-data used tabulation format FormatSpec. Format associated split. Formats can declared via strings (\"xx.x\") function. cases analyze calls, can character vectors lists functions.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add the column population counts to the header — add_colcounts","text":"PreDataTableLayouts object suitable passing layouting functions, build_table.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Add the column population counts to the header — add_colcounts","text":"often case column counts derived input data build_table representative population counts. example, events counted table header display number subjects total number events. case use col_count argument build_table control counts displayed table header.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Add the column population counts to the header — add_colcounts","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_colcounts.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Add the column population counts to the header — add_colcounts","text":"","code":"lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% add_colcounts() %>% split_rows_by(\"RACE\", split_fun = drop_split_levels) %>% analyze(\"AGE\", afun = function(x) list(min = min(x), max = max(x))) lyt #> A Pre-data Table Layout #> #> Column-Split Structure: #> ARM (lvls) #> #> Row-Split Structure: #> RACE (lvls) -> AGE (** analysis **) #> tbl <- build_table(lyt, DM) tbl #> A: Drug X B: Placebo C: Combination #> (N=121) (N=106) (N=129) #> ——————————————————————————————————————————————————————————————————— #> ASIAN #> min 20 21 22 #> max 58 55 53 #> BLACK OR AFRICAN AMERICAN #> min 23 21 24 #> max 60 42 51 #> WHITE #> min 30 25 28 #> max 47 55 47"},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_facet.html","id":null,"dir":"Reference","previous_headings":"","what":"Add a combination facet in postprocessing — add_combo_facet","title":"Add a combination facet in postprocessing — add_combo_facet","text":"Add combination facet postprocessing stage custom split fun.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_facet.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add a combination facet in postprocessing — add_combo_facet","text":"","code":"add_combo_facet(name, label = name, levels, extra = list()) add_overall_facet(name, label, extra = list())"},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_facet.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add a combination facet in postprocessing — add_combo_facet","text":"name character(1). Name resulting facet (use pathing, etc). label character(1). Label resulting facet. levels character. Vector levels combine within resulting facet. extra list. Extra arguments passed analysis functions applied within resulting facet.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_facet.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add a combination facet in postprocessing — add_combo_facet","text":"function can used within post argument make_split_fun.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_facet.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Add a combination facet in postprocessing — add_combo_facet","text":"add_combo_facet, data associated resulting facet data associated facets level levels, rbound together. particular, means levels overlapping, data appears duplicated.","code":""},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_facet.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Add a combination facet in postprocessing — add_combo_facet","text":"","code":"mysplfun <- make_split_fun(post = list( add_combo_facet(\"A_B\", label = \"Arms A+B\", levels = c(\"A: Drug X\", \"B: Placebo\") ), add_overall_facet(\"ALL\", label = \"All Arms\") )) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\", split_fun = mysplfun) %>% analyze(\"AGE\") tbl <- build_table(lyt, DM)"},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":null,"dir":"Reference","previous_headings":"","what":"Add Combination Levels to split — select_all_levels","title":"Add Combination Levels to split — select_all_levels","text":"Add Combination Levels split","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add Combination Levels to split — select_all_levels","text":"","code":"select_all_levels add_combo_levels(combosdf, trim = FALSE, first = FALSE, keep_levels = NULL)"},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":"format","dir":"Reference","previous_headings":"","what":"Format","title":"Add Combination Levels to split — select_all_levels","text":"object class AllLevelsSentinel length 0.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add Combination Levels to split — select_all_levels","text":"combosdf data.frame/tbl_df. Columns valname, label, levelcombo, exargs. levelcombo exargs list columns. Passing select_all_levels object value comblevels column indicates overall/-observations level created. trim logical(1). splits corresponding 0 observations kept tabulating. first logical(1). created split level placed first levels (TRUE) last (FALSE, default). keep_levels character NULL. non-NULL, levels retain across combination individual levels.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add Combination Levels to split — select_all_levels","text":"closure suitable use splitting function (splfun) creating table layout","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":"note","dir":"Reference","previous_headings":"","what":"Note","title":"Add Combination Levels to split — select_all_levels","text":"Analysis summary functions order matters never used within tabulation framework.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_combo_levels.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Add Combination Levels to split — select_all_levels","text":"","code":"library(tibble) combodf <- tribble( ~valname, ~label, ~levelcombo, ~exargs, \"A_B\", \"Arms A+B\", c(\"A: Drug X\", \"B: Placebo\"), list(), \"A_C\", \"Arms A+C\", c(\"A: Drug X\", \"C: Combination\"), list() ) lyt <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\", split_fun = add_combo_levels(combodf)) %>% analyze(\"AGE\") tbl <- build_table(lyt, DM) tbl #> A: Drug X B: Placebo C: Combination Arms A+B Arms A+C #> (N=121) (N=106) (N=129) (N=227) (N=250) #> ———————————————————————————————————————————————————————————————————— #> Mean 34.91 33.02 34.57 34.03 34.73 lyt1 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\", split_fun = add_combo_levels(combodf, keep_levels = c( \"A_B\", \"A_C\" ) ) ) %>% analyze(\"AGE\") tbl1 <- build_table(lyt1, DM) tbl1 #> Arms A+B Arms A+C #> (N=227) (N=250) #> —————————————————————————— #> Mean 34.03 34.73 smallerDM <- droplevels(subset(DM, SEX %in% c(\"M\", \"F\") & grepl(\"^(A|B)\", ARM))) lyt2 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\", split_fun = add_combo_levels(combodf[1, ])) %>% split_cols_by(\"SEX\", split_fun = add_overall_level(\"SEX_ALL\", \"All Genders\") ) %>% analyze(\"AGE\") lyt3 <- basic_table(show_colcounts = TRUE) %>% split_cols_by(\"ARM\", split_fun = add_combo_levels(combodf)) %>% split_rows_by(\"SEX\", split_fun = add_overall_level(\"SEX_ALL\", \"All Genders\") ) %>% summarize_row_groups() %>% analyze(\"AGE\") tbl3 <- build_table(lyt3, smallerDM) tbl3 #> A: Drug X B: Placebo Arms A+B Arms A+C #> (N=121) (N=106) (N=227) (N=121) #> ——————————————————————————————————————————————————————————————————————— #> All Genders 121 (100.0%) 106 (100.0%) 227 (100.0%) 121 (100.0%) #> Mean 34.91 33.02 34.03 34.91 #> F 70 (57.9%) 56 (52.8%) 126 (55.5%) 70 (57.9%) #> Mean 33.71 33.84 33.77 33.71 #> M 51 (42.1%) 50 (47.2%) 101 (44.5%) 51 (42.1%) #> Mean 36.55 32.10 34.35 36.55"},{"path":"https://insightsengineering.github.io/rtables/reference/add_existing_table.html","id":null,"dir":"Reference","previous_headings":"","what":"Add an already calculated table to the layout — add_existing_table","title":"Add an already calculated table to the layout — add_existing_table","text":"Add already calculated table layout","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_existing_table.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add an already calculated table to the layout — add_existing_table","text":"","code":"add_existing_table(lyt, tt, indent_mod = 0)"},{"path":"https://insightsengineering.github.io/rtables/reference/add_existing_table.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add an already calculated table to the layout — add_existing_table","text":"lyt layout object pre-data used tabulation tt TableTree (related class). TableTree object representing populated table. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_existing_table.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add an already calculated table to the layout — add_existing_table","text":"PreDataTableLayouts object suitable passing layouting functions, build_table.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_existing_table.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Add an already calculated table to the layout — add_existing_table","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_existing_table.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Add an already calculated table to the layout — add_existing_table","text":"","code":"lyt1 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(\"AGE\", afun = mean, format = \"xx.xx\") tbl1 <- build_table(lyt1, DM) tbl1 #> A: Drug X B: Placebo C: Combination #> —————————————————————————————————————————————— #> mean 34.91 33.02 34.57 lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(\"AGE\", afun = sd, format = \"xx.xx\") %>% add_existing_table(tbl1) tbl2 <- build_table(lyt2, DM) tbl2 #> A: Drug X B: Placebo C: Combination #> —————————————————————————————————————————————— #> sd 7.79 6.34 6.50 #> mean 34.91 33.02 34.57 table_structure(tbl2) #> [TableTree] root #> [ElementaryTable] AGE (1 x 3) #> [ElementaryTable] AGE (1 x 3) row_paths_summary(tbl2) #> rowname node_class path #> ———————————————————————————————————————— #> sd DataRow root, AGE, sd #> mean DataRow root, AGE, mean"},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_col.html","id":null,"dir":"Reference","previous_headings":"","what":"Add Overall Column — add_overall_col","title":"Add Overall Column — add_overall_col","text":"function add overall column top level splitting, within existing column splits. See add_overall_level recommended way add overall columns generally within existing splits.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_col.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add Overall Column — add_overall_col","text":"","code":"add_overall_col(lyt, label)"},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_col.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add Overall Column — add_overall_col","text":"lyt layout object pre-data used tabulation label character(1). label (confused name) object/structure.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_col.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add Overall Column — add_overall_col","text":"PreDataTableLayouts object suitable passing layouting functions, build_table.","code":""},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_col.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Add Overall Column — add_overall_col","text":"","code":"lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% add_overall_col(\"All Patients\") %>% analyze(\"AGE\") lyt #> A Pre-data Table Layout #> #> Column-Split Structure: #> ARM (lvls) #> (all obs) #> #> Row-Split Structure: #> AGE (** analysis **) #> tbl <- build_table(lyt, DM) tbl #> A: Drug X B: Placebo C: Combination All Patients #> ————————————————————————————————————————————————————————————— #> Mean 34.91 33.02 34.57 34.22"},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_level.html","id":null,"dir":"Reference","previous_headings":"","what":"Add an virtual 'overall' level to split — add_overall_level","title":"Add an virtual 'overall' level to split — add_overall_level","text":"Add virtual 'overall' level split","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_level.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add an virtual 'overall' level to split — add_overall_level","text":"","code":"add_overall_level( valname = \"Overall\", label = valname, extra_args = list(), first = TRUE, trim = FALSE )"},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_level.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add an virtual 'overall' level to split — add_overall_level","text":"valname character(1). 'Value' assigned implicit -observations split level. Defaults \"Overall\" label character(1). label (confused name) object/structure. extra_args list. Extra arguments passed tabulation function. Element position list corresponds children split. Named elements child-specific lists ignored match formal argument tabulation function. first logical(1). implicit level appear first (TRUE) last FALSE. Defaults TRUE. trim logical(1). splits corresponding 0 observations kept tabulating.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_level.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add an virtual 'overall' level to split — add_overall_level","text":"closure suitable use splitting function (splfun) creating table layout","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/add_overall_level.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Add an virtual 'overall' level to split — add_overall_level","text":"","code":"lyt <- basic_table() %>% split_cols_by(\"ARM\", split_fun = add_overall_level(\"All Patients\", first = FALSE )) %>% analyze(\"AGE\") tbl <- build_table(lyt, DM) tbl #> A: Drug X B: Placebo C: Combination All Patients #> ————————————————————————————————————————————————————————————— #> Mean 34.91 33.02 34.57 34.22 lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_rows_by(\"RACE\", split_fun = add_overall_level(\"All Ethnicities\") ) %>% summarize_row_groups(label_fstr = \"%s (n)\") %>% analyze(\"AGE\") lyt2 #> A Pre-data Table Layout #> #> Column-Split Structure: #> ARM (lvls) #> #> Row-Split Structure: #> RACE (lvls) -> AGE (** analysis **) #> tbl2 <- build_table(lyt2, DM) tbl2 #> A: Drug X B: Placebo C: Combination #> ———————————————————————————————————————————————————————————————————————————————————————————— #> All Ethnicities (n) 121 (100.0%) 106 (100.0%) 129 (100.0%) #> Mean 34.91 33.02 34.57 #> ASIAN (n) 79 (65.3%) 68 (64.2%) 84 (65.1%) #> Mean 34.20 32.68 34.63 #> BLACK OR AFRICAN AMERICAN (n) 28 (23.1%) 24 (22.6%) 27 (20.9%) #> Mean 34.68 31.71 34.00 #> WHITE (n) 14 (11.6%) 14 (13.2%) 18 (14.0%) #> Mean 39.36 36.93 35.11 #> AMERICAN INDIAN OR ALASKA NATIVE (n) 0 (0.0%) 0 (0.0%) 0 (0.0%) #> Mean NA NA NA #> MULTIPLE (n) 0 (0.0%) 0 (0.0%) 0 (0.0%) #> Mean NA NA NA #> NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER (n) 0 (0.0%) 0 (0.0%) 0 (0.0%) #> Mean NA NA NA #> OTHER (n) 0 (0.0%) 0 (0.0%) 0 (0.0%) #> Mean NA NA NA #> UNKNOWN (n) 0 (0.0%) 0 (0.0%) 0 (0.0%) #> Mean NA NA NA"},{"path":"https://insightsengineering.github.io/rtables/reference/additional_fun_params.html","id":null,"dir":"Reference","previous_headings":"","what":"Additional parameters within analysis and content functions\n(afun/cfun) — additional_fun_params","title":"Additional parameters within analysis and content functions\n(afun/cfun) — additional_fun_params","text":"possible add specific parameters afun cfun, analyze summarize_row_groups respectively. parameters grant access relevant information like row split structure (see spl_context) predefined baseline (.ref_group).","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/additional_fun_params.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Additional parameters within analysis and content functions\n(afun/cfun) — additional_fun_params","text":"list describe parameters can added custom analysis function: .N_col column-wise N (column count) full column tabulated within .N_total overall N (observation count, defined sum column counts) tabulation .N_row row-wise N (row group count) group observations analyzed (.e. column-based subsetting) .df_row data.frame observations row group analyzed (.e. column-based subsetting) .var variable analyzed .ref_group data.frame vector subset corresponding ref_group column including subsetting defined row-splitting. Optional required/meaningful ref_group column defined .ref_full data.frame vector subset corresponding ref_group column without subsetting defined row-splitting. Optional required/meaningful ref_group column defined .in_ref_col boolean indicates calculation done cells within reference column .spl_context data.frame, row gives information previous/'ancestor' split state. See spl_context .alt_df_row data.frame, .e. alt_count_df row splitting. can used .all_col_exprs .spl_context information retrieve current faceting, alt_count_df. can empty table entries filtered . .alt_df data.frame, .alt_df_row filtered columns expression. data present faceting main data df. also filters NAs related parameters set (e.g. inclNAs analyze). Similarly .alt_df_row, can empty table entries filtered . .all_col_exprs list expressions. represents different column splitting. .all_col_counts vector integers. represents global count column. differs alt_counts_df used (see build_table).","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/additional_fun_params.html","id":"note","dir":"Reference","previous_headings":"","what":"Note","title":"Additional parameters within analysis and content functions\n(afun/cfun) — additional_fun_params","text":"formals specified incorrectly present tabulation machinery, missing. example.ref_group missing baseline previously defined data splitting (via ref_group parameters , e.g., split_rows_by). Similarly, alt_counts_df provided build_table, .alt_df_row .alt_df present.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":null,"dir":"Reference","previous_headings":"","what":"Generate Rows Analyzing Variables Across Columns — analyze","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"Adding analyzed variables table layout defines primary tabulation performed. adding calls analyze /analyze_colvars layout pipeline. adding splitting, tabulation occur current/next level nesting default.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"","code":"analyze( lyt, vars, afun = simple_analysis, var_labels = vars, table_names = vars, format = NULL, na_str = NA_character_, nested = TRUE, inclNAs = FALSE, extra_args = list(), show_labels = c(\"default\", \"visible\", \"hidden\"), indent_mod = 0L, section_div = NA_character_ )"},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"lyt layout object pre-data used tabulation vars character vector. Multiple variable names. afun function. Analysis function, must take x df first parameter. Can optionally take parameters populated tabulation framework. See Details analyze. var_labels character. Variable labels 1 variables table_names character. Names tables representing atomic analysis. Defaults var. format FormatSpec. Format associated split. Formats can declared via strings (\"xx.x\") function. cases analyze calls, can character vectors lists functions. na_str character(1). String displayed value x missing. Defaults \"NA\". nested boolean. layout instruction applied within existing layout structure possible (TRUE, default) new top-level element (`FALSE). Ignored nest split underneath analyses, allowed. inclNAs boolean. observations NA var variable(s) included performing analysis. Defaults FALSE extra_args list. Extra arguments passed tabulation function. Element position list corresponds children split. Named elements child-specific lists ignored match formal argument tabulation function. show_labels character(1). variable labels corresponding variable(s) vars visible resulting table. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior. section_div character(1). String repeated section divider group defined split instruction, NA_character_ (default) section divider.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"PreDataTableLayouts object suitable passing layouting functions, build_table.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"non-NULL format used specify formats generated rows, can character vector, function, list functions. repped number rows known tabulation process, overridden formats specified within rcell calls afun. analysis function (afun) take first parameter either x df. function accepts changes behavior tabulation performed. afun's first parameter x, receive corresponding subset vector data relevant column (var ) raw data used build table. afun's first parameter df, receive corresponding subset data.frame (.e. columns) raw data tabulated addition differentiation first argument, analysis function can optionally accept number parameters , present formals passed function tabulation machinery. listed described additional_fun_params.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"note","dir":"Reference","previous_headings":"","what":"Note","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"None arguments described Details section can overridden via extra_args calling make_afun. .N_col .N_total can overridden via col_counts argument build_table. Alternative values others must calculated within afun based combination extra arguments unmodified values provided tabulation framework.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Generate Rows Analyzing Variables Across Columns — analyze","text":"","code":"lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% analyze(\"AGE\", afun = list_wrap_x(summary), format = \"xx.xx\") lyt #> A Pre-data Table Layout #> #> Column-Split Structure: #> ARM (lvls) #> #> Row-Split Structure: #> AGE (** analysis **) #> tbl <- build_table(lyt, DM) tbl #> A: Drug X B: Placebo C: Combination #> ————————————————————————————————————————————————— #> Min. 20.00 21.00 22.00 #> 1st Qu. 29.00 29.00 30.00 #> Median 33.00 32.00 33.00 #> Mean 34.91 33.02 34.57 #> 3rd Qu. 39.00 37.00 38.00 #> Max. 60.00 55.00 53.00 lyt2 <- basic_table() %>% split_cols_by(\"Species\") %>% analyze(head(names(iris), -1), afun = function(x) { list( \"mean / sd\" = rcell(c(mean(x), sd(x)), format = \"xx.xx (xx.xx)\"), \"range\" = rcell(diff(range(x)), format = \"xx.xx\") ) }) lyt2 #> A Pre-data Table Layout #> #> Column-Split Structure: #> Species (lvls) #> #> Row-Split Structure: #> Sepal.Length:Sepal.Width:Petal.Length:Petal.Width (** multivar analysis **) #> tbl2 <- build_table(lyt2, iris) tbl2 #> setosa versicolor virginica #> —————————————————————————————————————————————————————— #> Sepal.Length #> mean / sd 5.01 (0.35) 5.94 (0.52) 6.59 (0.64) #> range 1.50 2.10 3.00 #> Sepal.Width #> mean / sd 3.43 (0.38) 2.77 (0.31) 2.97 (0.32) #> range 2.10 1.40 1.60 #> Petal.Length #> mean / sd 1.46 (0.17) 4.26 (0.47) 5.55 (0.55) #> range 0.90 2.10 2.40 #> Petal.Width #> mean / sd 0.25 (0.11) 1.33 (0.20) 2.03 (0.27) #> range 0.50 0.80 1.10"},{"path":"https://insightsengineering.github.io/rtables/reference/analyze_colvars.html","id":null,"dir":"Reference","previous_headings":"","what":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","title":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","text":"Generate Rows Analyzing Different Variables Across Columns","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze_colvars.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","text":"","code":"analyze_colvars( lyt, afun, format = NULL, nested = TRUE, extra_args = list(), indent_mod = 0L, inclNAs = FALSE )"},{"path":"https://insightsengineering.github.io/rtables/reference/analyze_colvars.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","text":"lyt layout object pre-data used tabulation afun function list. Function(s) used calculate values column. list repped needed matched position columns tabulation. functions accepts parameters analyze like afun format. information see additional_fun_params. format FormatSpec. Format associated split. Formats can declared via strings (\"xx.x\") function. cases analyze calls, can character vectors lists functions. nested boolean. layout instruction applied within existing layout structure possible (TRUE, default) new top-level element (`FALSE). Ignored nest split underneath analyses, allowed. extra_args list. Extra arguments passed tabulation function. Element position list corresponds children split. Named elements child-specific lists ignored match formal argument tabulation function. indent_mod numeric. Modifier default indent position structure created function(subtable, content table, row) structure's children. Defaults 0, corresponds unmodified default behavior. inclNAs boolean. observations NA var variable(s) included performing analysis. Defaults FALSE","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze_colvars.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","text":"PreDataTableLayouts object suitable passing layouting functions, build_table.","code":""},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/reference/analyze_colvars.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","text":"Gabriel Becker","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/analyze_colvars.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Generate Rows Analyzing Different Variables Across Columns — analyze_colvars","text":"","code":"library(dplyr) #> #> Attaching package: ‘dplyr’ #> The following object is masked from ‘package:testthat’: #> #> matches #> The following objects are masked from ‘package:stats’: #> #> filter, lag #> The following objects are masked from ‘package:base’: #> #> intersect, setdiff, setequal, union ANL <- DM %>% mutate(value = rnorm(n()), pctdiff = runif(n())) ## toy example where we take the mean of the first variable and the ## count of >.5 for the second. colfuns <- list( function(x) rcell(mean(x), format = \"xx.x\"), function(x) rcell(sum(x > .5), format = \"xx\") ) lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by_multivar(c(\"value\", \"pctdiff\")) %>% split_rows_by(\"RACE\", split_label = \"ethnicity\", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = colfuns) lyt #> A Pre-data Table Layout #> #> Column-Split Structure: #> ARM (lvls) -> value:pctdiff (vars) #> #> Row-Split Structure: #> RACE (lvls) -> NA (** col-var analysis **) #> tbl <- build_table(lyt, ANL) tbl #> A: Drug X B: Placebo C: Combination #> value pctdiff value pctdiff value pctdiff #> ——————————————————————————————————————————————————————————————————————————————————————————————————————— #> ASIAN 79 (65.3%) 79 (65.3%) 68 (64.2%) 68 (64.2%) 84 (65.1%) 84 (65.1%) #> 0.1 45 0.2 35 0.3 41 #> BLACK OR AFRICAN AMERICAN 28 (23.1%) 28 (23.1%) 24 (22.6%) 24 (22.6%) 27 (20.9%) 27 (20.9%) #> -0.0 19 0.0 10 0.3 13 #> WHITE 14 (11.6%) 14 (11.6%) 14 (13.2%) 14 (13.2%) 18 (14.0%) 18 (14.0%) #> -0.1 6 -0.1 5 0.5 8 lyt2 <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by_multivar(c(\"value\", \"pctdiff\"), varlabels = c(\"Measurement\", \"Pct Diff\") ) %>% split_rows_by(\"RACE\", split_label = \"ethnicity\", split_fun = drop_split_levels ) %>% summarize_row_groups() %>% analyze_colvars(afun = mean, format = \"xx.xx\") tbl2 <- build_table(lyt2, ANL) tbl2 #> A: Drug X B: Placebo C: Combination #> Measurement Pct Diff Measurement Pct Diff Measurement Pct Diff #> —————————————————————————————————————————————————————————————————————————————————————————————————————————— #> ASIAN 79 (65.3%) 79 (65.3%) 68 (64.2%) 68 (64.2%) 84 (65.1%) 84 (65.1%) #> mean 0.10 0.54 0.18 0.50 0.26 0.50 #> BLACK OR AFRICAN AMERICAN 28 (23.1%) 28 (23.1%) 24 (22.6%) 24 (22.6%) 27 (20.9%) 27 (20.9%) #> mean -0.02 0.58 0.03 0.49 0.31 0.48 #> WHITE 14 (11.6%) 14 (11.6%) 14 (13.2%) 14 (13.2%) 18 (14.0%) 18 (14.0%) #> mean -0.10 0.47 -0.07 0.44 0.51 0.54"},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":null,"dir":"Reference","previous_headings":"","what":"Append a description to the 'top-left' materials for the layout — append_topleft","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"function adds newlines current set \"top-left materials\".","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"","code":"append_topleft(lyt, newlines)"},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"lyt layout object pre-data used tabulation newlines character. new line(s) added materials","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"PreDataTableLayouts object suitable passing layouting functions, build_table.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"Adds newlines set strings representing 'top-left' materials declared layout (content displayed left column labels resulting tables printed). Top-left material strings stored displayed exactly , structure indenting applied either added displayed.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":"note","dir":"Reference","previous_headings":"","what":"Note","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"Currently, construction layout called makes difference, independent actual splitting keywords. may change future. function experimental, name details behavior subject change future versions.","code":""},{"path":[]},{"path":"https://insightsengineering.github.io/rtables/reference/append_topleft.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Append a description to the 'top-left' materials for the layout — append_topleft","text":"","code":"library(dplyr) DM2 <- DM %>% mutate(RACE = factor(RACE), SEX = factor(SEX)) lyt <- basic_table() %>% split_cols_by(\"ARM\") %>% split_cols_by(\"SEX\") %>% split_rows_by(\"RACE\") %>% append_topleft(\"Ethnicity\") %>% analyze(\"AGE\") %>% append_topleft(\" Age\") tbl <- build_table(lyt, DM2) tbl #> Ethnicity A: Drug X B: Placebo C: Combination #> Age F M F M F M #> ————————————————————————————————————————————————————————————————————————————— #> ASIAN #> Mean 33.55 35.03 34.00 31.10 34.90 34.39 #> BLACK OR AFRICAN AMERICAN #> Mean 33.17 37.40 30.58 32.83 33.85 34.14 #> WHITE #> Mean 35.88 44.00 38.57 35.29 36.50 34.00"},{"path":"https://insightsengineering.github.io/rtables/reference/as_html.html","id":null,"dir":"Reference","previous_headings":"","what":"Convert an rtable object to a shiny.tag html object — as_html","title":"Convert an rtable object to a shiny.tag html object — as_html","text":"returned html object can immediately used shiny rmarkdown.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/as_html.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Convert an rtable object to a shiny.tag html object — as_html","text":"","code":"as_html( x, width = NULL, class_table = \"table table-condensed table-hover\", class_tr = NULL, class_th = NULL, link_label = NULL, bold = c(\"header\"), header_sep_line = TRUE, no_spaces_between_cells = FALSE )"},{"path":"https://insightsengineering.github.io/rtables/reference/as_html.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Convert an rtable object to a shiny.tag html object — as_html","text":"x rtable object width string indicate desired width table. Common input formats include percentage viewer window width (e.g. \"100%\") distance value (e.g. \"300px\"). Defaults NULL. class_table class table tag class_tr class tr tag class_th class th tag link_label link anchor label (including tab: prefix) table. bold elements table output bold. Options \"main_title\", \"subtitles\", \"header\", \"row_names\", \"label_rows\", \"content_rows\" (includes non-label rows). Defaults \"header\". header_sep_line whether black line printed table header. Defaults TRUE. no_spaces_between_cells whether spaces table cells collapsed. Defaults FALSE.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/as_html.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Convert an rtable object to a shiny.tag html object — as_html","text":"shiny.tag object representing x HTML.","code":""},{"path":"https://insightsengineering.github.io/rtables/reference/as_html.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Convert an rtable object to a shiny.tag html object — as_html","text":"","code":"tbl <- rtable( header = LETTERS[1:3], format = \"xx\", rrow(\"r1\", 1, 2, 3), rrow(\"r2\", 4, 3, 2, indent = 1), rrow(\"r3\", indent = 2) ) as_html(tbl) #> #>
#>
#>
<\/p> #> <\/div> #>
<\/div> #> <\/div> #>
#> #> <\/th> #> | A<\/th> #> | B<\/th> #> | C<\/th> #> <\/tr> #> |
#> r1<\/td> #> | 1<\/td> #> | 2<\/td> #> | 3<\/td> #> <\/tr> #> |
#> r2<\/td> #> | 4<\/td> #> | 3<\/td> #> | 2<\/td> #> <\/tr> #> |
#> r3<\/td> #> | <\/td> #> | <\/td> #> | <\/td> #> <\/tr> #> <\/caption> #> <\/table> #> |