Skip to content

Commit 7a5a31c

Browse files
authored
add gt_split into utils (#433)
* add gt_split into utils * remove header from gt and add function dependencies in gt_split * update documentation for gt_split in export * update create_tables_doc to reference asar::gt_split and not gt
1 parent 502788f commit 7a5a31c

File tree

4 files changed

+332
-2
lines changed

4 files changed

+332
-2
lines changed

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ export(create_titlepage_tex)
2020
export(create_yaml)
2121
export(export_split_tbls)
2222
export(format_quarto)
23+
export(gt_split)
2324
export(render_lg_table)

R/create_tables_doc.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n
266266
" gt::cols_width(\n",
267267
" everything() ~ pct(20)\n",
268268
" ) |> \n",
269-
" gt::gt_split(row_every_n = ", max_rows, ") |>\n",
269+
" asar::gt_split(row_every_n = ", max_rows, ") |>\n",
270270
" gt::grp_pull(", i, ")\n"
271271
),
272272
label = glue::glue("tbl-{tab_shortname}", i),
@@ -435,7 +435,7 @@ load(file.path(tables_dir, '", stringr::str_remove(tab, "_split"), "'))\n
435435
" gt::cols_width(\n",
436436
" everything() ~ pct(20)\n",
437437
" ) |> \n",
438-
" gt::gt_split(row_every_n = ", max_rows, ") |>\n",
438+
" asar::gt_split(row_every_n = ", max_rows, ") |>\n",
439439
" gt::grp_pull(", j, ")\n"
440440
),
441441
label = glue::glue("tbl-{tab_shortname}", i, "-", j),

R/utils.R

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,226 @@ SS3_extract_fleet <- function(dat, vers) {
158158

159159
# create notin operator
160160
`%notin%` <- Negate(`%in%`)
161+
162+
#----------------------------------------------------------
163+
164+
# gt_split()
165+
#' Split a table into a group of tables (a `gt_group`)
166+
#'
167+
#' @description
168+
#'
169+
#' With a **gt** table, you can split it into multiple tables and get that
170+
#' collection in a `gt_group` object. This function is useful for those cases
171+
#' where you want to section up a table in a specific way and print those
172+
#' smaller tables across multiple pages (in RTF and Word outputs, primarily via
173+
#' \link[gt]{gtsave}, or, with breaks between them when the output context is HTML.
174+
#'
175+
#' @param data *The gt table data object*
176+
#'
177+
#' `obj:<gt_tbl>` // **required**
178+
#'
179+
#' This is the **gt** table object that is commonly created through use of the
180+
#' [gt()] function.
181+
#'
182+
#' @param row_every_n *Split at every n rows*
183+
#'
184+
#' `scalar<numeric|integer>` // *default:* `NULL` (`optional`)
185+
#'
186+
#' A directive to split at every *n* number of rows. This argument expects a
187+
#' single numerical value.
188+
#'
189+
#' @param row_slice_i *Row-slicing indices*
190+
#'
191+
#' `vector<numeric|integer>` // *default:* `NULL` (`optional`)
192+
#'
193+
#' An argument for splitting at specific row indices. Here, we expect either a
194+
#' vector of index values or a function that evaluates to a numeric vector.
195+
#'
196+
#' @param col_slice_at *Column-slicing locations*
197+
#'
198+
#' `<column-targeting expression>` // *default:* `NULL` (`optional`)
199+
#'
200+
#' Any columns where vertical splitting across should occur. The splits occur
201+
#' to the right of the resolved column names. Can either be a series of column
202+
#' names provided in `c()`, a vector of column indices, or a select helper
203+
#' function (e.g. \link[gt]{starts_with}, \link[gt]{ends_with}, \link[gt]{contains}, \link[gt]{matches},
204+
#' \link[gt]{num_range}, and \link[gt]{everything}).
205+
#'
206+
#' @return An object of class `gt_group`.
207+
#'
208+
#' @note
209+
#' This function is a temporary export of asar, but all development and rights
210+
#' belong to `rstudio/gt`. This function provides a fix to the function
211+
#' introduced by a bug in gt v1.3.0. Until this is corrected in the package, we
212+
#' are using the function here. Once this bug is patched, we will deprecate
213+
#' and remove this function from asar and direct users to use the gt package
214+
#' version of this function.rom
215+
#'
216+
#' @section Examples:
217+
#'
218+
#' Use a subset of the [`gtcars`] dataset to create a **gt** table. Format the
219+
#' `msrp` column to display numbers as currency values, set column widths with
220+
#' \link[gt]{cols_width}, and split the table at every five rows with `gt_split()`.
221+
#' This creates a `gt_group` object containing two tables. Printing this object
222+
#' yields two tables separated by a line break.
223+
#'
224+
#' ```r
225+
#' gtcars |>
226+
#' dplyr::slice_head(n = 10) |>
227+
#' dplyr::select(mfr, model, year, msrp) |>
228+
#' gt() |>
229+
#' fmt_currency(columns = msrp) |>
230+
#' cols_width(
231+
#' year ~ px(80),
232+
#' everything() ~ px(150)
233+
#' ) |>
234+
#' gt_split(row_every_n = 5)
235+
#' ```
236+
#'
237+
#' Use a smaller subset of the [`gtcars`] dataset to create a **gt** table.
238+
#' Format the `msrp` column to display numbers as currency values, set the table
239+
#' width with [tab_options()] and split the table at the `model` column This
240+
#' creates a `gt_group` object again containing two tables but this time we get
241+
#' a vertical split. Printing this object yields two tables of the same width.
242+
#'
243+
#' ```r
244+
#' gtcars |>
245+
#' dplyr::slice_head(n = 5) |>
246+
#' dplyr::select(mfr, model, year, msrp) |>
247+
#' gt() |>
248+
#' fmt_currency(columns = msrp) |>
249+
#' tab_options(table.width = px(400)) |>
250+
#' gt_split(col_slice_at = "model")
251+
#' ```
252+
#'
253+
#' @family table group functions
254+
#' @section Function ID:
255+
#' 14-2
256+
#'
257+
#' @section Function Introduced:
258+
#' `v0.9.0` (Mar 31, 2023)
259+
#'
260+
#' @export
261+
gt_split <- function(
262+
data,
263+
row_every_n = NULL,
264+
row_slice_i = NULL,
265+
col_slice_at = NULL
266+
) {
267+
268+
# Perform input object validation
269+
gt:::stop_if_not_gt_tbl(data = data)
270+
271+
# Resolution of columns as character vectors
272+
col_slice_at <-
273+
gt:::resolve_cols_c(
274+
expr = {{ col_slice_at }},
275+
data = data,
276+
null_means = "nothing"
277+
)
278+
279+
gt_tbl_built <- gt:::build_data(data = data, context = "html")
280+
281+
# Get row count for table (data rows)
282+
n_rows_data <- nrow(gt_tbl_built[["_stub_df"]])
283+
284+
row_slice_vec <- rep.int(1L, n_rows_data)
285+
286+
row_every_n_idx <- NULL
287+
if (!is.null(row_every_n)) {
288+
row_every_n_idx <- seq_len(n_rows_data)[seq(0, n_rows_data, row_every_n)]
289+
}
290+
291+
row_slice_i_idx <- NULL
292+
if (!is.null(row_slice_i)) {
293+
row_slice_i_idx <- row_slice_i
294+
}
295+
296+
row_idx <- sort(unique(c(row_every_n_idx, row_slice_i_idx)))
297+
298+
group_i <- 0L
299+
300+
for (i in seq_along(row_slice_vec)) {
301+
302+
if (i %in% (row_idx + 1)) {
303+
group_i <- group_i + 1L
304+
}
305+
306+
row_slice_vec[i] <- row_slice_vec[i] + group_i
307+
}
308+
309+
row_range_list <-
310+
split(
311+
seq_len(n_rows_data),
312+
row_slice_vec
313+
)
314+
315+
gt_tbl_main <- data
316+
317+
gt_group <- gt::gt_group(.use_grp_opts = FALSE)
318+
319+
for (i in seq_along(row_range_list)) {
320+
321+
gt_tbl_i <- gt_tbl_main
322+
323+
gt_tbl_i[["_data"]] <- gt_tbl_i[["_data"]][row_range_list[[i]], ]
324+
gt_tbl_i[["_stub_df"]] <- gt_tbl_i[["_stub_df"]][seq_along(row_range_list[[i]]), ]
325+
326+
if (!is.null(col_slice_at)) {
327+
328+
# Get all visible vars in their finalized order
329+
visible_col_vars <- gt:::dt_boxhead_get_vars_default(data = data)
330+
331+
# Stop function if any of the columns to split at aren't visible columns
332+
if (!all(col_slice_at %in% visible_col_vars)) {
333+
cli::cli_abort(
334+
"All values provided in `col_slice_at` must correspond to visible columns."
335+
)
336+
}
337+
338+
# Obtain all of the column indices for vertical splitting
339+
col_idx <- which(visible_col_vars %in% col_slice_at)
340+
341+
col_slice_vec <- rep.int(1L, length(visible_col_vars))
342+
343+
group_j <- 0L
344+
345+
for (i in seq_along(col_slice_vec)) {
346+
347+
if (i %in% (col_idx + 1)) {
348+
group_j <- group_j + 1L
349+
}
350+
351+
col_slice_vec[i] <- col_slice_vec[i] + group_j
352+
}
353+
354+
col_range_list <-
355+
split(
356+
seq_along(visible_col_vars),
357+
col_slice_vec
358+
)
359+
360+
for (j in seq_along(col_range_list)) {
361+
362+
gt_tbl_j <- gt_tbl_i
363+
364+
gt_tbl_j[["_data"]] <-
365+
gt_tbl_j[["_data"]][, visible_col_vars[col_range_list[[j]]]]
366+
367+
gt_tbl_j[["_boxhead"]] <-
368+
gt_tbl_j[["_boxhead"]][
369+
gt_tbl_j[["_boxhead"]]$var %in% visible_col_vars[col_range_list[[j]]],
370+
]
371+
372+
gt_group <- gt::grp_add(gt_group, gt_tbl_j)
373+
}
374+
375+
376+
} else {
377+
gt_group <- gt::grp_add(gt_group, gt_tbl_i)
378+
}
379+
}
380+
381+
gt_group
382+
}
383+

man/gt_split.Rd

Lines changed: 106 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)