Groups and labels

library(tinytable)
options(tinytable_tt_digits = 3)
options(tinytable_theme_placement_latex_float = "H")
x <- mtcars[1:4, 1:5]

The group_tt() function can label groups of rows (i) or columns (j).

Rows

The i argument accepts a named list of integers. The numbers identify the positions where row group labels are to be inserted. The names includes the text that should be inserted:

dat <- mtcars[1:9, 1:8]

tt(dat) |>
  group_tt(i = list(
    "I like (fake) hamburgers" = 3,
    "She prefers halloumi" = 4,
    "They love tofu" = 7))
mpg cyl disp hp drat wt qsec vs
21 6 160 110 3.9 2.62 16.5 0
21 6 160 110 3.9 2.88 17 0
22.8 4 108 93 3.85 2.32 18.6 1
21.4 6 258 110 3.08 3.21 19.4 1
18.7 8 360 175 3.15 3.44 17 0
18.1 6 225 105 2.76 3.46 20.2 1
14.3 8 360 245 3.21 3.57 15.8 0
24.4 4 147 62 3.69 3.19 20 1
22.8 4 141 95 3.92 3.15 22.9 1

The numbers in the i list indicate that a label must be inserted at position # in the original table (without row groups). For example,

tt(head(iris)) |>
  group_tt(i = list("After 1" = 2, "After 2" = 3, "After 3" = 4, "After 5" = 6))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa
tt(head(iris)) |>
  group_tt(i = list("After 1a" = 2, "After 1b" = 2, "After 5" = 6))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa
tt(head(iris)) |>
  group_tt(i = list("After 0" = 1))
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

We can style group rows in the same way as regular rows (caveat: not in Word or Markdown):

tt(dat) |>
  group_tt(
    i = list(
      "I like (fake) hamburgers" = 3,
      "She prefers halloumi" = 4,
      "They love tofu" = 7
    )
  ) |>
  style_tt(
    i = c(3, 5, 9),
    align = "c",
    color = "white",
    background = "gray",
    bold = TRUE
  )
mpg cyl disp hp drat wt qsec vs
21 6 160 110 3.9 2.62 16.5 0
21 6 160 110 3.9 2.88 17 0
22.8 4 108 93 3.85 2.32 18.6 1
21.4 6 258 110 3.08 3.21 19.4 1
18.7 8 360 175 3.15 3.44 17 0
18.1 6 225 105 2.76 3.46 20.2 1
14.3 8 360 245 3.21 3.57 15.8 0
24.4 4 147 62 3.69 3.19 20 1
22.8 4 141 95 3.92 3.15 22.9 1

Automatic row groups

We can use the group_tt() function to group rows and label them using spanners (almost) automatically. For example,

# subset and sort data
df <- mtcars |> 
  head(10) |>
  sort_by(~am)

# draw table
tt(df) |> group_tt(i = df$am)
mpg cyl disp hp drat wt qsec vs am gear carb
21.4 6 258 110 3.08 3.21 19.4 1 0 3 1
18.7 8 360 175 3.15 3.44 17 0 0 3 2
18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
24.4 4 147 62 3.69 3.19 20 1 0 4 2
22.8 4 141 95 3.92 3.15 22.9 1 0 4 2
19.2 6 168 123 3.92 3.44 18.3 1 0 4 4
21 6 160 110 3.9 2.62 16.5 0 1 4 4
21 6 160 110 3.9 2.88 17 0 1 4 4
22.8 4 108 93 3.85 2.32 18.6 1 1 4 1

Styling row groups

Calculating the location of rows can be cumbersome. Instead of doing this by hand, we can extract information from the table@group_index_i S4 slot in the table object. Please be aware, however, that slot names are subject to change without warning in future versions of tinytable.

library(magrittr)
tt(df) %>%
  group_tt(i = df$am) %>%
  style_tt(i = .@group_index_i, background = "pink")
mpg cyl disp hp drat wt qsec vs am gear carb
21.4 6 258 110 3.08 3.21 19.4 1 0 3 1
18.7 8 360 175 3.15 3.44 17 0 0 3 2
18.1 6 225 105 2.76 3.46 20.2 1 0 3 1
14.3 8 360 245 3.21 3.57 15.8 0 0 3 4
24.4 4 147 62 3.69 3.19 20 1 0 4 2
22.8 4 141 95 3.92 3.15 22.9 1 0 4 2
19.2 6 168 123 3.92 3.44 18.3 1 0 4 4
21 6 160 110 3.9 2.62 16.5 0 1 4 4
21 6 160 110 3.9 2.88 17 0 1 4 4
22.8 4 108 93 3.85 2.32 18.6 1 1 4 1

Columns

The syntax for column groups is very similar, but we use the j argument instead. The named list specifies the labels to appear in column-spanning labels, and the values must be a vector of consecutive and non-overlapping integers that indicate which columns are associated to which labels:

tt(dat) |>
  group_tt(
    j = list(
      "Hamburgers" = 1:3,
      "Halloumi" = 4:5,
      "Tofu" = 7))
Hamburgers Halloumi Tofu
mpg cyl disp hp drat wt qsec vs
21 6 160 110 3.9 2.62 16.5 0
21 6 160 110 3.9 2.88 17 0
22.8 4 108 93 3.85 2.32 18.6 1
21.4 6 258 110 3.08 3.21 19.4 1
18.7 8 360 175 3.15 3.44 17 0
18.1 6 225 105 2.76 3.46 20.2 1
14.3 8 360 245 3.21 3.57 15.8 0
24.4 4 147 62 3.69 3.19 20 1
22.8 4 141 95 3.92 3.15 22.9 1

Here is a table with both row and column headers, as well as some styling:

dat <- mtcars[1:9, 1:8]
tt(dat) |>
  group_tt(
    i = list(
      "I like (fake) hamburgers" = 3,
      "She prefers halloumi" = 4,
      "They love tofu" = 7
    ),
    j = list(
      "Hamburgers" = 1:3,
      "Halloumi" = 4:5,
      "Tofu" = 7
    )
  ) |>
  style_tt(
    i = c(3, 5, 9),
    align = "c",
    background = "teal",
    color = "white"
  ) |>
  style_tt(i = -1, color = "teal")
Hamburgers Halloumi Tofu
mpg cyl disp hp drat wt qsec vs
21 6 160 110 3.9 2.62 16.5 0
21 6 160 110 3.9 2.88 17 0
22.8 4 108 93 3.85 2.32 18.6 1
21.4 6 258 110 3.08 3.21 19.4 1
18.7 8 360 175 3.15 3.44 17 0
18.1 6 225 105 2.76 3.46 20.2 1
14.3 8 360 245 3.21 3.57 15.8 0
24.4 4 147 62 3.69 3.19 20 1
22.8 4 141 95 3.92 3.15 22.9 1

We can also stack several extra headers on top of one another:

tt(x) |>
  group_tt(j = list("Foo" = 2:3, "Bar" = 5)) |>
  group_tt(j = list("Hello" = 1:2, "World" = 4:5))
Hello World
Foo Bar
mpg cyl disp hp drat
21 6 160 110 3.9
21 6 160 110 3.9
22.8 4 108 93 3.85
21.4 6 258 110 3.08

Repeated column names

In some contexts, users wish to repeat the column names to treat them as group labels. Consider this dataset:

library(tinytable)
library(magrittr)

dat = data.frame(
  Region = as.character(state.region),
  State = row.names(state.x77), 
  state.x77[, 1:3]) |>
  sort_by(~ Region + State) |>
  subset(Region %in% c("North Central", "Northeast"))
dat = do.call(rbind, by(dat, dat$Region, head, n = 3))
row.names(dat) = NULL
dat
         Region         State Population Income Illiteracy
1 North Central      Illinois      11197   5107        0.9
2 North Central       Indiana       5313   4458        0.7
3 North Central          Iowa       2861   4628        0.5
4     Northeast   Connecticut       3100   5348        1.1
5     Northeast         Maine       1058   3694        0.7
6     Northeast Massachusetts       5814   4755        1.1

Here, we may want to repeat the column names for every region. The group_tt() function does not support this directly, but it is easy to achieve this effect by:

  1. Insert column names as new rows in the data.
  2. Creat a row group variable (here: region)
  3. Style the column names and group labels
# Format the numeric values early because repeating column names coerces all data to character
dat2 = format_tt(dat, digits = 1)

# Insert column names as new rows
dat2 = do.call(rbind, lapply(split(dat2, dat2$Region), \(d) {
  rbind(as.list(colnames(d)), d)
}))

# Create row group variable
idx = which(dat2$Region == "Region")
dat2$Region[idx] = dat2$Region[idx + 1]

# Draw table
dat2 %>% 
  subset(select = -Region) %>%
  tt() %>%
  group_tt(i = dat2$Region) %>%
  style_tt(i = .@group_index_i + 1, background = "black", color = "white") %>%
  style_tt(i = .@group_index_i, align = "c", color = "red") %>%
  setNames(NULL)
State Population Income Illiteracy
Illinois 11197 5107 0.9
Indiana 5313 4458 0.7
Iowa 2861 4628 0.5
State Population Income Illiteracy
Connecticut 3100 5348 1.1
Maine 1058 3694 0.7
Massachusetts 5814 4755 1.1