Changes in version 0.9.0 (2026-03-15) New features - New function body_add_flextable2(): automatically adds a legend if the flextable object has a "legend" attribute. Equivalent functionality is now available in body_add_gg2(). (#100) - New argument rename_with_labels(cols), more intuitive than except. (#99) - New function ct_bind_cols() helper to combine (column-bind) two multi-by crosstables into a wider crosstable, enabling multi-column outputs with intra-group margins (experimental). (#97) - New argument as_flextable(allow_breaks=FALSE), to avoid breaking a table group in 2 pages. (#92) - New argument as_flextable(compact=TRUE, collapse), to collapse levels in compact format. (#98) For example, collapse="Yes" will yield a single row for the "Yes" level, instead of a redundant Yes/No that sums up to 100%. This will only apply if there are 2 levels only, NA included. See examples for more. Documentation - New vignette on advanced customization, demonstrating how to plug custom summary functions, effect estimators and statistical tests into crosstable(). (#112) - New hex logo! (#115) Bug fixes and improvements - Fixed ct_compact() so that .id is kept in the output. - Improved format_fixed() for more reproducible output across platforms. This may result in minor rounding differences. (#114) - Fixed bug in rename_with_labels() when except contains c().(#99) - Fixed bug in crosstable() when a factor variable had NA as a level (like after forcats::fct_value_to_na_level()). (#94) - Fixed as_flextable() so that compact=TRUE works in transposed (t()) crosstables (#91). - Fixed as_flextable() so that keep_id=TRUE works in multi-by crosstables (#88). Lifecycle - compact(), conflicting with purrr::compact() and deprecated if favor of ct_compact() since 2022, has been removed from the package. Changes in version 0.8.2 (2025-09-07) New features - New argument apply_labels(fn) to modify existing labels (#80). Bug fixes and improvements - fixed crosstable_options() sometimes not being taken into account (#90). - fixed error when using a named list of functions in crosstable(funs) (#89). - fixed rare infinite loop with get_label() on very specific objects like POSIXlt (#86). Changes in version 0.8.1 (2024-11-01) Bug fixes and improvements - fixed as_flextable(compact=TRUE) with duplicated labels (#87). Changes in version 0.8.0 (2024-09-30) New features - Remove percents when N=0 with crosstable(remove_zero_percent=TRUE) (#78). - Better header for multi-by flextables as_flextable(ct, remove_header_keys=TRUE) (#74). - Control font size with body_add_normal(font_size) and crosstable_options(normal_font_size) (#65). - Option control on height & width in body_add_gg2() (#68). Bug fixes and improvements - Add envir argument to functions that call glue() (#83). - Better error when x is NULL in body_add_table_section() (#82). - Warn if by is "" (#77). - Default to quiet warnings if columns are missing (#76). - Moved the legend above the table in body_add_table_section() (#73). - Fixed get_label() on named vector columns (#72). - Fixed crosstable(drop_levels) (#69, #70, #71). - Flextable autofit now uses the recommended method (#62). - transpose() will work on multi-by crosstables (#60). - Fixed a bug happening when NA is already a level (#59). - Fixed error where by contains "label" (#56). - Fixed as_flextable(header_show_n) not working in single by (#54). - Fixed some missing options in crosstable_options() and added tests (#53). Changes in version 0.7.0 (2023-11-12) New features - New officer helper body_add_table_section() that adds a table (crosstable or flextable) which can be surrounded by a title (level 3 by default), a legend, and/or a short sentence. - set_label() now accepts a function as a value (e.g. set_label(mtcars2, toupper)). - Argument format_fixed(scientific) can now be FALSE to force standard format. Can be set through options(crosstable_scientific_log=FALSE) (#49). - New argument format_fixed(epsilon) to format values as " And here is red text.") See ?body_add_normal for more insight. Bug fixes and improvements - Reimplement description of columns containing both NA (missing values) and "NA" (characters) (#42). - Argument as_flextable(by_header) can now be set through options(crosstable_by_header=FALSE) to remove all headers. - Argument body_add_crosstable(header_fontsize) now defaults to 1.2*body_fontsize. - Adds an error message in as_flextable() when the crosstable is empty (#41). - Fixes a regression where references were not added anymore. Changes in version 0.6.2 (2023-05-28) Bug fixes and improvements - Fixes for CRAN checks. - Fixes write_and_open() not opening sometimes. - Trailing commas will not make crosstable_options() fail anymore. Changes in version 0.6.1 (2023-04-04) Fixes for CRAN checks. Changes in version 0.6.0 (2023-03-26) New features - New function transpose_crosstable() (or simply t()), which transposes a crosstable so that by is in rows instead of columns.\ ct = crosstable(mtcars2, c(mpg, drat), by=am) t_ct = t(ct) as_flextable(t_ct, by_header="Variables") - New function pivot_crosstable(), which pivots a crosstable so that variable is spread as several columns.\ ct = crosstable(mtcars2, c(mpg, drat)) p_ct = pivot_crosstable(ct) as_flextable(p_ct) - body_add_table_list() now replaces body_add_crosstable_list() and body_add_flextable_list(). It allows both crosstables and flextable (and even dataframes, which are turned to flextables first) and has a new argument fun_after to control what comes after the table. See examples in ?body_add_table_list. - New function get_percent_pattern(). See also the new vignette vignette("percent_pattern"). - New argument crosstable(drop_levels=TRUE) to drop unused levels in factors. - copy_label_from() now works on dataframes as well. - as_flextable(header_show_n_pattern) can now be a list of names cell and total, so that the "Total" column can be labelled too. Breaking changes (minor) - showNA="no" is now consistent with stats::addmargins(), gtsummary::tbl_cross(missing="no"), and janitor::tabyl(show_na=FALSE). It now actually removes all NA from the equation, instead of not doing much (#24). - In percent_patern, the proportion relative to the total sample p_cell has been renamed to p_tot for clarity. Bug fixes and improvements - Removed unexpected warning "NaNs produced" thrown when calculating percentages in totals while the number of NA is higher than the other classes (#20). - The output is now correct when header_show_n=TRUE and remove_header_keys=TRUE while using multiple by variables (#21). - Flextables will not have missing columns when using multiple by variables that has same key levels. - String interpolation works as intended in body_add_xxx_legend(). - crosstable() will not fail if fisher.test() fails [#28] - forcats::fct_explicit_na() is not used anymore [#29] - body_add_normal() now removes ` symbols when showing code [#31] - Trailing commas will not make apply_labels() fail anymore [#32] - Performance improvement (around 30% for small tables) as confidence intervals are not calculated anymore when not needed [#34] Many thanks to Stephan Daus (@sda030) for his bug reports and feedback on this release. Changes in version 0.5.0 (2022-08-16) New features - New clean_names_with_labels() which cleans the names of the dataframe but keeps the old names as labels. Obviously inspired by {janitor}. - New variables n_col, n_row, and n_tot available for percent_pattern. Also, every variable has now its counterpart with the _na suffix which accounts for missing values. For instance, one can now write: crosstable(mtcars2, cyl, percent_pattern="{p_col} ({n}/{n_col}) [95%CI: {p_col_inf}; {p_col_sup}]") crosstable(mtcars2, cyl, percent_pattern="{p_col_na} ({n}/{n_col_na}) [95%CI: {p_col_inf}; {p_col_sup}]") - percent_pattern can now be a list of characters with names body, total_row, total_col, and total_all to also control the pattern in other parts of the crosstable than the body. Improvements - crosstable_options() does not need the "crosstable_" prefix anymore, so the autocomplete is less clumsy. Options with the prefix will still work though. crosstable_options(reset=TRUE) is deprecated in favor of the new crosstable_reset_options(). - crosstable_test_args() and crosstable_effect_args() now have arguments to easily control the non-default parameters. - Allow scientific notation for big numbers. Default to numbers for which abs(log10(x))>4. This can be controlled using options, e.g. crosstable_options(scientific_log=5). - In MS Word, crosstables will now break across pages by default. You can revert this by using body_add_crosstable(allow_break=FALSE) or using crosstable_options(). This is the pendant of keepnext in officer/flextable. - New argument body_add_crosstable(max_cols=25), which limits the size of crosstables in Word documents. This prevents very large tables to be wrongly included. - peek() is now usable on non-crosstable objects as well. as_flextable() method will be applied on the object if available, otherwise flextable() will be applied. - Better error messages in import_labels() when data_label doesn't have the right columns. Deprecations - rename_dataframe_with_labels() is now deprecated in favor of the better named rename_with_labels(). - compact() is now deprecated in favor of ct_compact() to avoid the conflict with purrr::compact(). It will be removed in the next version. Bug fixes - body_add_crosstable(..., parse=NULL) will now work as intended . - by_header can replace the "value" column if by has only one stratum, in both as_gt() and as_flextable() (https://github.com/DanChaltiel/crosstable/issues/9). - percent_pattern can now contain functions that start by "p" (https://github.com/DanChaltiel/crosstable/issues/10). - percent_pattern is actually taken into account when by has one single level (https://github.com/DanChaltiel/crosstable/issues/13). - import_labels() now works even if there is a missing value in the dataframe. - Minor other bugfixes Changes in version 0.4.1 (2022-02-25) - Maintenance release to fix CRAN errors. Changes in version 0.4.0 (2022-02-14) New features - You can now use basic markdown formats in body_add_normal(): **bold**, *italic*, _underlined_, and `code`. - New keep_id argument in compact.crosstable(), which enables as_flextable(compact=TRUE, keep_id=TRUE) - You can use as_flextable(header_show_n_pattern) to change the glue pattern of these group sizes. The default is {.col} (N={.n}); you can use {.col_key} and {.col_val} when by has multiple strata. - Functions body_add_table_legend() and body_add_figure_legend() earn an argument legend_prefix. Useful when set through global options, for instance to make every table/figure start with the name of the study. - crosstable_options() can handle new options for all those new features. Improvements - You can now set normal style directly in body_add_normal() (it was only doable through options before). - Removed the "variable" header in compacted crosstables. - Dataset mtcars2 is now a tibble, with its rownames as a column named "model". - as_flextable(by_header=FALSE) now removes the group header (if by has only one stratum). - as_flextable(header_show_n) now also works if by has multiple strata. - as_flextable(header_show_n=TRUE) adds group size for all groups. Bug fixes - Fixed a bug when numeric variables are treated as categorical (failed if one had a NA value). - Fixed a bug that caused body_add_normal() to add an extra empty paragraph if there was a reference in the text. - Fixed a bug so that body_add_normal() can be used without argument. - Fixed a bug in effect=TRUE when some groups were present in table() but not in glm() due to missing values. Changes in version 0.3.2 (2021-11-27) New features - Global options management is now easier thanks to crosstable_options() and autocompletion. Almost every argument can now be set using options. See ?crosstable_options for further details. - You can also use crosstable_peek_options() to see which crosstable option is currently set. - New argument num_digits in crosstable(). It was about time! - New argument header_show_n for as_flextable(), which adds the group size (N=xx) to the header of the flextable. - New arguments (and global options) par_before and par_after in respectively body_add_table_legend() and body_add_figure_legend(), which add an empty paragraph before/after the legend (for readability). - New function for {officer}: body_replace_text_at_bkms(), to replace several bookmarks at once. - New global option crosstable_options(crosstable_zero_percent=FALSE), which removes percentages whenever n==0 (as it would always be 0%). I should add it as an argument of crosstable() one day... Improvements - Better sorting when numeric variables are treated as categorical (before, 10 was sorted before 2). - Removed the labelled class which caused too many problems for little to no improvement. See https://github.com/larmarange/labelled/issues/111. Bug fixes - fixed a bug in crosstable() occurring when one of funs does not have ellipsis (...) and funs_arg contains an unused argument. Changes in version 0.3.1 (2021-11-14) New features - New parameter percent_pattern in replacement of crosstable(margin=x) for better control over proportion format. Introduces the possibility of displaying confidence intervals (using Wilson score method) along with proportions. - New function body_add_crosstable_list() to add a list of crosstables all at once, separated by some customizable titles. Also works with flextables and plain old dataframes. - New argument crosstable_padding_v for as_flextable() to manage vertical padding. Also available as the global option crosstable_padding_v. - New global options crosstable_{arg} for almost all arguments. See ?crosstable_options for more details. Bug fixes - fixed a bug in as_flextable() occurring when showNA=TRUE (header row was disappearing) - fixed a bug occurring very randomly, when rounding caused format_fixed() to return a numeric value ("Error: Can't combine ..1$value and ..2$value .") - crosstable now shows all unused levels in by when it is a factor - removed extra bold columns in compacted crosstables displayed as flextables Internal - renamed branch master to main - use a lot more snapshots in tests Changes in version 0.2.2 (2021-10-19) New features - Added support for multiple by! You can now write crosstable(mtcars, c(mpg, gear), by=c(am, vs)) %>% as_flextable(). - Added a macro that can autofit every table in the document at once. This macro can be generated using the function generate_autofit_macro() which creates a file that should then be imported into MS Word. - body_add_crosstable() gains a padding_v argument to control the vertical padding of all rows. - body_add_title() and body_add_xxx_legend) gain a glue functionality. You can now write body_add_title("The iris dataset (nrow={nrow(iris)})", 1). - as_workbook() can now take a named list of crosstables, that will be considered as sheets. - New parameter percent in format_fixed(percent=TRUE/FALSE) to easily format percentages. Minor changes - style is deprecated in body_add_table_legend() and body_add_image_legend() in favor of name_format. - Changed the behaviour of some effect calculations that were done by column instead of rows. That might change some outputs but not their meaning. - body_add_normal() now removes duplicated spaces (squish) in its input by default. Use squish=FALSE to override. - docx_bookmarks2() gains a target parameter. Bug fixes - effect calculation now takes into account the reference level (first level of a factor). - body_add_crosstable() rightly takes body_fontsize and header_fontsize into account. - Added few more warnings, so that you know what went wrong. Internal - burgled 2 functions using burglr::burgle() to avoid dependency: nortest::ad.test() and DescTools::CochranArmitageTest(). - fixes the bug from the breaking change in testthat (https://github.com/DanChaltiel/crosstable/pull/3). Changes in version 0.2.1 (2021-03-08) - First version on CRAN - Improved functions naming in funs, especially with multiple combinations of named and unnamed functions, including lambda or anonymous - Use simplify=FALSE in get_label() to get a list instead of a vector Changes in version 0.2.0 Misc - added lots of global options for easier implementation. See ?crosstable_options for the comprehensive list. - added label helpers: apply_labels() (inspired by expss's), copy_label_from() and rename_dataframe_with_labels() - added as_workbook() to export a crosstable as a formatted openxlsx Excel workbook, for copypasting purpose. - added peek() to open a crosstable in a temporary Word document, as copy-pasting in RStudio's viewer is very limited. - fixed the bug when a columns contained both "NA" (string) and (missing). - fixed the bug where function in funs was not found if declared in another environment. - numerous other minor bugfixes and internal improvements. Officer - added cross-reference functionality to body_add_figure_legend() and body_add_table_legend(). Use the bookmark argument to set a reference, then write "\\@ref(my_bkm)" inside body_add_normal() to call it. - added docx helpers to add lists: body_add_list() and body_add_list_item(). These will unfortunately not work with the default officer template. - added some alternatives for some officer functions: - docx_bookmarks2(), which list bookmarks found in the header and footer as well - body_add_img2(), and body_add_gg2(), which win a units=c("in", "cm", "mm") argument - write_and_open(), an alternative to print() for documents, which tries to open it right away. Deprecations - Ellipsis (...) use in crosstable() has been deprecated for a more "tidy" syntax. Write crosstable(mtcars2, c(disp, vs)) instead of crosstable(mtcars2, disp, vs). Ellipsis will be defunct in future v1.0. - crosstable(.vars=) has been renamed to crosstable(cols=). - moystd() has been renamed to meansd(). - body_add_glued() has been superseded by body_add_normal(), which inherits all functionalities and more. Changes in version 0.1.5 - added minimal support for gt tables (with as_gt()) for those who like them better than flextables - improved working with officer: added body_add_figure_legend() and fontsize options for body_add_crosstable() Changes in version 0.1.4 - added save_labels() to ease working with dplyr - added meanCI() an additional summary function to use in crosstable()'s funs argument - improved support for Date variables - multiple, numerous bug fixes - renamed moystd() to meansd() Changes in version 0.1.3 - Added support for description of Date variables. Format can be specified in funs_arg with the date_format key. - Removed some dependencies to ease installation Changes in version 0.1.2 - Effect refactoring: better error/warning handling - Name sanitation: replacing "." by "_" in function names - Better error messages - Bug fixes Changes in version 0.1.1 New features and behaviors - Added format_fixed(), rounding with the right number of decimals (including zeros) - Added import_labels(), which apply labels taken from a source dataframe (name, label) to another dataframe - Added margin="none" option, to remove percentages and keep only counts - Columns of unsupported class are dropped with a warning instead of failing with an error Misc - Method cross_to_flextable() (ctf()) was deprecated and renamed as_flextable() (#207) - Reexporting pipes and tidyselect helpers so that user does not have to load these libraries - Computing time optimization (speed x2.6!) - Fixed bug in normality testing - Fixed bug in compact() Changes in version 0.1.0 - First release, big changes from the biostat2 package.