Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/davidgohel/flextable/llms.txt

Use this file to discover all available pages before exploring further.

Flextable provides several ways to create frequency and cross-tabulation tables. proc_freq() computes and renders contingency tables directly. as_flextable() methods for table and xtable objects convert standard R summary objects. as_grouped_data() restructures any data frame into a grouped-row presentation.

proc_freq() — SAS-style frequency tables

proc_freq() computes a one-way or two-way contingency table and renders it as a flextable in a single step. It is inspired by SAS PROC FREQ and is designed to be compact.
proc_freq(
  x,
  row = character(),
  col = character(),
  include.row_percent = TRUE,
  include.column_percent = TRUE,
  include.table_percent = TRUE,
  include.table_count = TRUE,
  weight = character(),
  count_format_fun = fmt_int,
  ...
)
ParameterDescription
xA data.frame containing the variables to count.
rowColumn name for the row variable.
colColumn name for the column variable.
include.row_percentWhether to include row percentages (default TRUE).
include.column_percentWhether to include column percentages (default TRUE).
include.table_percentWhether to include overall table percentages (default TRUE).
include.table_countWhether to include raw counts (default TRUE).
weightColumn name for a weight variable. When supplied, counts are the sum of weights rather than row counts.
count_format_funFunction used to format count values. Defaults to fmt_int.
Exactly one or two variables must be specified using row and col. Providing zero or more than two raises an error.

One-way frequency table

When only row (or only col) is provided, proc_freq() returns a simple frequency table with counts and overall percentages:
library(flextable)

proc_freq(mtcars, "vs")

Two-way cross-tabulation

When both row and col are specified, the result is a cross-tabulation with margin totals:
proc_freq(mtcars, "vs", "gear")
Each cell in the body shows:
  • Count and overall table percentage (e.g. 5 (15.6%)) — controlled by include.table_count and include.table_percent
  • Column percentage and/or row percentage on a second line (e.g. 33.3% ; 71.4%) — controlled by include.column_percent and include.row_percent
Margin rows and columns are labelled "Total". A horizontal rule separates the data rows from the “Total” row.

Weighted counts

Pass a numeric column name to weight to compute weighted frequencies:
proc_freq(mtcars, "gear", "vs", weight = "wt")

Controlling which percentages appear

# Show only counts, no percentages
proc_freq(mtcars, "vs", "gear",
  include.row_percent = FALSE,
  include.column_percent = FALSE,
  include.table_percent = FALSE
)

# Show only column percentages (no row percentages)
proc_freq(mtcars, "vs", "gear",
  include.row_percent = FALSE
)

Output layout

For two-way tables, proc_freq() uses tabulator() internally. The .what. column distinguishes count rows from margin-percentage rows. When both row and column percents are included, a footnote " (1)" marks the margin percent row explaining the format as “Columns and rows percentages”.

as_flextable() for table objects

R’s table() function returns a contingency table object. as_flextable.table() converts it into a flextable:
M <- as.table(rbind(c(762, 327, 468), c(484, 239, 477)))
dimnames(M) <- list(
  gender = c("F", "M"),
  party = c("Democrat", "Independent", "Republican")
)

library(flextable)
ft <- as_flextable(M)
ft

as_flextable() for xtable objects

Objects produced by xtable::xtable() can be converted with as_flextable.xtable():
if (require("xtable")) {
  ft <- as_flextable(xtable::xtable(head(mtcars)))
  ft
}

Grouped row presentations with as_grouped_data()

as_grouped_data() restructures a data.frame so that repeated values in a group column become labelled separator rows rather than repeated cell values. This produces a compact, readable grouped layout.
as_grouped_data(
  x,
  groups,
  columns = NULL,
  expand_single = TRUE
)
ParameterDescription
xA data.frame (data.tables and tibbles are coerced).
groupsColumn name(s) whose values become row separator labels.
columnsColumns to include in the output. Defaults to all non-group columns.
expand_singleIf TRUE (default), groups with only one data row still receive a separator title row. Set to FALSE to suppress title rows for single-row groups.

Example

library(flextable)
library(data.table)

CO2 <- CO2
setDT(CO2)
CO2$conc <- as.integer(CO2$conc)

data_co2 <- dcast(CO2, Treatment + conc ~ Type,
  value.var = "uptake", fun.aggregate = mean
)

# Restructure so Treatment becomes a separator row
data_co2 <- as_grouped_data(x = data_co2, groups = c("Treatment"))

ft <- as_flextable(data_co2)
ft <- add_footer_lines(ft, "dataset CO2 has been used for this flextable")
ft <- add_header_lines(ft, "mean of carbon dioxide uptake in grass plants")
ft <- set_header_labels(ft, conc = "Concentration")
ft <- autofit(ft)
ft <- width(ft, width = c(1, 1, 1))
ft

Hiding group label prefixes

By default, the separator row reads "Treatment: chilled". Set hide_grouplabel = TRUE in as_flextable() to show only "chilled":
ft <- as_flextable(data_co2, hide_grouplabel = TRUE)

Using as_grouped_data() with tabulator()

tabulator() also supports a spread layout through its spread_first_col argument in as_flextable(), which calls as_grouped_data() internally on the first row dimension. This is the primary way to use grouped rows with summarizor() output:
library(flextable)

z <- summarizor(CO2[-c(1, 4)], by = "Treatment")
ft <- as_flextable(z, spread_first_col = TRUE, sep_w = 0)
ft

Quick reference

FunctionUse when
proc_freq(x, row, col)You need a ready-made frequency or cross-tabulation table from raw data
as_flextable(table_obj)You have an existing R table() object
as_flextable(xtable_obj)You have an xtable object
as_grouped_data() + as_flextable()You have a tidy data frame and want grouped row separators
tabulator() with spread_first_col = TRUEYou are using summarizor() or a custom aggregation and want the first row dimension as separators

Build docs developers (and LLMs) love