diff --git a/.Rbuildignore b/.Rbuildignore index 572725a334..6d47adf313 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -90,6 +90,7 @@ tests/testthat/test-table_parts.R tests/testthat/test-text_transform.R tests/testthat/test-util_functions.R tests/testthat/test-utils_formatters.R +tests/testthat/test-utils_plots.R tests/testthat/test-utils_render_html.R tests/testthat/test-utils_units.R tests/testthat/test-utils.R diff --git a/CITATION.cff b/CITATION.cff index a95d515c21..5ee8e3bde1 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -36,7 +36,7 @@ authors: orcid: https://orcid.org/0000-0002-4064-6012 repository: https://CRAN.R-project.org/package=gt repository-code: https://github.com/rstudio/gt -url: https://gt.rstudio.com/ +url: https://gt.rstudio.com contact: - family-names: Iannone given-names: Richard diff --git a/DESCRIPTION b/DESCRIPTION index 8ae8276fe3..6fe18bbb89 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -24,7 +24,7 @@ Authors@R: c( person("Posit Software, PBC", role = c("cph", "fnd")) ) License: MIT + file LICENSE -URL: https://gt.rstudio.com/, https://github.com/rstudio/gt +URL: https://gt.rstudio.com, https://github.com/rstudio/gt BugReports: https://github.com/rstudio/gt/issues Encoding: UTF-8 LazyData: true diff --git a/NEWS.md b/NEWS.md index d23c2b3b75..362fc2ef34 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,7 @@ ## Nanoplots -* We can now add in little plots called *nanoplots* to a **gt** table (#299, #515). (#1431, #1439, #1445, #1453) +* We can now add in little plots called *nanoplots* to a **gt** table (#299, #515). (#1431, #1439, #1445, #1453, #1458, #1459, #1461) * The function `cols_nanoplot()` adds a new column that contains the plots. The data can be obtained from one or more columns in the table. A helper function called `nanoplot_options()` allows for altering the composition and styling of the nanoplots in the new column. @@ -14,13 +14,13 @@ ## Other great new features -* Brand new rows can be added to a **gt** table with the new `rows_add()` function. The user can supply the new row data through name value pairs. You have control over where they are placed by way of the `.before` and `.after` arguments (new rows are added to the bottom of the table by default). You can also add empty (i.e., all `NA`) rows with the `.n_empty` option. (#1323) +* Brand new rows can be added to a **gt** table with the new `rows_add()` function. The user can supply the new row data through name value pairs. You have control over where they are placed by way of the `.before` and `.after` arguments (new rows are added to the bottom of the table by default). You can also add empty (i.e., all `NA`) rows with the `.n_empty` option (#698). (#1323) * To complement `rows_add()`, the `cols_add()` function was added. New columns can indeed be added to a **gt** table with this function, which has an interface close to that of `dplyr::mutate()`. (#1367) * You can now use an empty table as the starting point for a **gt** table. This can be used in conjunction with `cols_add()` and `rows_add()` to build a table piece-by-piece in specific workflows/settings. What constitutes empty tables can be any of: `0 x 0` tables, `0 x n` tables (no rows, some columns), or `n x 0` tables (some rows, no columns; treated the same as `0 x 0` tables). (#1376) -* There is now a way to better express measurement units and we do this in **gt** with something called units notation. With an intuitive and easy-to-learn syntax, **gt** will ensure that any measurement units are formatted correctly no matter what the output type is. We can format units in the table body with `fmt_units()`, we can attach units to column labels with `cols_units()`, and we can integrate units notation in the already-available `cols_label()` and `tab_spanner()` functions (#417, #533). (#1357, #1426) +* There is now a way to better express measurement units and we do this in **gt** with something called units notation. With an intuitive and easy-to-learn syntax, **gt** will ensure that any measurement units are formatted correctly no matter what the output type is. We can format units in the table body with `fmt_units()`, we can attach units to column labels with `cols_units()`, and we can integrate units notation in the already-available `cols_label()` and `tab_spanner()` functions (#417, #533). (#1357, #1426, #1446) * A very useful new helper function, `from_column()`, has been added so you can fetch values (for compatible arguments) from a column in the input table. For example, if you are using `fmt_scientific()` and the number of significant figures should vary across the values to be formatted, a column containing those values for the `n_sigfig` argument can be referenced by `from_column()`. (#1392, #1393, #1395, #1396, #1399, #1403) @@ -36,21 +36,23 @@ ## Improvements to the Word output format -* Processing to Word output now escapes HTML in more places. (#1303) +* Processing to Word output now escapes HTML in more places (#1378). (#1303) * The Word output format now uses the `side` argument present in `summary_rows()` and `grand_summary_rows()` to place the new summary rows either the top or bottom of the row group (with `summary_rows()`) or table as a whole (with `grand_summary_rows()`). (#1325) -* Tables rendered as Word output can now handle the specific case where a table with summary rows doesn't have rownames. (#1325) +* Tables rendered as Word output can now handle the specific case where a table with summary rows doesn't have row names. (#1325) + +* Summary rows in Word output tables can now be placed at the top or bottom of a group (or at the top or bottom of the table). (#1402) * Word output tables can now contain images. This entails compatibility with the `fmt_image()` function, and, images (local and remote) can be inserted through Markdown (#1272). (#1273) ## Documentation enhancements -* The **gt** website has been updated with a slightly different look; section names have been updated for consistency. (#1287, #1340, #1341) +* The **gt** website has been updated with a slightly different look; section names have been updated for consistency (#1419). (#1287, #1340, #1341, #1444) -* We've improved the formatting of arguments in the documentation so that they all have short titles and descriptions regarding expected inputs and default values. This looks great both in the internal R help pages and in the **pkgdown**-generated website. (#1338) +* We've improved the formatting of arguments in the documentation so that they all have short titles and descriptions regarding expected inputs and default values. This looks great both in the internal R help pages and in the **pkgdown**-generated website (#1290). (#1338) -* Several small documentation updates were made, with an emphasis on improving examples. (#1293, #1316, #1324, #1329, #1330, #1331, #1334, #1381, #1383, #1395, #1404) +* Several small documentation updates were made, with an emphasis on improving examples (#1304, #1349, #1369). (#1293, #1316, #1324, #1329, #1330, #1331, #1334, #1381, #1383, #1395, #1404, #1442, #1454) ## Minor improvements and bug fixes @@ -62,9 +64,11 @@ * The `cols_merge_range()` function now has a `locale` argument. Range patterns across locales are different (can involve the use of a single hyphen, en dash, em dash, tilde, etc.) and so it does make sense to follow the convention of a locale if provided (#158). (#1423) +* The `fmt_url()` function now has a few more options for adding anchor tag attributes (`"target"`, `"rel"`, `"referrerpolicy"`, and `"hreflang"`). Thanks @elipousson for the work on this! (#1428). (#1452) + * We now have rudimentary support for defining column widths for LaTeX output tables (with `cols_width()`). This accepts length values in 'px' which and automatic conversion to 'pt' values is performed to maximize compatibility with different LaTeX flavors (#634, #851, #1417). (#1371, #1450) -* It's now possible to use background fill colors and perform text coloring and bolding for body cells in LaTeX tables. This is commonly performed through the use of `tab_style()` and `data_color()`. (#1352) +* It's now possible to use background fill colors and perform text coloring and emboldened/italicized text within the body cells of LaTeX tables. This is commonly performed through the use of `tab_style()` and `data_color()` (#84, #869). (#1352) * The `gtsave()` function now works with `gt_group` objects (usually generated through `gt_split()` or `gt_group()`) (#1354). (#1365) @@ -90,16 +94,18 @@ * Fixed an issue with `cols_label_with()` where column names wouldn't be relabeled if the resolved columns were only a subset of the total columns available. (#1326) -* Fixed a LaTeX bug where some characters following a `\midrule` would corrupt the table (#145, #391). (#1390) +* Fixed a LaTeX bug where some characters following a `\midrule` would corrupt the table (#145, #391, #1107, #1182). (#1390) * Provided a rendering fallback for HTML tables rendered in Quarto where the combination of `fmt_markdown()` and `tab_options(quarto.disable_processing = TRUE)` would incorrectly result in empty cells. (#1455) -* A issue associated with a lack of HTML formatting within interactive tables has been fixed (#1299, #1370, #1384). (#1388) +* A issue associated with a lack of HTML formatting within interactive tables has been fixed (#1299, #1370, #1384, #1443). (#1388) * Many user-facing error messages have been enhanced using the latest features from the **cli** package. (#1337, thanks @olivroy!) * Unit tests can now be successfully run on Linux flavors that don't have the `locale` utility (#1214). (#1350, thanks @bastistician!) +* If ever the 'undetermined' (`"und"`) locale is used, it is automatically mapped to the `"en"` locale. (#1394) + * Many unit tests were added for much increased test coverage and many more were modified to increase the speed of running the test suite. (#1291, #1294, #1298, #1350, #1412) * Added utility functions to extract all examples for regularly building a Quarto website (to do integration testing). (#1344) diff --git a/R/format_data.R b/R/format_data.R index 791faa2b28..87d3e64ab6 100644 --- a/R/format_data.R +++ b/R/format_data.R @@ -8652,6 +8652,15 @@ format_units_by_context <- function(x, context = "html") { #' `as_button = TRUE`). All of these options are by default set to `"auto"`, #' allowing **gt** to choose appropriate fill, width, and outline values. #' +#' @param target,rel,referrerpolicy,hreflang *Anchor element attributes* +#' +#' `scalar` // *default:* `NULL` +#' +#' Additional anchor element attributes. For descriptions of each attribute +#' and the allowed values, refer to the [MDN Web Docs reference on the anchor +#' HTML element]( +#' https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes). +#' #' @return An object of class `gt_tbl`. #' #' @section Compatibility of formatting function with data values: @@ -8884,7 +8893,11 @@ fmt_url <- function( show_underline = "auto", button_fill = "auto", button_width = "auto", - button_outline = "auto" + button_outline = "auto", + target = NULL, + rel = NULL, + referrerpolicy = NULL, + hreflang = NULL ) { # Perform input object validation @@ -9059,7 +9072,8 @@ fmt_url <- function( button_outline_color <- "#DFDFDF" if (button_fill %in% c( - "#FFFFFF", "#FFFFFF", "#FAF5EF", "#FAFAFA", "#FFFEFC", "#FBFCFA", "#FBFAF2" + "#FFFFFF", "#FFFFFF", "#FAF5EF", "#FAFAFA", + "#FFFEFC", "#FBFCFA", "#FBFAF2" )) { button_outline_style <- "solid" } else { @@ -9112,7 +9126,6 @@ fmt_url <- function( } } else { - if (any(grepl("\\[.*?\\]\\(.*?\\)", x_str_non_missing))) { # Generate labels @@ -9152,28 +9165,125 @@ fmt_url <- function( } } + add_anchor_attr <- function( + init = NULL, + arg, + nm, + values = NULL, + error_arg = caller_arg(arg), + error_call = caller_env() + ) { + + if (!is.null(values)) { + + arg <- + rlang::arg_match( + arg, + values = values, + error_arg = error_arg, + error_call = error_call + ) + } + + if (!is_string(arg)) { + cli::cli_abort( + "{.arg {nm}} must be a string, not {.obj_type_friendly {arg}}", + call = error_call + ) + } + + paste0(init, " ", nm, "=\"", arg, "\"") + } + + target <- target %||% "_blank" + target_values <- NULL + + if (grepl("^_", target)) { + target_values <- c("_blank", "_self", "_parent", "_top") + } + + anchor_attr <- + add_anchor_attr( + arg = target, + nm = "target", + values = target_values + ) + + if (!is.null(rel)) { + + anchor_attr <- + add_anchor_attr( + anchor_attr, + rel, + nm = "rel", + values = c( + "alternate", "author", "bookmark", "external", "help", + "license", "next", "nofollow", "noreferrer", "noopener", + "prev", "search", "tag" + ) + ) + } + + if (!is.null(referrerpolicy)) { + + anchor_attr <- + add_anchor_attr( + anchor_attr, + referrerpolicy, + nm = "referrerpolicy", + values = c( + "no-referrer", "no-referrer-when-downgrade", "origin", + "origin-when-cross-origin", "same-origin", "strict-origin", + "strict-origin-when-cross-origin", "unsafe-url" + ) + ) + } + + if (!is.null(hreflang)) { + + anchor_attr <- + add_anchor_attr( + anchor_attr, + arg = hreflang, + nm = "hreflang" + ) + } + + anchor_attr <- + add_anchor_attr( + anchor_attr, + arg = paste0( + "color:", color[1], ";", + "text-decoration:", + if (show_underline) "underline" else "none", ";", + if (show_underline) "text-underline-position: under;" else NULL, + "display: inline-block;", + if (as_button) { + paste0( + "background-color: ", button_fill, ";", + "padding: 8px 12px;", + if (!is.null(button_width)) { + paste0("width: ", button_width, "; text-align: center;") + } else { + NULL + }, + "outline-style: ", button_outline_style, "; ", + "outline-color: ", button_outline_color, "; ", + "outline-width: ", button_outline_width, ";" + ) + } else { + NULL + } + ), + nm = "style" + ) + x_str_non_missing <- paste0( - "", + "", label_str, "" ) diff --git a/R/helpers.R b/R/helpers.R index a16f88ec87..f688b498d5 100644 --- a/R/helpers.R +++ b/R/helpers.R @@ -648,6 +648,22 @@ currency <- function( #' can however be changed by providing a color value to the #' `data_bar_negative_fill_color` option. #' +#' @param reference_line_color *Color for the reference line* +#' +#' `scalar` // *default:* `NULL` (`optional`) +#' +#' The reference line will have a color of `"#75A8B0"` if it is set to appear. +#' This color can be changed by providing a single color value to +#' `reference_line_color`. +#' +#' @param reference_area_fill_color *Fill color for the reference area* +#' +#' `scalar` // *default:* `NULL` (`optional`) +#' +#' If a reference area has been defined and is visible it has by default +#' a fill color of `"#A6E6F2"`. This can be modified by declaring a color +#' value in the `reference_area_fill_color` option. +#' #' @param vertical_guide_stroke_color *Color of vertical guides* #' #' `scalar` // *default:* `NULL` (`optional`) @@ -719,6 +735,15 @@ currency <- function( #' nanoplot. This hidden layer is active by default but can be deactivated by #' using `show_y_axis_guide = FALSE`. #' +#' @param y_val_fmt_fn,y_axis_fmt_fn,y_ref_line_fmt_fn *Custom formatting for y values* +#' +#' `function` // *default:* `NULL` (`optional`) +#' +#' If providing a function to `y_val_fmt_fn`, `y_axis_fmt_fn`, or +#' `y_ref_line_fmt_fn` then customized formatting of the *y* values associated +#' with the data points/bars, the *y*-axis labels, and the reference line can +#' be performed. +#' #' @param currency *Define values as currencies of a specific type* #' #' `scalar|obj:` // *default:* `NULL` (`optional`) @@ -755,6 +780,8 @@ nanoplot_options <- function( data_bar_negative_stroke_color = NULL, data_bar_negative_stroke_width = NULL, data_bar_negative_fill_color = NULL, + reference_line_color = NULL, + reference_area_fill_color = NULL, vertical_guide_stroke_color = NULL, vertical_guide_stroke_width = NULL, show_data_points = NULL, @@ -764,6 +791,9 @@ nanoplot_options <- function( show_reference_area = NULL, show_vertical_guides = NULL, show_y_axis_guide = NULL, + y_val_fmt_fn = NULL, + y_axis_fmt_fn = NULL, + y_ref_line_fmt_fn = NULL, currency = NULL ) { @@ -806,6 +836,12 @@ nanoplot_options <- function( if (is.null(data_bar_negative_fill_color)) { data_bar_negative_fill_color <- "#D75A68" } + if (is.null(reference_line_color)) { + reference_line_color <- "#75A8B0" + } + if (is.null(reference_area_fill_color)) { + reference_area_fill_color <- "#A6E6F2" + } if (is.null(vertical_guide_stroke_color)) { vertical_guide_stroke_color <- "#911EB4" } @@ -833,6 +869,15 @@ nanoplot_options <- function( if (is.null(show_y_axis_guide)) { show_y_axis_guide <- TRUE } + if (is.null(y_val_fmt_fn)) { + y_val_fmt_fn <- NULL + } + if (is.null(y_axis_fmt_fn)) { + y_axis_fmt_fn <- NULL + } + if (is.null(y_ref_line_fmt_fn)) { + y_ref_line_fmt_fn <- NULL + } if (is.null(currency)) { currency <- NULL } @@ -852,6 +897,8 @@ nanoplot_options <- function( data_bar_negative_stroke_color = data_bar_negative_stroke_color, data_bar_negative_stroke_width = data_bar_negative_stroke_width, data_bar_negative_fill_color = data_bar_negative_fill_color, + reference_line_color = reference_line_color, + reference_area_fill_color = reference_area_fill_color, vertical_guide_stroke_color = vertical_guide_stroke_color, vertical_guide_stroke_width = vertical_guide_stroke_width, show_data_points = show_data_points, @@ -861,6 +908,9 @@ nanoplot_options <- function( show_reference_area = show_reference_area, show_vertical_guides = show_vertical_guides, show_y_axis_guide = show_y_axis_guide, + y_val_fmt_fn = y_val_fmt_fn, + y_axis_fmt_fn = y_axis_fmt_fn, + y_ref_line_fmt_fn = y_ref_line_fmt_fn, currency = currency ) diff --git a/R/modify_columns.R b/R/modify_columns.R index f433b87018..b6932c3016 100644 --- a/R/modify_columns.R +++ b/R/modify_columns.R @@ -2266,6 +2266,15 @@ cols_add <- function( #' (7) `"first"`, or (8) `"last"`. Input can either be a vector or list with #' two elements. #' +#' @param expand_x,expand_y *Expand plot scale in the x and y directions* +#' +#' `vector` // *default:* `NULL` (`optional`) +#' +#' Should you need to have plots expand in the *x* or *y* direction, provide +#' one or more values to `expand_x` or `expand_y`. Any values provided that +#' are outside of the range of data provided to the plot should result in a +#' scale expansion. +#' #' @param new_col_name *Column name for the new column containing the plots* #' #' `scalar` // *default:* `NULL` (`optional`) @@ -2333,29 +2342,34 @@ cols_add <- function( #' #' @section How to supply data for nanoplots: #' -#' The input data for nanoplots naturally needs to be numeric and there are -#' two major ways to formulate that data: (1) from single values across many -#' columns, and (2) using text-based number streams. It's pretty to rationalize -#' the first, and we may already have wide data in the input data frame anyway -#' (take a look at the [`illness`] and [`towny`] datasets for examples of this). -#' There's one data value per column so the key thing here is to reference the -#' columns in the correct order. With a select helper, good column naming, and -#' the columns being in the intended order, this is a snap. +#' The input data for nanoplots naturally needs to be numeric and there are two +#' major ways to formulate that data: (1) from single values across many +#' columns, and (2) using text-based value streams. It's pretty easy to +#' rationalize the first, and we may already have wide data in the input data +#' frame anyway (take a look at the [`illness`] and [`towny`] datasets for +#' examples of this). There's one data value per column so the key thing here is +#' to reference the columns in the correct order. With a select helper, good +#' column naming, and the columns being in the intended order, this is a snap. #' -#' The second option is to use text-based number streams. Sometimes you simply +#' The second option is to use text-based value streams. Sometimes you simply #' don't want or don't need multiple columns and so a single column with all of #' the data might be more practical. To make this work, you'd need to have a set #' of numerical values separated by some sort of delimiter (could be a comma, a #' space, a semicolon, you get the idea). Here's an example with three numbers, #' written three ways: `"3.6 -2.44 1.98"`, `"3.6, -2.44, 1.98"`, and #' `"3.6;-2.44;1.98"`. You can include `NA` values, not a problem, and here's an -#' example of that: `"6.232 NA 3.7 0.93"`. This number streams can be pretty big -#' if you want them to be, and you don't have to deal with columns to the -#' *n*th degree as in *Option 1*. For the case where you need to provide two -#' sets of values (*x* and *y*, for line plots with `columns` and -#' `columns_x_vals`), have two equivalently sized number streams in two columns. -#' Number streams can also be concatenated together by referencing columns -#' having their own separate number streams. +#' example of that: `"6.232 NA 3.7 0.93"`. Another form of value stream involves +#' using datetimes in the ISO 8601 form of `YYYY-MM-DD HH:MM:SS`. These will +#' be internally converted to numeric values (seconds elapsed since +#' `"1970-01-01 00:00:00"`). An example of a datetime-based value stream is: +#' `"2012-06-12 08:24:13, 2012-06-12 10:37:08, 2012-06-12 14:03:24"`. +#' +#' Value streams can be pretty big if you want them to be, and you don't have to +#' deal with containing individual values across multiple columns. For the case +#' where you need to provide two sets of values (*x* and *y*, for line plots +#' with `columns` and `columns_x_vals`), have two equivalently sized value +#' streams in two columns. Value streams can also be concatenated together by +#' referencing columns having their own separate value streams. #' #' @section Reference line and reference area: #' @@ -2412,6 +2426,50 @@ cols_add <- function( #' `r man_get_image_tag(file = "man_cols_nanoplot_1.png")` #' }} #' +#' The previous table showed us some line-based nanoplots. We can also make very +#' small bar plots with `cols_nanoplot()`. Let's take the [`pizzaplace`] dataset +#' and make a small summary table showing daily pizza sales by type (there are +#' four types). This will be limited to the first ten days of pizza sales in +#' 2015, so, there will be ten rows in total. We can use `plot_type = "bar"` to +#' make bar plots from the daily sales counts in the `chicken`, `classic`, +#' `supreme`, and `veggie` columns. Because we know there will always be four +#' bars (one for each type of pizza) we can be a little creative and apply +#' colors to each of the bars through use of the `data_bar_fill_color` argument +#' in [nanoplot_options()]. +#' +#' ```r +#' pizzaplace |> +#' dplyr::select(type, date) |> +#' dplyr::group_by(date, type) |> +#' dplyr::summarize(sold = dplyr::n(), .groups = "drop") |> +#' tidyr::pivot_wider(names_from = type, values_from = sold) |> +#' dplyr::slice_head(n = 10) |> +#' gt(rowname_col = "date") |> +#' tab_header( +#' title = md("First Ten Days of Pizza Sales in 2015") +#' ) |> +#' cols_nanoplot( +#' columns = c(chicken, classic, supreme, veggie), +#' plot_type = "bar", +#' new_col_name = "pizzas_sold", +#' new_col_label = "Sales by Type", +#' options = nanoplot_options( +#' show_data_line = FALSE, +#' show_data_area = FALSE, +#' data_bar_stroke_color = "transparent", +#' data_bar_fill_color = c("brown", "gold", "purple", "green") +#' ) +#' ) |> +#' cols_width(pizzas_sold ~ px(150)) |> +#' cols_align(columns = -date, align = "center") |> +#' fmt_date(columns = date, date_style = "yMMMEd") |> +#' opt_all_caps() +#' ``` +#' +#' \if{html}{\out{ +#' `r man_get_image_tag(file = "man_cols_nanoplot_2.png")` +#' }} +#' #' Now we'll make another table that contains two columns of nanoplots. Starting #' from the [`towny`] dataset, we first reduce it down to a subset of columns #' and rows. All of the columns related to either population or density will be @@ -2464,7 +2522,7 @@ cols_add <- function( #' ``` #' #' \if{html}{\out{ -#' `r man_get_image_tag(file = "man_cols_nanoplot_2.png")` +#' `r man_get_image_tag(file = "man_cols_nanoplot_3.png")` #' }} #' #' The [`sza`] dataset can, with just some use of **dplyr** and **tidyr**, give @@ -2522,46 +2580,47 @@ cols_add <- function( #' ``` #' #' \if{html}{\out{ -#' `r man_get_image_tag(file = "man_cols_nanoplot_3.png")` +#' `r man_get_image_tag(file = "man_cols_nanoplot_4.png")` #' }} #' -#' You can use number streams as data for nanoplots. Let's demonstrate how we -#' can make use of them with some creative transformation of the [`pizzaplace`] -#' dataset. A number stream is really a string with delimited numeric values, -#' like this: `"7.24 84.2 14"`. They can be more convenient to use since -#' different rows/nanoplots can have varying amounts of data. There are `date` -#' and `time` columns in this dataset and we'll use that to get *x* values -#' denoting high-resolution time instants: the second of the day that a pizza -#' was sold (this is true pizza data science). We also have the sell price for a -#' pizza, and that'll serve as the *y* values. The pizzas belong to four -#' different groups (in the `type` column) and we'll group by that and create -#' number streams with `paste(..., collapse = ",")` in the **dplyr** summarize -#' call. With two number streams in each row (having the same number of values) -#' we can now make a **gt** table with nanoplots. +#' You can use number and time streams as data for nanoplots. Let's demonstrate +#' how we can make use of them with some creative transformation of the +#' [`pizzaplace`] dataset. A value stream is really a string with delimited +#' numeric values, like this: `"7.24,84.2,14"`. A value stream can also contain +#' dates and/or datetimes, and here's an example of that: +#' `"2020-06-02 13:05:13,2020-06-02 14:24:05,2020-06-02 18:51:37"`. Having data +#' in this form can often be more convenient since different nanoplots might +#' have varying amounts of data (and holding different amounts of data in a +#' fixed number of columns is cumbersome). There are `date` and `time` columns +#' in this dataset and we'll use that to get *x* values denoting high-resolution +#' time instants: the second of the day that a pizza was sold (this is true +#' pizza analytics). We also have the sell price for a pizza, and that'll serve +#' as the *y* values. The pizzas belong to four different groups (in the `type` +#' column) and we'll group by that and create value streams with +#' `paste(..., collapse = ",")` in the **dplyr** summarize call. With two value +#' streams in each row (having the same number of values) we can now make a +#' **gt** table with nanoplots. #' #' ```r #' pizzaplace |> #' dplyr::filter(date == "2015-01-01") |> -#' dplyr::mutate( -#' s_day = as.numeric( -#' vec_fmt_datetime(paste(date, time), format = "A") -#' ) / 1000 -#' ) |> -#' dplyr::select(type, s_day, price) |> +#' dplyr::mutate(date_time = paste(date, time)) |> +#' dplyr::select(type, date_time, price) |> #' dplyr::group_by(type) |> #' dplyr::summarize( -#' s_day = paste(s_day, collapse = ","), +#' date_time = paste(date_time, collapse = ","), #' sold = paste(price, collapse = ",") #' ) |> #' gt(rowname_col = "type") |> #' tab_header( #' title = md("Pizzas sold on **January 1, 2015**"), -#' subtitle = "Between the opening hours of 09:00 to 00:00" +#' subtitle = "Between the opening hours of 11:30 to 22:30" #' ) |> -#' cols_hide(columns = c(s_day, sold)) |> +#' cols_hide(columns = c(date_time, sold)) |> #' cols_nanoplot( #' columns = sold, -#' columns_x_vals = s_day, +#' columns_x_vals = date_time, +#' expand_x = c("2015-01-01 11:30", "2015-01-01 22:30"), #' reference_line = "median", #' new_col_name = "pizzas_sold", #' new_col_label = "Pizzas Sold", @@ -2577,10 +2636,10 @@ cols_add <- function( #' ``` #' #' \if{html}{\out{ -#' `r man_get_image_tag(file = "man_cols_nanoplot_4.png")` +#' `r man_get_image_tag(file = "man_cols_nanoplot_5.png")` #' }} #' -#' Notice that we hid the columns containing the number streams with +#' Notice that we hid the columns containing the value streams with #' [cols_hide()] because, while useful, they don't need to be displayed to #' anybody viewing a table. We have a lot of data points and a connecting line #' is not as valuable here. It's more interesting to see the clusters of the @@ -2610,6 +2669,8 @@ cols_nanoplot <- function( columns_x_vals = NULL, reference_line = NULL, reference_area = NULL, + expand_x = NULL, + expand_y = NULL, new_col_name = NULL, new_col_label = NULL, before = NULL, @@ -2701,10 +2762,15 @@ cols_nanoplot <- function( y_ref_line = reference_line, y_ref_area = reference_area, x_vals = data_vals_plot_x_i, + expand_x = expand_x, + expand_y = expand_y, missing_vals = missing_vals, plot_type = plot_type, line_type = options_plots$data_line_type, currency = options_plots$currency, + y_val_fmt_fn = options_plots$y_val_fmt_fn, + y_axis_fmt_fn = options_plots$y_axis_fmt_fn, + y_ref_line_fmt_fn = options_plots$y_ref_line_fmt_fn, data_point_radius = options_plots$data_point_radius, data_point_stroke_color = options_plots$data_point_stroke_color, data_point_stroke_width = options_plots$data_point_stroke_width, @@ -2717,6 +2783,8 @@ cols_nanoplot <- function( data_bar_negative_stroke_color = options_plots$data_bar_negative_stroke_color, data_bar_negative_stroke_width = options_plots$data_bar_negative_stroke_width, data_bar_negative_fill_color = options_plots$data_bar_negative_fill_color, + reference_line_color = options_plots$reference_line_color, + reference_area_fill_color = options_plots$reference_area_fill_color, vertical_guide_stroke_color = options_plots$vertical_guide_stroke_color, vertical_guide_stroke_width = options_plots$vertical_guide_stroke_width, show_data_points = options_plots$show_data_points, @@ -2851,8 +2919,23 @@ generate_data_vals_list <- function( is.character(data_vals_i[j][[1]]) ) { - data_vals_j <- - c(data_vals_j, process_number_stream(data_vals_i[j][[1]])) + # + # Detect value stream type and convert accordingly + # + + if ( + grepl("\\d{1,4}-\\d{2}-\\d{2}", data_vals_i[j][[1]]) + ) { + + data_vals_j <- + c(data_vals_j, process_time_stream(data_vals_i[j][[1]])) + + } else { + + data_vals_j <- + c(data_vals_j, process_number_stream(data_vals_i[j][[1]])) + } + } else { data_vals_j <- c(data_vals_j, unname(unlist(data_vals_i[j][[1]]))) diff --git a/R/shiny.R b/R/shiny.R index baefd3a854..efebfb6eb9 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -39,7 +39,7 @@ #' #' We need to ensure that we have the **shiny** package installed first. This #' is easily by using `install.packages("shiny")`. More information on creating -#' Shiny apps can be found at the \href{https://shiny.rstudio.com}{Shiny Site}. +#' Shiny apps can be found on the \href{https://shiny.posit.co}{Shiny website}. #' #' @param expr *Expression* #' @@ -212,7 +212,7 @@ render_gt <- function( #' #' We need to ensure that we have the **shiny** package installed first. This #' is easily by using `install.packages("shiny")`. More information on creating -#' Shiny apps can be found at the \href{https://shiny.rstudio.com}{Shiny Site}. +#' Shiny apps can be found on the \href{https://shiny.posit.co}{Shiny website}. #' #' @param outputId *Shiny output ID* #' diff --git a/R/utils_plots.R b/R/utils_plots.R index 1b07fefc0b..9c1519b047 100644 --- a/R/utils_plots.R +++ b/R/utils_plots.R @@ -27,10 +27,15 @@ generate_nanoplot <- function( y_ref_line = NULL, y_ref_area = NULL, x_vals = NULL, + expand_x = NULL, + expand_y = NULL, missing_vals = c("gap", "zero", "remove"), plot_type = c("line", "bar"), line_type = c("curved", "straight"), currency = NULL, + y_val_fmt_fn = NULL, + y_axis_fmt_fn = NULL, + y_ref_line_fmt_fn = NULL, data_point_radius = 10, data_point_stroke_color = "#FFFFFF", data_point_stroke_width = 4, @@ -43,6 +48,8 @@ generate_nanoplot <- function( data_bar_negative_stroke_color = "#CC3243", data_bar_negative_stroke_width = 4, data_bar_negative_fill_color = "#D75A68", + reference_line_color = "#75A8B0", + reference_area_fill_color = "#A6E6F2", vertical_guide_stroke_color = "#911EB4", vertical_guide_stroke_width = 12, show_data_points = TRUE, @@ -147,12 +154,11 @@ generate_nanoplot <- function( } } + # Determine the total number of `y` values available num_y_vals <- length(y_vals) - # Get a vector of data points that are missing and are to be treated as gaps - if (missing_vals == "gap") { - y_vals_gaps <- which(is.na(y_vals)) - } + # Find out whether the collection of non-NA `y` values are all integer-like + y_vals_integerlike <- rlang::is_integerish(y_vals) # Ensure that a reference line or reference area isn't shown if NULL or # any of its directives is NA @@ -293,20 +299,19 @@ generate_nanoplot <- function( } # Scale to proportional values - y_proportions_w_ref_line_area <- - normalize_vals( - c( - y_vals, - y_ref_line[1], - y_ref_area_l, - y_ref_area_u - ) + y_proportions_list <- + normalize_to_list( + vals = y_vals, + ref_line = y_ref_line[1], + ref_area_l = y_ref_area_l, + ref_area_u = y_ref_area_u, + expand_y = expand_y ) - y_proportion_ref_line <- y_proportions_w_ref_line_area[-(1:num_y_vals)][1] - y_proportions_ref_area_l <- y_proportions_w_ref_line_area[-(1:num_y_vals)][2] - y_proportions_ref_area_u <- y_proportions_w_ref_line_area[-(1:num_y_vals)][3] - y_proportions <- y_proportions_w_ref_line_area[(1:num_y_vals)] + y_proportions <- y_proportions_list[["vals"]] + y_proportion_ref_line <- y_proportions_list[["ref_line"]] + y_proportions_ref_area_l <- y_proportions_list[["ref_area_l"]] + y_proportions_ref_area_u <- y_proportions_list[["ref_area_u"]] # Scale reference line and reference area boundaries data_y_ref_line <- safe_y_d + ((1 - y_proportion_ref_line) * data_y_height) @@ -332,16 +337,15 @@ generate_nanoplot <- function( } # Scale to proportional values - y_proportions_w_ref_line <- - normalize_vals( - c( - y_vals, - y_ref_line[1] - ) + y_proportions_list <- + normalize_to_list( + vals = y_vals, + ref_line = y_ref_line[1], + expand_y = expand_y ) - y_proportion_ref_line <- y_proportions_w_ref_line[length(y_proportions_w_ref_line)] - y_proportions <- y_proportions_w_ref_line[-length(y_proportions_w_ref_line)] + y_proportions <- y_proportions_list[["vals"]] + y_proportion_ref_line <- y_proportions_list[["ref_line"]] # Scale reference line data_y_ref_line <- safe_y_d + ((1 - y_proportion_ref_line) * data_y_height) @@ -394,18 +398,17 @@ generate_nanoplot <- function( } # Scale to proportional values - y_proportions_w_ref_area <- - normalize_vals( - c( - y_vals, - y_ref_area_l, - y_ref_area_u - ) + y_proportions_list <- + normalize_to_list( + vals = y_vals, + ref_area_l = y_ref_area_l, + ref_area_u = y_ref_area_u, + expand_y = expand_y ) - y_proportions_ref_area_l <- y_proportions_w_ref_area[-(1:num_y_vals)][1] - y_proportions_ref_area_u <- y_proportions_w_ref_area[-(1:num_y_vals)][2] - y_proportions <- y_proportions_w_ref_area[(1:num_y_vals)] + y_proportions <- y_proportions_list[["vals"]] + y_proportions_ref_area_l <- y_proportions_list[["ref_area_l"]] + y_proportions_ref_area_u <- y_proportions_list[["ref_area_u"]] # Scale reference area boundaries data_y_ref_area_l <- safe_y_d + ((1 - y_proportions_ref_area_l) * data_y_height) @@ -414,7 +417,15 @@ generate_nanoplot <- function( } else { # Case where there is no reference line or reference area - y_proportions <- normalize_vals(y_vals) + + # Scale to proportional values + y_proportions_list <- + normalize_to_list( + vals = y_vals, + expand_y = expand_y + ) + + y_proportions <- y_proportions_list[["vals"]] } } @@ -490,29 +501,21 @@ generate_nanoplot <- function( } # Scale to proportional values - y_proportions_w_ref_line_area_list <- - normalize_vals_with_zero( - c( - y_vals, - y_ref_line[1], - y_ref_area_l, - y_ref_area_u - ) + y_proportions_list <- + normalize_to_list( + vals = y_vals, + ref_line = y_ref_line[1], + ref_area_l = y_ref_area_l, + ref_area_u = y_ref_area_u, + zero = 0, + expand_y = expand_y ) - y_proportions_zero <- y_proportions_w_ref_line_area_list[["zero"]] - - y_proportion_ref_line <- - y_proportions_w_ref_line_area_list[["vals"]][-(1:num_y_vals)][1] - - y_proportions_ref_area_l <- - y_proportions_w_ref_line_area_list[["vals"]][-(1:num_y_vals)][2] - - y_proportions_ref_area_u <- - y_proportions_w_ref_line_area_list[["vals"]][-(1:num_y_vals)][3] - - y_proportions <- - y_proportions_w_ref_line_area_list[["vals"]][(1:num_y_vals)] + y_proportions <- y_proportions_list[["vals"]] + y_proportion_ref_line <- y_proportions_list[["ref_line"]] + y_proportions_ref_area_l <- y_proportions_list[["ref_area_l"]] + y_proportions_ref_area_u <- y_proportions_list[["ref_area_u"]] + y_proportions_zero <- y_proportions_list[["zero"]] # Scale reference line and reference area boundaries data_y_ref_line <- safe_y_d + ((1 - y_proportion_ref_line) * data_y_height) @@ -538,21 +541,17 @@ generate_nanoplot <- function( } # Scale to proportional values - y_proportions_w_ref_line_list <- - normalize_vals_with_zero( - c( - y_vals, - y_ref_line[1] - ) + y_proportions_list <- + normalize_to_list( + vals = y_vals, + ref_line = y_ref_line[1], + zero = 0, + expand_y = expand_y ) - y_proportions_zero <- y_proportions_w_ref_line_list[["zero"]] - - y_proportion_ref_line <- - y_proportions_w_ref_line_list[["vals"]][num_y_vals + 1] - - y_proportions <- - y_proportions_w_ref_line_list[["vals"]][-(num_y_vals + 1)] + y_proportions <- y_proportions_list[["vals"]] + y_proportion_ref_line <- y_proportions_list[["ref_line"]] + y_proportions_zero <- y_proportions_list[["zero"]] # Scale reference line data_y_ref_line <- safe_y_d + ((1 - y_proportion_ref_line) * data_y_height) @@ -605,25 +604,19 @@ generate_nanoplot <- function( } # Scale to proportional values - y_proportions_w_ref_area_list <- - normalize_vals_with_zero( - c( - y_vals, - y_ref_area_l, - y_ref_area_u - ) + y_proportions_list <- + normalize_to_list( + vals = y_vals, + ref_area_l = y_ref_area_l, + ref_area_u = y_ref_area_u, + zero = 0, + expand_y = expand_y ) - y_proportions_zero <- y_proportions_w_ref_area_list[["zero"]] - - y_proportions_ref_area_l <- - y_proportions_w_ref_area_list[["vals"]][-(1:num_y_vals)][1] - - y_proportions_ref_area_u <- - y_proportions_w_ref_area_list[["vals"]][-(1:num_y_vals)][2] - - y_proportions <- - y_proportions_w_ref_area_list[["vals"]][(1:num_y_vals)] + y_proportions <- y_proportions_list[["vals"]] + y_proportions_ref_area_l <- y_proportions_list[["ref_area_l"]] + y_proportions_ref_area_u <- y_proportions_list[["ref_area_u"]] + y_proportions_zero <- y_proportions_list[["zero"]] # Scale reference area boundaries data_y_ref_area_l <- safe_y_d + ((1 - y_proportions_ref_area_l) * data_y_height) @@ -633,9 +626,16 @@ generate_nanoplot <- function( # Case where there is no reference line or reference area - y_proportions_list <- normalize_vals_with_zero(y_vals) - y_proportions_zero <- y_proportions_list[["zero"]] + # Scale to proportional values + y_proportions_list <- + normalize_to_list( + vals = y_vals, + zero = 0, + expand_y = expand_y + ) + y_proportions <- y_proportions_list[["vals"]] + y_proportions_zero <- y_proportions_list[["zero"]] } data_y0_point <- safe_y_d + ((1 - y_proportions_zero) * data_y_height) @@ -645,8 +645,22 @@ generate_nanoplot <- function( # there are no x values, generate equally-spaced x values according # to the number of y values if (plot_type == "line" && !is.null(x_vals)) { - x_proportions <- normalize_vals(x_vals) + + if (!is.null(expand_x) && is.character(expand_x)) { + expand_x <- as.numeric(as.POSIXct(expand_x, tz = "UTC")) + } + + # Scale to proportional values + x_proportions_list <- + normalize_to_list( + vals = unname(x_vals), + expand_x = expand_x + ) + + x_proportions <- x_proportions_list[["vals"]] + } else { + x_proportions <- seq(0, 1, length.out = num_y_vals) } @@ -870,7 +884,7 @@ generate_nanoplot <- function( "r=\"", data_point_radius_i + (data_point_radius_i / 2), "\" ", "stroke=\"", "red", "\" ", "stroke-width=\"", data_bar_stroke_width_i, "\" ", - "fill=\"", "white", "\" ", + "fill=\"transparent\" ", ">", "", "" @@ -902,9 +916,9 @@ generate_nanoplot <- function( bar_strings_i <- paste0( "" ) - y_value_i <- format_number_compactly(val = y_vals[i], currency = currency) + y_value_i <- + format_number_compactly( + val = y_vals[i], + currency = currency, + as_integer = y_vals_integerlike, + fn = y_val_fmt_fn + ) x_text <- data_x_points[i] + 10 @@ -1368,6 +1398,7 @@ normalize_option_vector <- function(vec, num_y_vals) { } normalize_vals <- function(x) { + x_missing <- which(is.na(x)) mean_x <- mean(x, na.rm = TRUE) x[x_missing] <- mean_x @@ -1381,14 +1412,30 @@ normalize_vals <- function(x) { x } -normalize_vals_with_zero <- function(x) { +normalize_to_list <- function(...) { + + value_list <- list(...) + + if (!rlang::is_named(value_list)) { + cli::cli_abort("All vectors provided to `...` must be named.") + } + + value_list_vec_nm <- gsub("\\d+", "", names(unlist(value_list))) + value_list_unique_nm <- names(value_list) + value_list_vec <- unlist(value_list) + + if (length(unique(value_list_vec)) == 1) { + value_list_vec <- jitter(value_list_vec, amount = 1 / 100000) + } + + values_normalized <- normalize_vals(value_list_vec) - normalized <- normalize_vals(c(0, x)) + for (i in seq_along(value_list_unique_nm)) { + value_list[[value_list_unique_nm[i]]] <- + values_normalized[value_list_vec_nm == value_list_unique_nm[i]] + } - list( - zero = normalized[1], - vals = normalized[-1] - ) + value_list } mad_double <- function(x) { @@ -1448,7 +1495,16 @@ generate_ref_line_from_keyword <- function(vals, keyword) { ref_line } -format_number_compactly <- function(val, currency) { +format_number_compactly <- function( + val, + currency = NULL, + as_integer = FALSE, + fn = NULL +) { + + if (!is.null(fn)) { + return(fn(val)) + } if (is.na(val)) { return("NA") @@ -1576,14 +1632,25 @@ format_number_compactly <- function(val, currency) { } else { - val_formatted <- - vec_fmt_number( - val, - n_sigfig = n_sigfig, - decimals = 1, - suffixing = suffixing, - output = "html" - ) + if (as_integer && val > -100 && val < 100) { + + val_formatted <- + vec_fmt_integer( + val, + output = "html" + ) + + } else { + + val_formatted <- + vec_fmt_number( + val, + n_sigfig = n_sigfig, + decimals = 1, + suffixing = suffixing, + output = "html" + ) + } } } @@ -1600,3 +1667,15 @@ process_number_stream <- function(number_stream) { number_stream <- as.numeric(number_stream) number_stream } + +process_time_stream <- function(time_stream) { + + time_stream <- unlist(strsplit(time_stream, split = "\\s*[;,]\\s*")) + time_stream <- gsub("T", " ", time_stream) + + time_stream_vals <- as.POSIXct(time_stream, tz = "UTC") + time_stream_vals <- as.numeric(time_stream_vals) + + names(time_stream_vals) <- time_stream + time_stream_vals +} diff --git a/README.md b/README.md index ad03b24380..515441bc09 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ [![Twitter Follow](https://img.shields.io/twitter/follow/gt_package?style=social)](https://twitter.com/gt_package) [![Posit Cloud](https://img.shields.io/badge/Posit%20Cloud-gt%20Test%20Drive-blue?style=social&logo=rstudio&logoColor=75AADB)](https://rstudio.cloud/project/779965) -[![Discord](https://img.shields.io/discord/1086103944280952992?color=%237289da&label=Discord)](https://discord.gg/Ux7nrcXHVV) +[![Discord](https://img.shields.io/discord/1086103944280952992?color=%237289da&label=Discord)](https://discord.com/invite/Ux7nrcXHVV) Contributor Covenant @@ -118,9 +118,9 @@ One such place is in [*GitHub Discussions*](https://github.com/rstudio/gt/discus [![GitHub Discussions](https://img.shields.io/badge/GitHub%20Discussions-Ask%20about%20anything-blue?style=social&logo=github&logoColor=gray)](https://github.com/rstudio/gt/discussions) -Another fine venue for discussion is in the [`gt_package` *Discord server*](https://discord.gg/Ux7nrcXHVV). This is a good option for asking about the development of **gt**, pitching ideas that may become features, and sharing your table creations! +Another fine venue for discussion is in the [`gt_package` *Discord server*](https://discord.com/invite/Ux7nrcXHVV). This is a good option for asking about the development of **gt**, pitching ideas that may become features, and sharing your table creations! -[![Discord Server](https://img.shields.io/badge/Discord-Chat%20with%20us-blue?style=social&logo=discord&logoColor=purple)](https://discord.gg/Ux7nrcXHVV) +[![Discord Server](https://img.shields.io/badge/Discord-Chat%20with%20us-blue?style=social&logo=discord&logoColor=purple)](https://discord.com/invite/Ux7nrcXHVV) Finally, there is the [`gt_package` *Twitter account*](https://twitter.com/gt_package). There you'll find tweets about **gt** (including sneak previews about in-development features) and other table-generation packages. @@ -156,7 +156,7 @@ There are several **R** packages that either use **gt** to generate tabular outp - **gtsummary** ([GITHUB](https://github.com/ddsjoberg/gtsummary), [WEBSITE](https://www.danieldsjoberg.com/gtsummary/)) - **gtExtras** ([GITHUB](https://github.com/jthomasmock/gtExtras), [WEBSITE](https://jthomasmock.github.io/gtExtras/)) - - **pointblank** ([GITHUB](https://github.com/rich-iannone/pointblank), [WEBSITE](https://rich-iannone.github.io/pointblank/)) + - **pointblank** ([GITHUB](https://github.com/rstudio/pointblank), [WEBSITE](https://rstudio.github.io/pointblank/)) - **tfrmt** ([GITHUB](https://github.com/GSK-Biostatistics/tfrmt), [WEBSITE](https://gsk-biostatistics.github.io/tfrmt/)) - **gto** ([GITHUB](https://github.com/GSK-Biostatistics/gto)) diff --git a/images/man_cols_nanoplot_2.png b/images/man_cols_nanoplot_2.png index cae82a8852..b68e7aaa25 100644 Binary files a/images/man_cols_nanoplot_2.png and b/images/man_cols_nanoplot_2.png differ diff --git a/images/man_cols_nanoplot_3.png b/images/man_cols_nanoplot_3.png index bb638c244a..cae82a8852 100644 Binary files a/images/man_cols_nanoplot_3.png and b/images/man_cols_nanoplot_3.png differ diff --git a/images/man_cols_nanoplot_4.png b/images/man_cols_nanoplot_4.png index adb5daedc8..bb638c244a 100644 Binary files a/images/man_cols_nanoplot_4.png and b/images/man_cols_nanoplot_4.png differ diff --git a/images/man_cols_nanoplot_5.png b/images/man_cols_nanoplot_5.png new file mode 100644 index 0000000000..96cb816f04 Binary files /dev/null and b/images/man_cols_nanoplot_5.png differ diff --git a/man/cols_nanoplot.Rd b/man/cols_nanoplot.Rd index 418ee7ff0f..2b177d3981 100644 --- a/man/cols_nanoplot.Rd +++ b/man/cols_nanoplot.Rd @@ -14,6 +14,8 @@ cols_nanoplot( columns_x_vals = NULL, reference_line = NULL, reference_area = NULL, + expand_x = NULL, + expand_y = NULL, new_col_name = NULL, new_col_label = NULL, before = NULL, @@ -119,6 +121,15 @@ one of the following keywords for the generation of the value: (1) (7) \code{"first"}, or (8) \code{"last"}. Input can either be a vector or list with two elements.} +\item{expand_x, expand_y}{\emph{Expand plot scale in the x and y directions} + +\verb{vector} // \emph{default:} \code{NULL} (\code{optional}) + +Should you need to have plots expand in the \emph{x} or \emph{y} direction, provide +one or more values to \code{expand_x} or \code{expand_y}. Any values provided that +are outside of the range of data provided to the plot should result in a +scale expansion.} + \item{new_col_name}{\emph{Column name for the new column containing the plots} \verb{scalar} // \emph{default:} \code{NULL} (\code{optional}) @@ -236,29 +247,34 @@ in the table) and returns a logical vector. \section{How to supply data for nanoplots}{ -The input data for nanoplots naturally needs to be numeric and there are -two major ways to formulate that data: (1) from single values across many -columns, and (2) using text-based number streams. It's pretty to rationalize -the first, and we may already have wide data in the input data frame anyway -(take a look at the \code{\link{illness}} and \code{\link{towny}} datasets for examples of this). -There's one data value per column so the key thing here is to reference the -columns in the correct order. With a select helper, good column naming, and -the columns being in the intended order, this is a snap. +The input data for nanoplots naturally needs to be numeric and there are two +major ways to formulate that data: (1) from single values across many +columns, and (2) using text-based value streams. It's pretty easy to +rationalize the first, and we may already have wide data in the input data +frame anyway (take a look at the \code{\link{illness}} and \code{\link{towny}} datasets for +examples of this). There's one data value per column so the key thing here is +to reference the columns in the correct order. With a select helper, good +column naming, and the columns being in the intended order, this is a snap. -The second option is to use text-based number streams. Sometimes you simply +The second option is to use text-based value streams. Sometimes you simply don't want or don't need multiple columns and so a single column with all of the data might be more practical. To make this work, you'd need to have a set of numerical values separated by some sort of delimiter (could be a comma, a space, a semicolon, you get the idea). Here's an example with three numbers, written three ways: \code{"3.6 -2.44 1.98"}, \code{"3.6, -2.44, 1.98"}, and \code{"3.6;-2.44;1.98"}. You can include \code{NA} values, not a problem, and here's an -example of that: \code{"6.232 NA 3.7 0.93"}. This number streams can be pretty big -if you want them to be, and you don't have to deal with columns to the -\emph{n}th degree as in \emph{Option 1}. For the case where you need to provide two -sets of values (\emph{x} and \emph{y}, for line plots with \code{columns} and -\code{columns_x_vals}), have two equivalently sized number streams in two columns. -Number streams can also be concatenated together by referencing columns -having their own separate number streams. +example of that: \code{"6.232 NA 3.7 0.93"}. Another form of value stream involves +using datetimes in the ISO 8601 form of \verb{YYYY-MM-DD HH:MM:SS}. These will +be internally converted to numeric values (seconds elapsed since +\code{"1970-01-01 00:00:00"}). An example of a datetime-based value stream is: +\code{"2012-06-12 08:24:13, 2012-06-12 10:37:08, 2012-06-12 14:03:24"}. + +Value streams can be pretty big if you want them to be, and you don't have to +deal with containing individual values across multiple columns. For the case +where you need to provide two sets of values (\emph{x} and \emph{y}, for line plots +with \code{columns} and \code{columns_x_vals}), have two equivalently sized value +streams in two columns. Value streams can also be concatenated together by +referencing columns having their own separate value streams. } \section{Reference line and reference area}{ @@ -319,6 +335,49 @@ possible to define a column label here using the \code{new_col_label} argument. This image of a table was generated from the first code example in the `cols_nanoplot()` help file. }} +The previous table showed us some line-based nanoplots. We can also make very +small bar plots with \code{cols_nanoplot()}. Let's take the \code{\link{pizzaplace}} dataset +and make a small summary table showing daily pizza sales by type (there are +four types). This will be limited to the first ten days of pizza sales in +2015, so, there will be ten rows in total. We can use \code{plot_type = "bar"} to +make bar plots from the daily sales counts in the \code{chicken}, \code{classic}, +\code{supreme}, and \code{veggie} columns. Because we know there will always be four +bars (one for each type of pizza) we can be a little creative and apply +colors to each of the bars through use of the \code{data_bar_fill_color} argument +in \code{\link[=nanoplot_options]{nanoplot_options()}}. + +\if{html}{\out{
}}\preformatted{pizzaplace |> + dplyr::select(type, date) |> + dplyr::group_by(date, type) |> + dplyr::summarize(sold = dplyr::n(), .groups = "drop") |> + tidyr::pivot_wider(names_from = type, values_from = sold) |> + dplyr::slice_head(n = 10) |> + gt(rowname_col = "date") |> + tab_header( + title = md("First Ten Days of Pizza Sales in 2015") + ) |> + cols_nanoplot( + columns = c(chicken, classic, supreme, veggie), + plot_type = "bar", + new_col_name = "pizzas_sold", + new_col_label = "Sales by Type", + options = nanoplot_options( + show_data_line = FALSE, + show_data_area = FALSE, + data_bar_stroke_color = "transparent", + data_bar_fill_color = c("brown", "gold", "purple", "green") + ) + ) |> + cols_width(pizzas_sold ~ px(150)) |> + cols_align(columns = -date, align = "center") |> + fmt_date(columns = date, date_style = "yMMMEd") |> + opt_all_caps() +}\if{html}{\out{
}} + +\if{html}{\out{ +This image of a table was generated from the second code example in the `cols_nanoplot()` help file. +}} + Now we'll make another table that contains two columns of nanoplots. Starting from the \code{\link{towny}} dataset, we first reduce it down to a subset of columns and rows. All of the columns related to either population or density will be @@ -370,7 +429,7 @@ elect to hide most of those with \code{\link[=cols_hide]{cols_hide()}}. }\if{html}{\out{}} \if{html}{\out{ -This image of a table was generated from the second code example in the `cols_nanoplot()` help file. +This image of a table was generated from the third code example in the `cols_nanoplot()` help file. }} The \code{\link{sza}} dataset can, with just some use of \strong{dplyr} and \strong{tidyr}, give @@ -427,45 +486,46 @@ bars. }\if{html}{\out{}} \if{html}{\out{ -This image of a table was generated from the third code example in the `cols_nanoplot()` help file. +This image of a table was generated from the fourth code example in the `cols_nanoplot()` help file. }} -You can use number streams as data for nanoplots. Let's demonstrate how we -can make use of them with some creative transformation of the \code{\link{pizzaplace}} -dataset. A number stream is really a string with delimited numeric values, -like this: \code{"7.24 84.2 14"}. They can be more convenient to use since -different rows/nanoplots can have varying amounts of data. There are \code{date} -and \code{time} columns in this dataset and we'll use that to get \emph{x} values -denoting high-resolution time instants: the second of the day that a pizza -was sold (this is true pizza data science). We also have the sell price for a -pizza, and that'll serve as the \emph{y} values. The pizzas belong to four -different groups (in the \code{type} column) and we'll group by that and create -number streams with \code{paste(..., collapse = ",")} in the \strong{dplyr} summarize -call. With two number streams in each row (having the same number of values) -we can now make a \strong{gt} table with nanoplots. +You can use number and time streams as data for nanoplots. Let's demonstrate +how we can make use of them with some creative transformation of the +\code{\link{pizzaplace}} dataset. A value stream is really a string with delimited +numeric values, like this: \code{"7.24,84.2,14"}. A value stream can also contain +dates and/or datetimes, and here's an example of that: +\code{"2020-06-02 13:05:13,2020-06-02 14:24:05,2020-06-02 18:51:37"}. Having data +in this form can often be more convenient since different nanoplots might +have varying amounts of data (and holding different amounts of data in a +fixed number of columns is cumbersome). There are \code{date} and \code{time} columns +in this dataset and we'll use that to get \emph{x} values denoting high-resolution +time instants: the second of the day that a pizza was sold (this is true +pizza analytics). We also have the sell price for a pizza, and that'll serve +as the \emph{y} values. The pizzas belong to four different groups (in the \code{type} +column) and we'll group by that and create value streams with +\code{paste(..., collapse = ",")} in the \strong{dplyr} summarize call. With two value +streams in each row (having the same number of values) we can now make a +\strong{gt} table with nanoplots. \if{html}{\out{
}}\preformatted{pizzaplace |> dplyr::filter(date == "2015-01-01") |> - dplyr::mutate( - s_day = as.numeric( - vec_fmt_datetime(paste(date, time), format = "A") - ) / 1000 - ) |> - dplyr::select(type, s_day, price) |> + dplyr::mutate(date_time = paste(date, time)) |> + dplyr::select(type, date_time, price) |> dplyr::group_by(type) |> dplyr::summarize( - s_day = paste(s_day, collapse = ","), + date_time = paste(date_time, collapse = ","), sold = paste(price, collapse = ",") ) |> gt(rowname_col = "type") |> tab_header( title = md("Pizzas sold on **January 1, 2015**"), - subtitle = "Between the opening hours of 09:00 to 00:00" + subtitle = "Between the opening hours of 11:30 to 22:30" ) |> - cols_hide(columns = c(s_day, sold)) |> + cols_hide(columns = c(date_time, sold)) |> cols_nanoplot( columns = sold, - columns_x_vals = s_day, + columns_x_vals = date_time, + expand_x = c("2015-01-01 11:30", "2015-01-01 22:30"), reference_line = "median", new_col_name = "pizzas_sold", new_col_label = "Pizzas Sold", @@ -481,10 +541,10 @@ we can now make a \strong{gt} table with nanoplots. }\if{html}{\out{
}} \if{html}{\out{ -This image of a table was generated from the fourth code example in the `cols_nanoplot()` help file. +This image of a table was generated from the fifth code example in the `cols_nanoplot()` help file. }} -Notice that we hid the columns containing the number streams with +Notice that we hid the columns containing the value streams with \code{\link[=cols_hide]{cols_hide()}} because, while useful, they don't need to be displayed to anybody viewing a table. We have a lot of data points and a connecting line is not as valuable here. It's more interesting to see the clusters of the diff --git a/man/fmt_url.Rd b/man/fmt_url.Rd index 2145b991ec..d7053488c5 100644 --- a/man/fmt_url.Rd +++ b/man/fmt_url.Rd @@ -14,7 +14,11 @@ fmt_url( show_underline = "auto", button_fill = "auto", button_width = "auto", - button_outline = "auto" + button_outline = "auto", + target = NULL, + rel = NULL, + referrerpolicy = NULL, + hreflang = NULL ) } \arguments{ @@ -89,6 +93,13 @@ be the same color as that set in the \code{color} option.} Options for styling a link-as-button (and only applies if \code{as_button = TRUE}). All of these options are by default set to \code{"auto"}, allowing \strong{gt} to choose appropriate fill, width, and outline values.} + +\item{target, rel, referrerpolicy, hreflang}{\emph{Anchor element attributes} + +\verb{scalar} // \emph{default:} \code{NULL} + +Additional anchor element attributes. For descriptions of each attribute +and the allowed values, refer to the \href{https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attributes}{MDN Web Docs reference on the anchor HTML element}.} } \value{ An object of class \code{gt_tbl}. diff --git a/man/gt-package.Rd b/man/gt-package.Rd index cfc1e43391..50828471c6 100644 --- a/man/gt-package.Rd +++ b/man/gt-package.Rd @@ -11,7 +11,7 @@ Build display tables from tabular data with an easy-to-use set of functions. Wit \seealso{ Useful links: \itemize{ - \item \url{https://gt.rstudio.com/} + \item \url{https://gt.rstudio.com} \item \url{https://github.com/rstudio/gt} \item Report bugs at \url{https://github.com/rstudio/gt/issues} } diff --git a/man/gt_output.Rd b/man/gt_output.Rd index da4d2132ba..69bfd60dae 100644 --- a/man/gt_output.Rd +++ b/man/gt_output.Rd @@ -28,7 +28,7 @@ ID given during the \code{\link[=render_gt]{render_gt()}} call is needed as the We need to ensure that we have the \strong{shiny} package installed first. This is easily by using \code{install.packages("shiny")}. More information on creating -Shiny apps can be found at the \href{https://shiny.rstudio.com}{Shiny Site}. +Shiny apps can be found on the \href{https://shiny.posit.co}{Shiny website}. } \section{Examples}{ diff --git a/man/nanoplot_options.Rd b/man/nanoplot_options.Rd index 4b77b4bddc..374e3f9ad4 100644 --- a/man/nanoplot_options.Rd +++ b/man/nanoplot_options.Rd @@ -18,6 +18,8 @@ nanoplot_options( data_bar_negative_stroke_color = NULL, data_bar_negative_stroke_width = NULL, data_bar_negative_fill_color = NULL, + reference_line_color = NULL, + reference_area_fill_color = NULL, vertical_guide_stroke_color = NULL, vertical_guide_stroke_width = NULL, show_data_points = NULL, @@ -27,6 +29,9 @@ nanoplot_options( show_reference_area = NULL, show_vertical_guides = NULL, show_y_axis_guide = NULL, + y_val_fmt_fn = NULL, + y_axis_fmt_fn = NULL, + y_ref_line_fmt_fn = NULL, currency = NULL ) } @@ -144,6 +149,22 @@ By default, all negative data bars have a fill color of \code{"#D75A68"}. This can however be changed by providing a color value to the \code{data_bar_negative_fill_color} option.} +\item{reference_line_color}{\emph{Color for the reference line} + +\verb{scalar} // \emph{default:} \code{NULL} (\code{optional}) + +The reference line will have a color of \code{"#75A8B0"} if it is set to appear. +This color can be changed by providing a single color value to +\code{reference_line_color}.} + +\item{reference_area_fill_color}{\emph{Fill color for the reference area} + +\verb{scalar} // \emph{default:} \code{NULL} (\code{optional}) + +If a reference area has been defined and is visible it has by default +a fill color of \code{"#A6E6F2"}. This can be modified by declaring a color +value in the \code{reference_area_fill_color} option.} + \item{vertical_guide_stroke_color}{\emph{Color of vertical guides} \verb{scalar} // \emph{default:} \code{NULL} (\code{optional}) @@ -215,6 +236,15 @@ The \emph{y}-axis guide will appear when hovering over the far left side of a nanoplot. This hidden layer is active by default but can be deactivated by using \code{show_y_axis_guide = FALSE}.} +\item{y_val_fmt_fn, y_axis_fmt_fn, y_ref_line_fmt_fn}{\emph{Custom formatting for y values} + +\code{function} // \emph{default:} \code{NULL} (\code{optional}) + +If providing a function to \code{y_val_fmt_fn}, \code{y_axis_fmt_fn}, or +\code{y_ref_line_fmt_fn} then customized formatting of the \emph{y} values associated +with the data points/bars, the \emph{y}-axis labels, and the reference line can +be performed.} + \item{currency}{\emph{Define values as currencies of a specific type} \verb{scalar|obj:} // \emph{default:} \code{NULL} (\code{optional}) diff --git a/man/render_gt.Rd b/man/render_gt.Rd index e6ebd0e6a3..39db67c4e9 100644 --- a/man/render_gt.Rd +++ b/man/render_gt.Rd @@ -78,7 +78,7 @@ other fine-grained options for positioning are available in the We need to ensure that we have the \strong{shiny} package installed first. This is easily by using \code{install.packages("shiny")}. More information on creating -Shiny apps can be found at the \href{https://shiny.rstudio.com}{Shiny Site}. +Shiny apps can be found on the \href{https://shiny.posit.co}{Shiny website}. } \section{Examples}{ diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 407de1d4f0..226b8b95f9 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -12,6 +12,17 @@ authors: JooYoung Seo: href: "https://jooyoungseo.github.io/" +news: + releases: + - text: "Version 0.9.0" + href: https://posit.co/blog/?search=gt%2520%25200.9.0 + - text: "Version 0.8.0" + href: https://posit.co/blog/new-features-upgrades-in-gt-0-8-0/ + - text: "Version 0.7.0" + href: https://posit.co/blog/all-new-things-in-gt-0-7-0/ + - text: "Version 0.6.0" + href: https://posit.co/blog/changes-for-the-better-in-gt-0-6-0/ + development: mode: release @@ -361,10 +372,5 @@ reference: - rx_adsl - rx_addv -navbar: - structure: - left: [intro, articles, reference, news] - components: - intro: - text: Intro - href: articles/intro-creating-gt-tables.html +redirects: + - ["articles/intro-creating-gt-tables.html", "articles/gt.html"] diff --git a/tests/testthat/test-fmt_url.R b/tests/testthat/test-fmt_url.R index ab15080112..5d8e0d5b8e 100644 --- a/tests/testthat/test-fmt_url.R +++ b/tests/testthat/test-fmt_url.R @@ -307,6 +307,16 @@ test_that("The `fmt_url()` function works correctly", { "Search Mozilla" ) ) + expect_equal( + (tab_alt %>% + fmt_url(columns = a, hreflang = "en-GB", target = "_blank", rel = "external", referrerpolicy = "no-referrer") %>% + render_formats_test(context = "html"))[["a"]], + c( + "Example Site", + "Learn at Mozilla", + "Search Mozilla" + ) + ) # Expect that a column with NAs will work fine with `fmt_url()`, # it'll just produce NA values diff --git a/tests/testthat/test-utils_plots.R b/tests/testthat/test-utils_plots.R new file mode 100644 index 0000000000..53ad656a73 --- /dev/null +++ b/tests/testthat/test-utils_plots.R @@ -0,0 +1,191 @@ +test_that("Value stream processing produces the correct numeric vectors", { + + num_stream_01 <- "1 2 3 4 5 6" + num_stream_02 <- "1;2;3;4;5;6" + num_stream_03 <- "1,2,3,4,5,6" + num_stream_04 <- "1, 2 3 4; 5,6 " + num_stream_05 <- "1.2 2.3 3.34 4.1 5 6" + num_stream_06 <- "1.2 2.3 3.34 4.1 5.23 6.233232" + num_stream_07 <- "-1.2 2.3 +3.34 4.1 -5.23 6.22" + num_stream_08 <- "-1.2E-05 2.3E2 +3.34 4.1e-1 -5.23 -6.2e4" + num_stream_09 <- "(-1.2E-05 2.3E2 +3.34 4.1e-1 -5.23 -6.2e4)" + num_stream_10 <- "c(-1.2E-05 2.3E2 +3.34 4.1e-1 -5.23 -6.2e4)" + num_stream_11 <- "[-1.2E-05 2.3E2 +3.34 4.1e-1 -5.23 -6.2e4]" + num_stream_12 <- "[ -1.2E-05 2.3E2 +3.34 4.1e-1 -5.23 -6.2e4 ]" + num_stream_13 <- "[ -1.2E-05, 2.3E2, +3.34, 4.1e-1, -5.23, -6.2e4 ]" + num_stream_14 <- "1 2 3 4 5 NA 6" + num_stream_15 <- "1,2,3,4,5,NA,6" + num_stream_16 <- "NA -1.2E-05 2.3E2 +3.34 4.1e-1 -5.23 -6.2e4" + num_stream_17 <- "NA -1.2E-05 2.3E2 NA +3.34 NA 4.1e-1 NA -5.23 NA -6.2e4 NA" + + expect_equal( + process_number_stream(num_stream_01), + c(1, 2, 3, 4, 5, 6) + ) + expect_equal( + process_number_stream(num_stream_02), + c(1, 2, 3, 4, 5, 6) + ) + expect_equal( + process_number_stream(num_stream_03), + c(1, 2, 3, 4, 5, 6) + ) + expect_equal( + process_number_stream(num_stream_04), + c(1, 2, 3, 4, 5, 6) + ) + expect_equal( + process_number_stream(num_stream_05), + c(1.2, 2.3, 3.34, 4.1, 5, 6) + ) + expect_equal( + process_number_stream(num_stream_06), + c(1.2, 2.3, 3.34, 4.1, 5.23, 6.233232) + ) + expect_equal( + process_number_stream(num_stream_07), + c(-1.2, 2.3, 3.34, 4.1, -5.23, 6.22) + ) + expect_equal( + process_number_stream(num_stream_08), + c(-1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_09), + c(-1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_10), + c(-1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_11), + c(-1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_12), + c(-1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_13), + c(-1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_14), + c(1, 2, 3, 4, 5, NA, 6) + ) + expect_equal( + process_number_stream(num_stream_15), + c(1, 2, 3, 4, 5, NA, 6) + ) + expect_equal( + process_number_stream(num_stream_16), + c(NA, -1.2e-05, 230, 3.34, 0.41, -5.23, -62000) + ) + expect_equal( + process_number_stream(num_stream_17), + c(NA, -1.2e-05, 230, NA, 3.34, NA, 0.41, NA, -5.23, NA, -62000, NA) + ) + + time_stream_01 <- "2000-02-24 02:03:58, 2000-02-25 15:24:04, 2000-02-26 00:23:26" + time_stream_02 <- "2000-02-24 02:03:58,2000-02-25 15:24:04, 2000-02-26 00:23:26" + time_stream_03 <- "2000-02-24 02:03:58;2000-02-25 15:24:04 ; 2000-02-26 00:23:26" + time_stream_04 <- "2000-02-24T02:03:58,2000-02-25T15:24:04, 2000-02-26T00:23:26" + time_stream_05 <- "2000-02-24 02:03:58,2000-02-25, 2000-02-25 00:00, 2000-02-25 00:00:00, 2000-02-26T00:23:26" + + expect_equal( + process_time_stream(time_stream_01), + c( + `2000-02-24 02:03:58` = 951357838, + `2000-02-25 15:24:04` = 951492244, + `2000-02-26 00:23:26` = 951524606 + ) + ) + expect_equal( + process_time_stream(time_stream_02), + c( + `2000-02-24 02:03:58` = 951357838, + `2000-02-25 15:24:04` = 951492244, + `2000-02-26 00:23:26` = 951524606 + ) + ) + expect_equal( + process_time_stream(time_stream_03), + c( + `2000-02-24 02:03:58` = 951357838, + `2000-02-25 15:24:04` = 951492244, + `2000-02-26 00:23:26` = 951524606 + ) + ) + expect_equal( + process_time_stream(time_stream_04), + c( + `2000-02-24 02:03:58` = 951357838, + `2000-02-25 15:24:04` = 951492244, + `2000-02-26 00:23:26` = 951524606 + ) + ) + expect_equal( + process_time_stream(time_stream_05), + c( + `2000-02-24 02:03:58` = 951350400, + `2000-02-25` = 951436800, + `2000-02-25 00:00` = 951436800, + `2000-02-25 00:00:00` = 951436800, + `2000-02-26 00:23:26` = 951523200 + ) + ) +}) + +test_that("The `format_number_compactly()` function works well", { + + values <- + c( + 0, NA, NaN, Inf, -Inf, 1, -1, 10, -10, 0.234, -0.432, 0.142537342, -0.36342342, + 1.243 * 10^seq(1, 15), 6.257363 * 10^seq(-1, -15), + 0.0000000024200240024240, 0.0000000000000000007, 23492879275207850274, + 239472947294782.2947829427, 23947294729478229478.29427 + ) + + values_fmt <- character(length = length(values)) + for (i in seq_along(values)) { + values_fmt[i] <- format_number_compactly(val = values[i]) + } + + expect_equal( + values_fmt, + c( + "0", "NA", "NA", "Inf.", "−Inf.", "1.00", "−1.00", "10.0", + "−10.0", "0.23", "−0.43", "0.14", "−0.36", "12.4", "124", + "1.24K", "12.4K", "124K", "1.24M", "12.4M", "124M", "1.24B", + "12.4B", "124B", "1.24T", "12.4T", "124T", "1.2E15", "0.63", + "0.063", "6.3E−03", "6.3E−04", "6.3E−05", "6.3E−06", + "6.3E−07", "6.3E−08", "6.3E−09", "6.3E−10", "6.3E−11", + "6.3E−12", "6.3E−13", "6.3E−14", "6.3E−15", "2.4E−09", + "7.0E−19", "2.3E19", "239T", "2.4E19" + ) + ) + + values_fmt_curr <- character(length = length(values)) + for (i in seq_along(values)) { + values_fmt_curr[i] <- format_number_compactly(val = values[i], currency = "EUR") + } + + expect_equal( + values_fmt_curr, + c( + "0", "NA", "NA", ">€1,000T", ">€1,000T", "€1.00", + "−€1.00", "€10.00", "−€10.00", "€0.23", + "−€0.43", "€0.14", "−€0.36", "€12.43", + "€124.30", "€1.24K", "€12.4K", "€124K", + "€1.2M", "€12.4M", "€124.3M", "€1.2B", + "€12.4B", "€124.3B", "€1.2T", "€12.4T", + "€124.3T", ">€1,000T", "€0.63", "€0.06", + "€0.01", "€0.00", "€0.00", "€0.00", + "€0.00", "€0.00", "€0.00", "€0.00", + "€0.00", "€0.00", "€0.00", "€0.00", + "€0.00", "€0.00", "€0.00", ">€1,000T", + "€239.5T", ">€1,000T" + ) + ) +}) diff --git a/vignettes/intro-creating-gt-tables.Rmd b/vignettes/gt.Rmd similarity index 100% rename from vignettes/intro-creating-gt-tables.Rmd rename to vignettes/gt.Rmd