# qol 1.3.4

### New functions

* `create_table_of_contents()`: Creates a table of contents sheet with custom style based on the provided 'Excel' workbook. (02.08.2026, thanks to @JanMarvin)
* `check_required_package()`: Check whether a certain package is required and ask the user whether it should be installed or not. (08.08.2026)
* `run_script()`, `run_folder()`, `run_project()`, `run_project_parallel()`: RStudio helper functions that run scripts, folders or whole folder structures via diolog selection. (13.08.2026)

### New functionality

* `load_file()`, `load_file_multi()`: When passing a named vector or list into the `keep` parameter, the original variables will directly be renamed. (18.07.2026)
* `any_table()`: Can now handle duplicate column names by making them unique (except NA columns). (18.07.2026)
* `frequencies()`, `crosstabs()`: Now also receive a colored background. (22.07.2026)
* `import_multi()`: Received a new paramter `stack_data` which can stack read in files and return them as a single data frame. (22.07.2026)
* `import_multi()`: Can now handle a vector of sheet names and import only the specified ones. (22.07.2026)
* `multi_join()`: Variable names in the `on` parameter can now be passed without quotation marks. (23.07.2026)
* `remove_stat_extension()`: Statistic extensions can now be passed without quotation marks. (24.07.2026)
* `set_style_options()`: Parameters, which are originally NULL, can now be reset by assigning a value of NULL. (24.07.2026)
* `combine_into_workbook()`: Can now create a custom styleable table of contents. (01.08.2026, thanks to @JanMarvin)
* `excel_output_style()`: Added parameters for the custom styleable table of contents. (01.08.2026)
* `any_table()`: When using `by` variables the special keyword `[by_var]` is now replaced with the actual `by` value in the titles and footnotes. (01.08.2026)
* `frequencies()`, `crosstabs()`: Now also export the Excel workbook. (02.08.2026)
* `frequencies()`, `crosstabs()`: When using `by` variables the special keyword `[by_var]` is now replaced with the actual `by` value in the titles and footnotes. (01.08.2026)
* `where.()`: Added a html rendered view of the data frame as fallback, if the new window through `utils::View()` can't be opened. (03.08.2026)
* `where.()`: Is now also able to use the new writing style with conditions as characters introduced by `ifelse_multi()`. (04.08.2026)
* `any_table()`: Can now render the tables as html file and show it in a browser window. This can be controlled via the new `output` options `html` and `excel_html`. The function now also returns an additional html element. (07.08.2026)
* `load_file()`, `load_file_multi()`: Can now load fst files `by_reference`, which means files are not loaded into memory and instead the necessary values are only loaded on demand. (08.08.2026)
* `any_table()`, `frequencies()`, `crosstabs()`: Added new `full_precision` parameter, which ignores the decimal places given through the `style` parameter and outputs all values with all their decimal places. (10.08.2026)
* `recode.()`: Can now recode multiple variables into multiple new ones. (10.08.2026)
* `any_table()`: Formats can now be applied to pre summarised data. (14.08.2026)
* `rename_multi()`: Can now rename variables stored in vectors when passing them like: old_vektor = new_vektor. (14.08.2026)
* `load_file()`, `load_file_multi()`: New `keep_var_order` parameter enables to keep the original stored variable order, when using keep variables, instead of sorting them in provided order. (14.08.2026)
* `load_file()`, `save_file()`: `where` now works with old and renamed variable names. Additionally the parameter can now handle the new writing style with conditions as characters introduced by `ifelse_multi()`. (14.08.2026)
* `save_file()`: When passing a named vector or list into the `keep` parameter, the original variables will directly be renamed. (14.08.2026)
* `summarise_plus()`, `any_table()`, `frequencies()`, `crosstabs()`: The `print_miss` parameter now also has an effect when not using formats. (17.08.2026)

### Changed functionality

* `combine_into_workbook()`: With the function making use of the new `style` parameter, the `file` parameter has been removed. Saving files no works with the `style` parameter like in the other tabulation functions. (03.08.2026)
* `retain_value()`: Without by variable the function now carries forward values through upcoming NA values instead of just writing the first value into all other cells. (08.08.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: The functions now set the font family per cell instead of a base font for the whole workbook. This enables different fonts per sheet in multi sheet workbooks, if the sheets are created with separate calls. (08.08.2026)
* `recode_multi()`: `convert` parameter is now set to TRUE by default. (11.08.2026)
* `build_master()`: Uses a new compact layout as default. Additionally the full layout can now only be created as a whole. (13.08.2026)

### Removed

* `get_integer_length()`: There is no real benefit in this function. (18.07.2026)

### Fixed

* `any_table()`: Variables generated with the `compute` parameter now receive a default `statistics` extension, if they are missing one, so that the column header is generated correctly and a number format is applied. (17.07.2026)
* `mark_case()`: When marking `last` cases and the last expression within the `by` variable was NA, then the last case in the second to last expression became NA instead of TRUE. This is fixed now. (18.07.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: When different title or footnote styles where used, they were only applied, if multiple colors where used. Otherwise the formatting broke. This is fixed now. Additionally the code is now simplified and streamlined. (22.07.2026)
* `any_table()`, `frequencies()`, `crosstabs()`: The additional `by` title is now also taken into account with individual styling, when multiple titles are present. (22.07.2026)
* `import_data()`: Fixed a Problem with encoding. (22.07.2026)
* `close_file()`: Now sets the `file` entry in the global list of style elements actually to NULL instead of removing it. (24.07.2026)
* `any_table()`, `summarise_plus()`: Now keep on running and throw a warning, if a character variable is passed as `values`, instead of throwing a cryptic error. (30.07.2026)
* `import_multi()`: Ran into an error when trying to import a not existing Excel workbook. Now the file path is checked before. (30.07.2026)
* `convert_factor()`: Now uses `unique()` instead of `collapse:unique()` to identify german umlauts as they are seen on screen and not as they are stored in memory, which caused errors. (31.07.2026)
* `frequencies()`: When using interval formats without weight, the sum columns where displayed even though they were equal to the freq columns. This is fixed now. (31.07.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Removed the extra empty row over the tables when no titles where set. Additionally there are no more empty named regions for titles and footnotes, if they are not present. (02.08.2026)
* `combine_into_workbook()`: Now uses the global options for `print` and `monitor` parameter. (02.08.2026)
* `any_table()`: `pct_block` values are now rounded. (07.08.2026)
* `where.()`: Now shows the full data frame when condition is NULL instead of throwing an error. (08.08.2026)
* `if.()`: The function crashed when NA was assigned to a variable. NA is now assigned. (08.08.2026)
* `ifelse_multi.()`: When NA was assigned to a variable, all values became NA instead of just the ones for which the condition was TRUE. This is fixed now. (08.08.2026)
* `any_table()`: The column header got corrupted, if value variables had underscores in their names and were used in combination with `pct_block`. This is fixed now. (10.08.2026)
* `any_table()`: When using the `pct_block` parameter and formats have overlapping values, then they now will be kept in user provided order. (10.08.2026)
* Every function, that is able to apply formats, now keeps original values if they are not captured by the applied formats instead of setting uncovered values zo NA. (11.08.2026)
* `recode.()`: Now can also handle the "other" keyword. (11.08.2026)
* `any_table()`: `pct_block` percentages are now calculated on unrounded sums, to have maximum precision. (11.08.2026)

### Additionally

* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Extended `titles` and `footnotes` description according to the new functionality. (22.07.2026)
* `frequencies()`, `crosstabs()`: Added some new messages on the styling progress. (22.07.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Added named regions for titles, main title, footnotes, table header and row headers. (22.07.2026)
* Cleanup of a partial matches. (22.07.2026, thanks to @JanMarvin and @olivroy)
* `stack_data()`: Swapped out a copypasta error message with the right one. (22.07.2026)
* `combine_into_workbook()`: Added example on how to save a file. (03.08.2026)
* `dummy_data()`: `NUTS2` variable is now stored as an integer. (03.08.2026)
* `load_file()`: Added example which shows how to directly rename variables. (03.08.2026)
* `dummy_data()`: `NUTS3` variable is now stored as an integer. (05.08.2026)
* New suggested package `yyjsonr` added, which is required, for the new html view of `where.()`. (08.08.2026)
* `rename_multi()`: Clarified error message on old variable names not found in data frame. (08.08.2026)
* `any_table()`: Updated `pct_block` parameter description and examples to make it more clear how this parameter works and has to be used. (10.08.2026)
* `any_table()`: Now uses html output as fallback in case Excel is not available. (13.08.2026)
* Minimum `openxlsx2` version is now 1.29, which fixes a bug that caused damaged output when tabulating with by-Variables and `print_miss`. (20.08.2026)

# qol 1.3.3

CRAN release on 16.07.2026

### New functions

* `ifelse_multi()`: A variation of the `ifelse()` function which can handle multiple conditions in one go. The function takes in the conditions as characters. The conditions are parsed before evaluation to enable SAS like writing styles. (20.06.2026)

### Renamed functions

* `stack_data()`: `set` was renamed to prevent masking errors in combination with `data.table`. (03.07.2026)

### New functionality

* `if.()`, `else_if.()`: Are now also able to use the new writing style with conditions as characters introduced by `ifelse_multi()`. (20.06.2026)
* `any_table()`: With the new `compute` parameter all possibilities of the `compute.()` function can be used within `any_table()`. (26.06.2026)
* `any_table()`: `order_by` parameter now allows to input a vector of variable names. (26.06.2026)
* `any_table()`, `summarise_plus()`: Variable combinations in rows, columns and types can now also take in variable combinations inside the brackets like "state + (age, sex + education, first_person)". (26.06.2026)
* `All functions with filepaths`: Wherever a file path or name can be passed, macro variables can be used inside the path or name. (06.07.2026)
* `any_table()`, `summarise_plus()`: It is now possible to specify per variable which statistics should be output. Instead of passing a vector of statistics into the `statistics` parameter, it can now also be a named list of variable names, where the list entry names are the statistics and the list elements the variable names or vectors of variable names. (07.07.2026)

### Changed functionality

* `any_table()`, `summarise_plus()`: Variable combinations in rows, columns and types like "state + (age, sex + education, first_person)", now have to use commas as separators in the brackets. (26.06.2026)
* `import_data()`: Now uses Latin-1 encoding. (01.07.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Now the whole background is filled white as default. The new default is set in `excel_output_style()`. (03.07.2026)
* `any_table()`: When `by` variables are provided that are also part of the `row` or `column` variables, the function now throws a warning instead of an error and keeps running without by variables. (07.07.2026)

### Fixed

* `multi_join()`: Before joining it is now checked whether there will be duplicate variable names after the join. If so, these variables will be dropped before joining. Otherwise it was possible that a variable could show up in the final data frame with the exact same name multiple times. (21.06.2026)
* `any_table()`: The statistic `sum_wgt` couldn't be renamed correctly. It can now be renamed by just doing `stat_labels = list("sum" = "Some label")`. (26.06.2026)
* `stack_data()`: When a data frame and a list was input, the function tried to hack the list into one of the data frame columns. Now all lists are flattened and data frames extracted to prevent this behaviour. (03.07.2026)
* `print_start_message()`: Now doesn't reset start timer anymore when message is suppressed. (04.07.2026)
* `print_start_message()`: Doesn't reset message stack  anymore when called in a deeply nested situation, which was caused by the optimization introduced in the last update. (06.07.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Footnote heights are applied again. (06.07.2026)
* `export_with_style()`: Excel number stored as text error is now ignored for the whole table. (06.07.2026)
* `interval_format()`: `include_lower` now works as intended and doesn't include the lower bound anymore. (07.07.2026)

### Optimization

* `summarise_plus()`: Brought percentiles up to more speed. (17.06.2026)
* I underestimated how big in size the call stack can get depending on what functions are called. Therefore the message stack now doesn't receive the full call stack per message anymore but a condensed version and just as a list of character strings. (04.07.2026)
* `summarise_plus()`: If types are selected, the loop to calculate all the combinations is now aborted early, if the maximum specified combination depth is surpassed. (07.07.2026)

### Additionally

* `multi_join()`: Now displays the actual data frame names to be joined instead of just an iterative number in the console. (21.06.2026)
* `any_table()`: Received some additional messages for computing stats. (26.06.2026)
* `any_table()`: Received some additional messages for computing stats. (26.06.2026)
* `summarise_plus()`: Added example for type combinations. (26.06.2026)
* Minimum `openxlsx2` version is now 1.28, which allows a faster background fill. (03.07.2026)
* `load_file_multi()`: Added a message when files are stacked. (04.07.2026)
* `any_table()`, `frequencies()`, `export_with_style()`: Output from these functions received a class name. (07.07.2026)

# qol 1.3.2

CRAN release on 16.06.2026

### New functions

* `code_statistics()`: Reads in a folder or entire folder structure, grabs all R script files, scans the contents for different patterns and outputs a small report. (04.06.2026)

### Renamed functions

* `retain_stat()`: `retain_sum` got a new name because it can now generate any statistic not only sums with the new `statistics` parameter. (11.06.2026)
* `set_labels()`, `get_labels()`: The global setters and getters for variable and statistic labels `set_variable_labels()`, `get_variable_labels()` and `set_stat_labels()`, `get_stat_labels()` have been put together in one function. (12.06.2026)

### New functionality

* `running_number()`: With the new `sort` parameter a vektor of variable names can be passed as `by`, which then is automatically sorted before generating the running number. There is also a new `group_nr` parameter which creates a running number for each group as a whole instead of a running number within the group. (26.05.2026)
* `concat()`: Can now concatenate variables inside a data frame with a separator. (03.06.2026)
* `recode_multi()`: New `convert` parameter converts recoded variables to numeric or character depending on the input format instead of leaving them as factors. (03.06.2026)
* `any_table()`, `excel_output_style()`: Can now apply a `background_color` to all cells which aren't covered by any other background color parameter. (03.06.2026)
* `frequencies()`: The `number format` option `sum_decimals` within the `excel_output_style()` function can now be used to set the decimal places in text outputs as well. (04.06.2026)
* `libname()`: With the new `recursive` parameter files can now also be retreived from subfolders. (04.06.2026)
* `export_with_style()`: Can now apply a `background_color` to all cells which aren't covered by any other background color parameter. (11.06.2026)
* `export_with_style()`: Added a new `column_align` parameter with which every column alignment can be controlled individually. (11.06.2026)

### Changed functionality

* `compute.()`: Now handles calls sequential, so that a new variable generated within `compute.()` can directly be accesses and used within the same `compute.()` call. (13.06.2026)
* `compute.()`: In a do over loop, if different values are assigned to the same variable, then now the last value is assigned instead of the first. (16.06.2026)
* `summarise_plus()`: Percentiles are now calculated the SAS way, which is Type 2 described in `quantile()`. (16.06.2026)

### Fixed

* `if.()`, `else_if.()`, `else.()`: Do over loop couldn't handle logical vectors. Additionally character expresions where always converted to symbols, which was fine if variable names were meant, but crashed if actual character values were passed. This is fixed now. (01.06.2026)
* `print_step()`: Doesn't print empty lines anymore, when surrounding the function with `suppressMessages()`. (01.06.2026)
* `sort_plus()`: Doesn't crash anymore when trying to preserve factor variables. (03.06.2026)
* `export_data()`: When exporting a csv file characters like ä, ö, ü are displayed correctly, when opening the file in Excel. (03.06.2026)
* `libname()`: When using the `extensions` parameter then now only files with these extensions are printed out to the console. Otherwise a `NOTE` is displayed stating that no file has been found. (04.06.2026)
* `if.()`, `else_if.()`: If a colon ":" was written somewhere in a character expression inside a condition the function crashed because it was looking for the return value of the special ":" symbol. This is fixed now. (09.06.2026)
* `export_with_style()`: Function doesn't crash anymore, if a variable label is provided, but the corresponding variable doesn't exist in the data frame. (11.06.2026)
* `dummy_data()`: Variable `number_of_persons` now has the right values. Before it was generated too early. (11.06.2026)
* `compute.()`, `if.()`, `else_if.()`, `else.()`: If do-over-loop behavior is combined with normal variable generation, then the do over loop now doesn't run for the normal variable generation and condition set up anymore. (13.06.2026)
* `any_table()`: The statistic `freq_g0` couldn't be renamed correctly. It can now be renamed by just doing `stat_labels = list("freq" = "Some label")`. (15.06.2026)
* `if.()`, `else_if.()`, `else.()`: If the variable to which a value should be assigned already existed in the data frame, then the existing values were overwritten with NA, if there were NA values in a variable that was used in the condition. Values are now preserved. (16.06.2026)

### Optimization

* `if.()`: Got rid of an unnecessary loop. (13.06.2026)
* `print_step()`, `print_start_message()`, `print_headline()`, `print_closing()`: Execution token is now only retrieved, if the print function is called from a low depth. Meaning deeply nested print statements wont retrieve the token because this becomes very resource heavy. (13.06.2026)
* `summarise_plus()`: Tackled a performance nightmare in the core summarisation function. Basically every statistic operation is now up to 10+ times faster than before and uses less than half the memory. This also has an impact on all the tabulation functions. (14.06.2026)
* `any_table()`: The cell merging for the column and row headers was handled range by range, meaning a merge per range. Now the ranges to be merged are collected first and then merged in one go. This gives a massive performance gain for larger tables. (15.06.2026)

### Additionally

* `if.()`, `export_with_style()`: Updated examples. (19.05.2026)
* `summarise_plus()`, `drop_type_vars()`: Updated documentation with a detailed description of the automatically generated variables TYPE, TYPE_NR and DEPTH when using the `nesting = "all"` or `nesting = "single"` option. (27.05.2026, thanks to @cyklee)
* `expand_formats()`: Added a `NOTE`message if the cartesian product produces over 1 million rows. (02.06.2026)
* `any_table()`: Added two new `NOTE` messages in case `pct_value` or `pct_block` is specified in statistics put corresponding parameter isn't set. In this case the statistic will be removed. (08.06.2026)
* `export_with_style()`: Added some more messages informing the user about the formatting timings. (11.06.2026)
* Global options documentation received some more comments. Additionally the functions themselves received some messages stating whether options have been set. Also the getters which retrieve a list have a nicer output. (12.06.2026)

# qol 1.3.1

CRAN release on 16.05.2026

### Renamed functions

* `compute.()`, `recode.()`: Both functions have been renamed and now have a "." at the end to prevent masking errors in combination with `dplyr`. (28.04.2026)

### New functionality

* `if.()`, `else_if.()`: The ":" can now be used in conditions as a placeholder for "starts with" ("text:"), "ends with" (":text") and "contains" (":text:"). (21.03.2026)
* `set_no_color()`: Suppresses the color codes so that messages can be printed clean. The option is auto controlled on load via the system variable `NO_COLOR` but can also be set individually by this function. Console output in e.g. RStudio vs. output to a logging system should be handled automatically rightnow. (25.04.2026, thanks to @TroyHernandez)
* `set_up_custom_message()`: Waiting symbols as well as the color of the time stamps can now be customized. (25.04.2026)
* `print_step()`: Now has a new `in_place` parameter, which prints the message on the same line as before, instead of in the next line. This can e.g. be used inside loops as follows. (29.04.2026)
* `any_table()`, `export_with_style()`: If the whole result list from these functions is passed for the `workbook` parameter, the functions now are able to extract the actual workbook from the list and run without error. Additionally if a list is passed, which is not a result list containing the workbook, the functions error and abort execution. (05.05.2026)
* `transpose_plus()`: In a wide to long transposition it is now possible to not only put results below each other, but also side by side. (11.05.2026)
* `any_table()`, `frequencies()`, `crosstabs()`: If 'csv' is specified as extension in the `file name` set in the global options or the style parameter the result table will then be exported as 'csv'. Otherwise the actual workbook will be exported as `xlsx` as normal. (13.05.2026)
* `if.()`: If the new `delete` keyword is passed instead of a variable assignment, the provided condition deletes observations instead of keeping them. (15.05.2026)

### Changed functionality

* `set_up_custom_message()`: The way custom messages are set up has slightly changed and will break existing code. Custom message types are now stored globally and are called within quotation marks like all the other built-in message types. See updated README for the new handling. (25.04.2026)
* `interval_format()`: The function had a flaw in that it always included the lower and upper bound values. The "workaround" in the examples to pass bounds like e.g. 0:499, 500:999, would miss values between 499 and 500 or 999 and 1000. The function now got two new parameters `inlcude_lower` (TRUE) and `include_upper` (FALSE), which by default read as "from ... to under ...". Which means bounds can be passed like 0:500, 500:1000 without overlapping. (07.05.2026)
* `recode.()`: Interval formats can now be applied even though a variable has NA values. NA values are taken care of automatically instead of the function aborting with an error. Additionally multilabels are reduced so that no additional observations are created. (08.05.2026)

### Fixed

* `combine_into_workbook()`: Titles and footnotes are now styled again. (18.04.2026)
* `compute.()`: If a variable was all NA, a type miss match happend, leading to the function beeing aborted. This is fixed now. (24.04.2026)
* `print_step()`: Waiting character "?" is now drawn as intended in non-utf8 mode. (25.04.2026)
* `compute.()`, `if.()`, `else_if.()`, `else.()`: Now detect vectors for do-over-loops in every place. Previously do over loop was only detected, if the variable for assignment was a vector. (30.04.2026)
* `compute.()`: Doesn't crash anymore, if multiple values are assigned to the same variable in a do over loop. (30.04.2026)
* `dummy_data()`: Variable `income_class` was miss matched in some places due to it being generated to early. This is fixed now. (30.04.2026)
* `any_table()`: If the `block` keyword was used in `var_labels` parameter and all `block` keywords weren't written in lower case, the column header generation crashed. This is fixed now. (04.05.2026)
* `interval_format()`: Now preserves single values. (08.05.2026)
* `dummy_data()`: `person_id` is now adjusted after taking sample to have continuous numbers. (08.05.2026)

### Additionally

* Message time stamps are now drawn in a lighter grey to enhance visibility on darker themes. (20.04.2026)
* `else_do()`, `end_do()`, `end_all_do()`: Functions now throw errors instead of warnings, if `do_if()` is missing. (23.04.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: If only the `save_path` or the `file name` is specified in the global options or the style parameter there will now be a note stating that something is missing. (13.05.2026)
* `if.()`, `else_if.()`: `&&` and `||` will now be converted into `&` and `|`. Additionally a warning is displayed. (15.05.2026)
* Set the used threads from data.table to 2 for the `summarise_plus()` examples, because the CRAN server throws a NOTE for the r-devel-linux-x86_64-debian-gcc: "Examples with CPU time > 2.5 times elapsed time". (16.05.2026)

# qol 1.3.0

CRAN release on 18.04.2026

### New functions

* `row_calculation()`: Perform row wise calculations on numeric variables. (12.03.2026)
* `do_if()`, `else_do()`, `end_do()`, `end_all_do()`: These functions create overarching filter variables. When used with functions capable of handling these filter variables, it is possible to create more readable if-blocks without writing the same condition over and over again. (21.03.2026)
* `compute.()`: Compute new variables without having to write the name of the data frame multiple times. (23.03.2026)
* `save_file()`, `save_file_multi()`: Saves fst and rds files. Offers variable selection and observation subsetting. By default the function has a write protection, which has to be explicitly turned off to be able to overwrite files. (28.03.2026)
* `load_file()`, `load_file_multi()`: Loads fst and rds files. Provided variables to keep are read in case insensitive and are returned in provided order. Additionally a subset can be defined directly. Loaded files in the multi version can be stacked or output as a list. (28.03.2026)
* `set_threads()`, `get_threads()`: Globally sets/gets the number of used threads for the save and load file functions. (28.03.2026)
* Base R message system:
	* `print_message()`, `print_headline()`, `print_start_message()`, `print_closing()`, `print_step()`: Print out different messages with custom styling to the console. (09.04.2026)
	* `get_message_stack()`: Get the global message stack for inspection. (09.04.2026)
	* `set_no_print()`: Prevents messages being printed to the console globally. Can e.g. be used in unit test situations. (09.04.2026)
	* `print_stack_as_messages()`: Print the global message stack as message, warning or error to be able to receive their signals. (09.04.2026)
	* `convert_square_brackets()`: Transforms the format symbols (like {b}{/b}) into the actual console readable formattings. (09.04.2026)
	* `set_up_custom_message()`: Sets up the basic items for a custom message. These custom types can be input into `print_message()` and `print_step()`. (13.04.2026)
* `hex_to_256()`, `hex_to_ansi()`: Generate a 256-color 6x6x6 color cube and apply hex color and font weight to a text as ansi codes. (09.04.2026)
* `round_multi()`: Rounds multiple variables at once inside a data frame. (09.04.2026)
* `report_test_results()`, `test_package()`, `test_single_file()`: Print out tinytest results with custom reporting style. (15.04.2026)

### New functionality

* `if.()`, `else_if.()`: The ":" can now be used in conditions as a placeholder for "starts with" ("text:"), "ends with" (":text") and "contains" (":text:"). (21.03.2026)
* `if.()`, `else_if.()`, `else.()`: All functions are now able to handle vectors in a do over loop. Meaning, that if using vectors in conditions or assignments, the functions will iterate over each vector simultaneously. (21.03.2026)
* `if.()`, `else_if.()`, `else.()`: Can be used within the new `do_if()` context. (21.03.2026)
* `if.()`, `else_if.()`, `else.()`: Since the functions now make use of the new `compute.()`, they are able to do more than just simple variable assignments and can also perform calculations. (23.03.2026)
* `row_calculation()`: Now has an optional rounding parameter. (23.03.2026)
* `dummy_data()`: Added `weight_per_year` variable. (25.03.2026)
* `build_master()`: Added `with_monitor` parameter, which allows to visualize the time consumption of the individual scripts. (26.03.2026)
* `split_by()`: Now also outputs NA values. (29.03.2026)
* `import_data()`, `import_multi()`, `export_data()`, `export_multi()`: Now can import and export csv files with file extension txt. (30.03.2026)
* `round_values()`: Now is also able to round in multiples of a provided value. (09.04.2026)
* `combine_into_workbook()`: Now can handle `export_with_style()` results. (10.04.2026)
* `excel_output_style()`: Now accepts multiple title and footnote colors, sizes and boldings. (10.04.2026)
* `excel_output_style()`: New option `header_stat_merging` determines how the statistic symbols or labels are merged in the column header. As a new standard the labels are now not fully merged but in blocks. (10.04.2026)
* `if.()`: Observation selection now also works as do over loop. (12.04.2026)
* `any_table()`: New parameter `pct_block` enables percentage calculation inside the respective second to last grouping of the row or column variables. (12.04.2026)
* `any_table()`: If the result expression of a format starts with an "!", this expression will be used in calculations but will be dropped in the result table. This can be used to calculate block percentages based on a total expression without showing the total in the result table. (12.04.2026)
* `any_table()`: Variable combinations in rows and columns can now also be passed like "state + (age sex education)", which results in "state + age", "state + sex", "state + education", with the addition that the results with the same root grouping will be sorted together concerning the row header variables. For the column sorting there is a new `order_by` option with "blocks" for that purpose. (12.04.2026)
* `any_table()`: There are new special `var_labels`, which add additional top column headers. When assigning labels to variables that start with "block", e.g. "block1" = "Column Percentages", "block2" = "Row Percentages", these labels will appear as the top line of the column header. (12.04.2026)
* `summarise_plus()`: Variable combinations in types can now also be passed like "state + (age sex education)", which results in "state + age", "state + sex", "state + education". (15.04.2026)
* `frequencies()`, `crosstabs()`, `any_table()`, `export_with_style()`: Titles and footnotes can now also link to cells "cell:" and to files "file:". (15.04.2026)
* `libname()`: Can now optionally filter file list by provided file extensions. (16.04.2026)
* `set_style_options()`: Can now save the currently set global options to a rds file. (16.04.2026)
* `get_style_options()`: Can now load a rds file and set the contents as global style options. (16.04.2026)

### Changed functionality

* `summarise_plus()`: Instead of aborting when no values are passed, the function now generates a variable to output unweighted results. (26.03.2026)
* `Unit tests`: Moved unit tests completely from `testthat` to `tinytest`. `testthat` repeatedly got in the way with its own environment, `tinytest` offers more natural conditions and works faster. (31.01.2026)
* `transpose_plus()`: Removed macro variable usage. (09.04.2026)
* `export_with_style()`: Now returns a list with table, workbook and meta information, which can be used with `combine_into_workbook()`. (10.04.2026)
* `build_master()`: Now only catches R-script files and ignores files of all other types. (16.04.2026)

### Fixed

* `any_table()`: Some examples had a too long running time. Additionally shortened the running time of unit tests by ignoring styling options in the larger tables. (11.03.2026)
* `any_table()`: Fixed percentages based on single formatted variable expressions couldn't be computed with by variables. (11.03.2026)
* `any_table()`: The column header wasn't put together correctly, if only multiple group percentages were computed. (11.03.2026)
* `any_table()`: Excel number stored as text error is now ignored in general even if no styling is used. This was especially noticeable when generating tables with by variables and the `print_miss` option. (11.03.2026)
* `any_table()`: Even if no styling is used the tables now all receive the named regions. (11.03.2026)
* `any_table()`: Columns are now sorted correctly, if only row or column percentages are computed. (11.03.2026)
* `dummy_data()`: Now cuts down observations by random sample instead of just the head, so that the cut expressions aren't lost completely. (11.03.2026)
* `dummy_data()`: `balance` variable wasn't calculated correctly, which is now fixed. (12.03.2026)
* `any_table()`, `combine_into_workbook()`: The `output` parameter wasn't stored in the meta information list which lead to `combine_into_workbook()` not able to access it and always output formatted tables. Now the `output` parameter is stored in meta information. (24.03.2026)
* `build_master()`: Now ignores the root folder when building the script, to enable automatic rebuilding of the script. (26.03.2026)
* `import_data()`: In the examples the file names to read in weren't correct. This is fixed now. (28.03.2026)
* `split_by()`: Should now work inside custom functions. (29.03.2026)
* `retain_value()`, `retain_sum()`: On error now return NA instead of the whole data frame. (31.01.2026)
* `transpose_plus()`: Wide to long transposition now working again as intended. (09.04.2026)
* `any_table()`: Without column variables each variable in the column header received a "_ " at the end, which is not the case anymore. (10.04.2026)
* `sort_plus()`: When formats where used for sorting, the temporary sorting variables where always put up front instead of inserted in sequential order. This is fixed now. (10.04.2026)
* `keep()`, `dropp()`: If a character pattern is used to keep or drop variables and no variable matching the pattern is found, the functions now abort with a warning. (12.04.2026)
* `where.()`: Now aborts with a warning if no observations or variables are left in the data frame. (13.04.2026)
* `export_with_style()`: Can now rename variables that have blanks in their name. (13.04.2026)
* `summarise_plus()`, `frequencies()`, `crosstabs()`, `any_table()`: If an invalid format is passed, this now throws a warning instead of removing all formats silently. (14.04.2026)

### Optimization

* `if.()`, `else_if.()`, `else.()`: With the new `compute.()` they now perform variable assignments and calculations in a single pass instead of per iteration. (23.03.2026)
* `running_number()`: Now uses faster `data.table` function. (23.03.2026)
* `round_values()`: Now uses vectorized math to reduce memory allocation. (23.03.2026)
* `dummy_data()`: Reduced memory allocation. (25.03.2026)
* `summarise_plus()`: When using factor or character variables in `class` parameter, the check to preserve "." is now executed on the unique values first and only mapped back to the data frame, if there are any replacements needed, instead of running a `gsub` every time over the whole vector. (28.03.2026)
* `import_data()`, `import_multi()`: `import_data()` now also accepts a `openxlsx2` workbook as infile and not only a path to a workbook. This enables a massive performance boost for `import_multi()`, which now passes a workbook to `import_data()` instead of the file path. (28.03.2026)

### Additionally

* `any_table()`: `statistics` parameter is now NULL by default to enable group percentages to be computed on their own without sums. (11.03.2026)
* Swapped out a variable in abort `split_by()` because of duplicates test, because the test didn't throw the expected error. This was a very rare error because of bad luck with the dummy_data sample. The function works as intended. (23.03.2026)
* `any_table()`: Upped the dummy_data observations in tests to prevent rare errors. (23.03.2026)
* `any_table()`: Omitted table formatting in some tests to make them run quicker. (24.03.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Added missing global options function in the `see also` section. (25.03.2026)
* `import_multi()`, `export_multi()`: Added some messages to show progress. (28.03.2026)
* Added repository to DeepWiki and added badge to README. (29.03.2026)
* `any_table()`, `export_with_style()`: Added examples for `combine_into_workbook()`. (10.04.2026)
* `any_table()`: When row header labels are suppressed, the excess slashes are now removed. (10.04.2026)
* `compute.()`: Now throws a warning, if duplicate variable names are used, but goes on computing the valid variables. (15.04.2026)

# qol 1.2.2

CRAN release on 10.03.2026

### New functions

* `round_values()`: Rounds values according to the round half up rule. (20.02.2026)

### New functionality

* `any_table()`: Added `row_pct` and `col_pct` keywords to `pct_group` parameter. With this the function can calculate total percentages for rows and columns regardless of the respective other dimension. (21.02.2026)
* `rename_multi()`: Variable names can now be passed without quotation marks. (28.02.2026)
* `any_table()`: Percentages based on single formatted variable expressions can now be computed with the `pct_value` parameter. (05.03.2026)
* `any_table()`: If the `pct_value` or `pct_group` parameters are used but are not part of the statistics parameter, they will be automatically added to statistics. (05.03.2026)
* `dummy_data()`: Reworked the dummy data generation in mutliple ways:
	* Added new variables: NUTS2, NUTS3, number_of_persons, body_height, body_weight, income_class, expenses, balance. (09.03.2026)
	* Now generates up to five years. (09.03.2026)
	* Variables are now sorted by year, state, household_id, person_id. (09.03.2026)
	* household_id is now numbered individually within each state. (09.03.2026)
	* Weights now adapt to the number of observations so that weighted results stay roughly the same. (09.03.2026)
	* Brought variety in most of the variables so that different distributions aren't evenly spread anymore. (09.03.2026)
	* Adding NA values is now optional. (09.03.2026)

### Changed functionality

* `frequencies()`, `crosstabs()`, `any_table()`, `content_report()`: Now uses round half up rule for rounding values. (20.02.2026)

### Fixed

* `any_table()`: When there was an NA value in the first table column while the row label column was deleted, the NA symbol wasn't set. This is fixed now. (12.02.2026)
* `any_table()`: If the custom NA symbol is set to a number, Excels number stored as text error is now ignored. (12.02.2026)
* `transpose_plus()`: Took out a debug print I forgot in the function. (12.02.2026)
* `summarise_plus()`: Fixed formats not matching data when computing percentages, if numeric values are stored as character. (18.02.2026)
* `if.()`, `else_if.()`, `else.()`: When passing a vector to a new variable, the functions don't error any more if there are NA values. (22.02.2026)
* `summarise_plus()`: Fixed "." in a variable expression is now preserved as intended. (09.03.2026)
* `frequencies()`: When by variables were used the first by expression was omitted. This is fixed now. (09.03.2026)

### Optimization

* `dummy_data()`: Got rid of the loop for generating multiple years at random. Now the dummy data is based on a smaller set of observations, which allows faster variable generation. Years are only added as cartesian product at the end and values altered per year. This allowed the rework mentioned above: More variables and more variety in the same amount of time as before. (09.03.2026)

### New Error Checks

* `any_table()`: Added an error check in case all calculations are done and no value variable was computed. (05.03.2026)

### Additionally

* `frequencies()`: Fixed two of the examples where `list` was missing in formats parameter. (14.02.2026)

# qol 1.2.1

CRAN release on 11.02.2026

### New functions

* `expand_formats()`: Generates a data frame which contains all nested combinations of the provided format labels. (15.01.2026)
* `set_print_miss()`, `get_print_miss()`: Additional global setters and getters. (15.01.2026)
* `concat()`: Concatenate multiple variables inside a data frame into a new variable with automatic or individual padding. (21.01.2026)
* `sub_string()`: Can extract text from left, right or middle and is able to look up characters as start or end position. (22.01.2026)
* `remove_blanks()`: Removes blanks in the expressions of a character variable. (24.01.2026)
* `macro()`, `apply_macro()`: Resolve objects starting with "&" within a text. Functions which can print titles, footnotes, variable or statistic labels make use of these functions, so that e.g. a title can be passed as "The current year is &year" and will be resolved to "The current year is 2026". (24.01.2026)
* `where.()`: A quick way to filter a data frame and get a direct view of the result. (27.01.2026)
* `free_memory()`: Provides more flexible ways to remove objects from memory. (28.01.2026)
* `import_multi()`: Import multiple csv or xlsx files. The function is also capable of importing all sheets from multiple xlsx file. (10.02.2026)
* `export_multi()`: Export multiple csv or xlsx files based on a list of data frames. The function is also capable of exporting all data frames to multiple sheets in one xlsx file. (10.02.2026)
* `get_duplicate_var_names()`: Checks for duplicate variable names in a data frame, e.g. AGE, age and Age. (11.02.2026)
* `get_duplicate_var_count()`: Counts the number of duplicated variables in a data frame. If a variable appears three times, e.g. AGE, age and Age, the variable count will be one. (11.02.2026)

### New functionality

* `summarise_plus()`, `any_table()`, `frequencies()`, `crosstabs()`: The new parameter `print_miss` outputs all possible categories of the grouping variables based on the provided formats, even if there are no observations for a combination. (15.01.2026)
* `retain_variables()`: The ":" can now be used as a placeholder for "starts with" ("text:"), "ends with" (":text") and "contains" (":text:"). (15.01.2026)
* `excel_output_style()`: New `subheader` parameters which come into play when setting the `by_as_subheaders` to TRUE when using `any_table()`. The parameters can also be set as global option. (18.01.2026)
* `any_table()`: When using by variables with the new styling option `by_as_subheaders` the tables aren't split among multiple sheets, instead the by variable expressions are used as subheaders in one big table. (18.01.2026)
* `summarise_plus()`, `any_table()`, `crosstabs()`: `statistics` parameter can now be passed without quotation marks. (11.02.2026)
* `any_table()`: `pct_group` parameter can now be passed without quotation marks. (11.02.2026)

### Changed functionality

* `frequencies()`: Until now the function always printed a means and a frequencies table as default. Now it only prints a frequencies table as default to get the main results as fast as possible on screen. The means table can be activated again with the new `means` parameter. (17.01.2026)
* `frequencies()`: When using multiple by variables with excel `output`, worksheets are now ordered in provided variable order instead of alternating. (17.01.2026)
* `mark_case()`: Now outputs a variable with 1/0 instead of TRUE/FALSE. (18.01.2026)
* `recode.()`: Removed `new_var` parameter. Recode now doesn't return the whole data frame anymore, but only the recoded variable as a vector. So instead of writing `my_data <- my_data |> recode.("age_group", age = age.)`, the syntax is now more natural like this `my_data[["age_group"]] <- my_data |> recode.(age = age.)`. (21.01.2026)
* `running_number()`, `mark_case()`, `retain_value()`, `retain_sum()`: Removed `var_name` parameter. Functions now don't return the whole data frame anymore, but only the retained variable as a vector or list of vectors. So instead of writing e.g. `my_data <- my_data |> running_number("running")`, the syntax is now more natural like this `my_data[["running"]] <- my_data |> running_number()`. (22.01.2026)
* `any_table()`: When using pre summarised data, the error handling is now less restrict. Instead of aborting if the TYPE variable is missing, it is now auto generated. Additionally if statistic extensions are missing to the value variable names, the function now doesn't abort, instead extensions are now added according to the provided statistics and a warning is thrown. (23.01.2026)
* `summarise_plus()`: Numeric values stored as characters are now returned as character, while originally numeric variables stay numeric. (24.01.2026)
* `any_table()`: Instead of aborting when no values are passed, the function now generates a variable to output unweighted results. (02.02.2026)

### Fixed

* `frequencies()`: Mean tables are now printed when by variables are provided. (17.01.2026)
* `frequencies()`: No empty "total" table is printed when by variables are provided. (17.01.2026)
* `frequencies()`: "total" row is not printed anymore, when variables use multilabels and are computed with a by group. (17.01.2026)
* Unit test: Fixed global footnote option unit test. `set_titles()` was called instead of `set_footnotes()`. (18.01.2026)
* `any_table()`: When variable names started with the same base name and variable labels should be assigned, it could happen that the label of the shortest variable was applied to all variables. Now variable labels are always assigned from longest to shortest variable name to prevent this. (23.01.2026)
* `any_table()`: If a variable name was part of another variable label, it could happen that the already set label was altered with the label of the variable appearing in the label. This can't happen anymore. (24.01.2026)
* `summarise_plus()`: When a factor variable is used as class variable and a factor level has a "." in it, it is now output as provided instead of the "." being converted to "!!!". (24.01.2026)
* `summarise_plus()`: When a character variable is used as class variable and there is a "." in an expression, the function doesn't error anymore. (24.01.2026)
* `any_table()`: When removing the value variable label with "" from the column header, so that the header line is removed, the alternation of the header lines won't mess up anymore. (28.01.2026)
* `any_table()`: The function ran into an error when statistic labels where set in the variable labels parameter. This is no more possible. (28.01.2026)
* `build_master()`: Added `library(qol)` to the top of the file. Loading the package is neccessary so that the `libname()` functions are able to run. (29.01.2026)
* `any_table()`: Percentages could have the wrong number of decimal places, if the group percentage variable name ended in a specific way and a number. (05.02.2026)

### Optimization

* `summarise_plus()`: When only statistics based on sums are selected, the function already pre summarises the data frame, to apply the formats on a much smaller data frame. When using nesting = "all"/"single" the data frame is now pre summarised a second time before applying formats, this time only using the grouping variables of the combination beeing processed. This drastically cuts down memory allocation - especially for larger data frames - and speeds up every iteration significantly. In addition to this function `any_table()` benefits greatly from this optimization. (16.01.2026)
* `any_table()`, `frequencies()`, `crosstabs()`: When using by variables in excel `output`, the new `print_miss` option enables a shortcut in formatting the sheets after the first one. Since the option guarantees that all follow up sheets are printed with the exact same table width and height, because all categories are printed, only the first sheet must be formatted. All other sheets can clone the entire style from the first sheet. (17.01.2026)
* `replace_except()`: Got rid of the nested for loop. This saves time in `any_table()` on larger data frames. (17.01.2026)
* `reorder_combination()`: Reordering is now only done on unique vector values instead of a whole larger vector. This saves time in `any_table()` on larger data frames. (17.01.2026)

### New Error Checks

* `combine_into_workbook()`: Added an error check in case a provided object is no `any_table()` result list. (17.01.2026)
* `discrete_format()`, `interval_format()`: Added an error check in case something other than list elements are provided. (29.01.2026)

### Additionally

* `any_table()`: Removed c() in examples where not necessary. (24.01.2026)
* `if.()`: Now outputs a message on how many observations have been removed and how many are left. (27.01.2026)
* `any_table()`, `combine_into_workbook()`: Adjusted examples. (02.02.2026)
* `if.()`, `else_if.()`, `else.()`: If used inside a function, these functions should now be able to catch the original variable name passed into the parent function. (10.02.2026)
* `content_report`: Added duplicate variable count to global information. (11.02.2026)


# qol 1.2.0

CRAN release on 13.01.2026

### New functions

* `vars_between()`: Get variable names between two variables in a data frame. (21.12.2025)
* `convert_factor()`: Converts all given variables to factor. (23.12.2025)
* `transpose_plus()`: Format driven transposition, which is able to do more than just transpose values. (27.12.2025)
* `sort_plus()`: Sort data frame observations with some additions. (27.12.2025)
* `split_by()`: Since `split_by_var()` didn't have any benefit, `split_by_var()` and `split_by_condition()` are now fused into one function. Additionally to give the new function more power, formats can be used to not only split up a data frame by the actual values in the data frame, but also into the desired ones passed with a format. Using multilabels it is also possible to generate completely new values and therefore data frames on the fly. (30.12.2025)
* `set()`: Stack data frames by column names with optional character compression. (30.12.2025)
* Error handling functions:
	* `resolve_intersection()`: Compares if two vectors have intersecting values. (02.01.2026)
	* `part_of_df()`: Check if variable names are part of a data frame. (02.01.2026)
	* `remove_doubled_values()`: Remove values from a vector that appear more than once. (02.01.2026)
	* `check_weight()`: Check whether a suitable weight variable was provided. (02.01.2026)
* `rename_multi()`: Rename one or more variables. (02.01.2026)
* `retain_variables()`: Order variables to the front or back of a data frame. Can also add empty variables. (04.01.2026)
* `add_variable_range()`: Add empty variables to a data frame in the provided range. (04.01.2026)
* Global style options:
	* `set_style_options()`: Sets the options from `excel_output_style()` and `number_format_style()` to a global environment so that every function which is capable of outputting styled Excel workbooks can use them without passing the `style` parameter every time individually. (06.01.2026)
	* `get_style_options()`: Print the currently set global options. (06.01.2026)
	* `reset_style_options()`: Set global style options to default values of `excel_output_style()` and `number_format_style()`. (06.01.2026)
	* `close_file()`: A simple, more readable wrapper for setting file parameter to NULL. (07.01.2026)
	* `set_variable_labels()`, `get_variable_labels()`, `set_stat_labels()`, `get_stat_labels()`, `set_print()`, `get_print()`, `set_monitor()`, `get_monitor()`, `set_na.rm()`, `get_na.rm()`: Additional global setters and getters. (07.01.2026)
	* `set_output()`, `get_output()`: Additional global setters and getters. (11.01.2026)
	* `set_titles()`, `get_titles()`, `set_footnotes()`, `get_footnotes()`: Additional global setters and getters. (11.01.2026)
* `content_report()`: Collects and prints global and per variable information about the provided data frame. (08.01.2026)
* `import_data()`, `export_data()`: Lightweight import and export for csv and xlsx files. (10.01.2026)
* `first_row_as_names()`: Sets the values of the first data frame row as variable names and deletes first row. (10.01.2026)
* `qol_news()`: Opens changelog GitHub Page. (11.01.2026)

### New functionality

* `inverse()`: Now supports variable names written without quotation marks. (21.12.2025)
* `keep()`/`dropp()`: Now support variable ranges, like "state:income". (21.12.2025)
* `any_table()`: Removed `pre_summed` parameter. Instead the function now checks on it's own, whether the provided data frame is pre summarised or not. (23.12.2025)
* `dots_to_char()`: Renamed from `args_to_char()` and is now able to get the original argument from up the environment tree and return it as character vector. (02.01.2026)
* `args_to_char()`: Now converts the contents of a given argument to a character vector. (02.01.2026)
* `excel_output_style()`: Parameter `file` is now split up into `save_path` and `file` (meaning just the file name + extension). (06.01.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: The `style` parameter is now set to the new global styling options as default instead of `excel_output_style()`. (06.01.2026)
* `any_table()`: The `var_labels` and `stat_labels` parameter is now set to the new global options as default. (07.01.2026)
* In general: New global options for `print`, `monitor` and `na.rm` have been implemented into the functions capable of using them. (07.01.2026)
* `if.()`: If only a single variable name is provided, it is now evaluated as !is.na(var_name). (10.01.2026)
* `retain_value()`: Reworked iternally, can now also handle character variables. (10.01.2026)
* `excel_output_style()`: New parameters `as_heatmap`, `heatmap_low_color`, `heatmap_middle_color` and `heatmap_high_color`. These can also be set as global options. If `as_heatmap` is TRUE the tables from `any_table()`, `frequencies()`, `crosstabs()` and `export_with_style()` receive a conditional formatting. (11.01.2026)
* `crosstabs()`: Now has a new parameter `show_total` to control output of row and column totals. (11.01.2026)
* `keep()`/`dropp()`: The ":" can now be used as a placeholder for "starts with" ("text:"), "ends with" (":text") and "contains" (":text:"). (12.01.2026)
* `any_table()`: Is now able to generate 'pct_value' statistic without outputting additional 'sum' values. (12.01.2026)
* `build_master()`: Now generates a code block which can rebuilt the master file. Additionally the folder structure is now generated in code blocks which can open the folders and files. Overall gave the master file a bit more visual structure. (12.01.2026)

### Removed

* `split_by_var()` and `split_by_condition()`: See comment under 'New functions'. (30.12.2025)
* `is_numeric()`: There was simply no benefit. (02.01.2026)

### Fixed

* `keep()`: Variables where always output in provided order. order_vars = FALSE (default) will now output variables in order of appearance. (21.12.2025)
* `any_table()`: Now checks if a column combination is also part of the row combinations. (22.12.2025)
* `any_table()`: Fixed row header variables where sorted alphabetically instead of in provided order. Bug was introduced in version 1.1.1. (23.12.2025)
* `any_table()`: Fixed by variables could be sorted in the wrong order. (30.12.2025)
* `any_table()`: Added missing format for variable 'state' in examples. (02.01.2026)
* `frequencies()`: 'formats' parameter was missing a '=' in examples. (02.01.2026)
* `summarise_plus()`: Group percentages with nesting = "all" or "single" and na.rm = TRUE are now computed as intended. (02.01.2026)
* `handle_cell_styles()`: Set apply_font and font_id in a save way to prevent warnings. (04.01.2026, thanks to @JanMarvin)
* `summarise_plus()`: Removed conversion to numeric values before applying formats, which could lead to not matching formats, if a numeric value was intentionally stored as character value. (07.01.2026)
* `any_table()`: When na.rm was TRUE and many table cells where generated while only having few observations, it could happen that some combinations weren't generated and a result mismatch happened. Results are now joined instead of cbind together to be safe. (11.01.2026)

### Optimization

* `summarise_plus()`: Now uses faster `collapse::na_omit` for NA removal. (27.12.2025)
* `dummy_data()`: Optimized and now just takes half the time to generate data. (27.12.2025)
* `summarise_plus()`: Swapped in more `collapse` functions. Turned off sorting of `collapse::GRP` when using the shortcut route. (28.12.2025)
* `apply_formats()`: NA value subsetting is now only done once and not twice with interval formats. `data.table::setkey` is now only called on the format data frame. (28.12.2025)
* `summarise_plus()`: When na.rm option is TRUE and only statistics based on sums are selected, `summarise_plus()` is now able to take the shortcut route. This also gives `any_table()` a huge performance boost when na.rm option is TRUE. (29.12.2025)
* In general: Swapped in more `collapse` functions. (29.12.2025)
* `any_table()`: If more than two group percentages have to be computed, any additional one gets computed faster, because they are computed on a smaller data frame. (29.12.2025)
* In general: Error handling has been generalized in many places to make it more robust and get a better overview. (02.01.2026)
* `apply_format()`: Removed redundant data.table transformations. (11.01.2026)

### New Error Checks

* `any_table()`: Added an error check in case a variable combination was provided, which is not part of a pre summarised data frame. (23.12.2025)
* `summarise_plus()` and `any_table()`: Added an error check in case an invalid statistic is provided. (29.12.2025)
* `summarise_plus()` and `any_table()`: Added an error check in case an invalid type is provided. (02.01.2026)
* `recode_multi()`: Added an error check in case an unknown object is provided. (02.01.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Added check if specified save path exists. (08.01.2026)
* `export_with_style()`: Added check for output. (11.01.2026)

### Additionally

* Restructured some "Small Helpers" into "Renaming" and "Variable Selection". (21.12.2025)
* Adjusted some warning messages. (22.12.2025)
* Added some comments to the heavier functions, to enhance visual code structure. (22.12.2025)
* Added `transpose_plus()` and `sort_plus()` examples to the README. (27.12.2025)
* `discrete_format()` and `interval_format()`: Labels will now be converted to numeric if they are all numeric. (02.01.2026)
* In general: Added more messages to display what functions do. (03.01.2026)
* `retain_value()`, `retain_sum()`: `value` parameter is now called `values`. (10.01.2026)
* `mark_case()`, `retain_value()`, `retain_sum()`: Adjusted unit tests to something that actually makes sense. (10.01.2026)
* In general: Added some unit tests on file saving and retrieving. (10.01.2026)
* `any_table()`, `frequencies()`, `crosstabs()`, `export_with_style()`: Updated examples to show how a file is saved via the style element. (13.01.2026)

### GitHub related

* Updated article on further comparison with SAS on import csv and xlsx and `content_report()` as well as the new `if.()` selection with only providing a variable name. Also added `transpose_plus()` example and selecting ranges with `keep()`/`dropp()`. (12.01.2026)


# qol 1.1.1

CRAN release on 13.12.2025

### New functions

* `multi_join()`: Join two or more data frames together in one operation. (30.11.2025)
* `libname()`: Check if path exists and retrieve files. (04.12.2025)

### Fixed

* `any_table()`: Fixed multi layered column header labels where not applied correct. (28.11.2025)
* `any_table()`: Fixed incorrect column order when using order_by "values" while variable names have underscores. (04.12.2025)
* `any_table()`: Using pre summed data now also works, if variable names carry underscores. (04.12.2025)

### GitHub related

* Added article comparing this package with SAS even further. (02.12.2025)


# qol 1.1.0

CRAN release on 20.11.2025

### New functions

* `build_master()`: Reads a given folder structure, which contains scripts, and builds a master script as a markdown file. (18.10.2025)
* `build_rstheme()`: Build a complete theme file, which can be used to change the visual appearance of RStudio. (23.10.2025)
* `combine_into_workbook()`: Combines any number of tables created with `any_table()` into one workbook and styles them according to their meta information. (26.10.2025)
* `replace_except()`: Replaces a provided pattern with another, while protecting exceptions. (29.10.2025)
* `mark_case()`: Marks first or last cases within a provided group. (31.10.2025)
* `retain_value()`: Retains the first value for all cases of the same group. (31.10.2025)
* `retain_sum()`: Retains the summarised values for all cases of the same group. (31.10.2025)

### New functionality

* `interval_format()`: Implemented keywords "low "and "high" with which one can define pseudo low or high values, if one doesn't know, what the minimum or maximum value of a variable is. (20.10.2025)
* `discrete_format()`: Implemented keyword "other" with which one can catch any other value not covered by the explicitly specified values. (27.10.2025)
* `any_table()`: Now returns styling meta information as a third list element. This meta information can be used by `combine_into_workbook()`. (26.10.2025)
* `any_table()`: Now supports underscores in variable names. (29.10.2025)
* `any_table()`: Added new column ordering possibilities by "columns" or "values_stats". (03.11.2025)
* `any_table()`: Can now output tables even though no column variables are specified. (05.11.2025)

### Fixed

* In `any_table()` the header and table row heights as well as the column widths set by the style option where 1 row/column to short. (14.10.2025)
* In `any_table()` the row heights didn't catch the whole table. (14.10.2025)
* `any_table()` ran into an error, if a variable was provided as pct_group, which was not part of the row and column variables. (14.10.2025)
* Fixed typos in frequencies examples, where it said "frequency" instead of "frequencies". (18.10.2025)
* In Excel outputs the number stored as text error is now ignored. (19.10.2025, thanks to @JanMarvin)
* Fixed table length captured with too many rows. (19.10.2025)
* `any_table()`: Fixed order_by stats not working as intended in some cases. (03.11.2025)
* `any_table()`: Fixed table rows not ordered correctly in some cases. (03.11.2025)
* `any_table()`: Depending on variable constellation and ordering of the column header it could happen, that the header wasn't merged correct in the Excel workbook. This was fixed. (04.11.2025)
* `any_table()`: Doesn't run into an error, if the table only consists of one value column. (05.11.2025)

### Changed functionality

* In `excel_output_style()` the options `column_widths` and `row_heights` now start at the first column/row instead of the beginning of the table. (19.10.2025)
* In `summarise_plus()`, if summarised values should be merged back, the variables TYPE, TYPE_NR and DEPTHS are now not merged back anymore. (31.10.2025)

### Additionally

* Added missing functions `export_with_style()` and `get_excel_range()` to the ?qol overview page. (14.10.2025)
* Added information to the startup message to use ?qol to get an overview. (14.10.2025)
* Now using openxlsx2 helper to convert row and column numbers to Excel ranges. (19.10.2025, thanks to @JanMarvin)
* All Excel tables now have named ranges for the table and the values. (19.10.2025, thanks to @JanMarvin)
* Added an example to the README showing how to save an Excel workbook to the filesystem. (19.10.2025)
* Used lintr package for some code cleanup. (20.10.2025, thanks to @JanMarvin for the advise)
* Added custom theme section to the README. (23.10.2025)
* Added message, if a format is applied to a factor variable. (27.10.2025)
* Excel workbooks will now only be opened in interactive sessions. (29.10.2025, thanks to @JanMarvin)
* Corrected typos in snippets. (03.11.2025)

### GitHub related

* Added article comparing this package with SAS. (14.10.2025)
* Changed GitHub Page style. (14.10.2025)
* Added example themes with corresponding code. (23.10.2025)


# qol 1.0.2

CRAN release on 14.10.2025

### DESCRIPTION file

* Fixed brackets in DESCRIPTION so that auto linking works.
* Added another URL.

### Fixed

* There could be an error in `summarise_plus()`, `any_table()` and `args_to_char()`, if there where to many variables provided.
* `summarise_plus()` ran into an error, if a value label from a format contained a ".".
* `any_table()` could lead to wrong results if the pre_summed option was used and a variable was part of multiple combinations in the summarised data.

### Changed functionality

* In `summarise_plus()`, when types are defined, the total row is now removed if not explicitly defined as type.
* In `summarise_plus()`, when the nesting option "deepest" is used, the variables TYPE, TYPE_NR and DEPTHS are now also generated.

### Unit tests

* Adjusted tests according to changed functionalities

### Additionally

* Added openxlsx2 as import in the qol main help file
* Corrected a typo in a warning message in `any_table()` concerning variable order.


# qol 1.0.1

CRAN release on 10.10.2025

* Added references to specific SAS functions in the description field of the Description file where they are mentioned.
* Removed specific seed in R/dummy_data.R
* Fixed a bug in dummy_data where it could happen that not enough observations where generated.


# qol 1.0.0 

* Initial CRAN submission.
