Set {gt} table header height - r

Is there any way to set a {gt} table header to a specific height?
I have a collection of tables, all with same width. Depending on how long is the text on the title, it breaks a line and the header height increases. It is great for a single table, but I need each table in the collection to match their heights.
At the moment, I am able to add the option heading.padding = 20, but this is added also when the line is broken. I can set it to be applied only when the title has more than n characters, but it won't work to a generic case - besides, it's not elegant. I took some shots using gt::html() but no success yet.
Here's an example:
require(dplyr)
require(gt)
iris %>%
select(1,2) %>%
gt() %>%
tab_header(title = "Test") %>%
tab_options(heading.padding = 20,
table.width = 10)
iris %>%
select(1,2) %>%
gt() %>%
tab_header(title = "Testing a longer title to set the height") %>%
tab_options(heading.padding = 20,
table.width = 10)
Both tables above should have the same height. Any tips to achieve that will be appreciated.
Thanks in advance!

EDIT:Added option 2
Try using this function that will change the heading.padding
Option 1: according to the number of characters in your title:
require(dplyr)
require(gt)
Create.table <- function(title) {
if (nchar(title) > 22) {
my.table <- iris %>%
select(1, 2) %>%
gt() %>%
tab_header(title = title) %>%
tab_options(heading.padding = "inherit",
table.width = 10)
} else {
my.table <- iris %>%
select(1, 2) %>%
gt() %>%
tab_header(title = title) %>%
tab_options(heading.padding = 14.46,
table.width = 10)
return(my.table)
}
}
tbl1 <- Create.table(title = "Testing a longer title to set the height")
tbl2 <- Create.table(title = "Test")
Option 2: Add line breaks to your title (from #Rodrigo Salvador's comment).
In this case we need the package stringr to look for line breaks:
require(dplyr)
require(gt)
require(stringr)
Create.table <- function(title) {
if (str_count(title, '<br>') > 0) {
my.table <- iris %>%
select(1, 2) %>%
slice_head() %>%
gt() %>%
tab_header(title = md(title)) %>% # markdown title
tab_options(heading.padding = "inherit",
table.width = 10)
} else {
my.table <- iris %>%
select(1, 2) %>%
slice_head() %>%
gt() %>%
tab_header(title = md(title)) %>%
tab_options(heading.padding = 14.46,
table.width = 10)
return(my.table)
}
}
tbl1 <- Create.table(title = "Testing a longer title <br>to set the height")
tbl2 <- Create.table(title = "Test")
tbl3 <- Create.table(title = "Loooooooooooooooooooooooooooooooooooooooooong")

Related

How to write a table with hyperlinks in officedown powerpoint with R?

I can print my data with gt table and create hyperlinks for different cells, as shown below by knitting an HTML file:
library(dplyr)
library(gt)
raw_dat <- mtcars[1:15, ] %>% rownames_to_column(var = "id") %>% select(id, mpg)
df <- tibble(
name = raw_dat$id,
link = 'https://www.cars.com//')
df %>%
mutate(link = sprintf('<p>%s', link, raw_dat$mpg),
link = map(link, gt::html)) %>%
gt
I want to print the same output to a powerpoint slide and preserve the links. I understand that I cannot use 'ph_with' with an object of class "c('gt_tbl', 'list')". So I was wondering what else I can do to print a table with hyperlinks in powerpoint.
NEW <- read_pptx("Presentation1.pptx") %>%
add_slide(., layout = "Blank", master = "Office Theme") %>%
ph_with(raw_dat,
location = ph_location(left = 0, top = 0, width = 13.33, height = 7.5))
This is a solution with flextable that is supported by officer.
library(tibble)
library(dplyr)
library(flextable)
library(officer)
raw_dat <- mtcars[1:15, ] %>% rownames_to_column(var = "id") %>% select(id, mpg)
df <- tibble(name = raw_dat$id, link = 'https://www.cars.com//')
ft <- flextable(df) %>%
mk_par(j = "link", value = as_paragraph(hyperlink_text(x = name, url = link))) %>%
autofit()
read_pptx() %>%
add_slide() %>%
ph_with(ft, location = ph_location_type()) %>%
print(target = "toto.pptx")
PS: I am not sure that I answer the question as it is generated with officer, not officedown, but your example is not a R Markdown example. I assumed you wanted to use officer.

how to make customised pretty flexable function

I am loving flextable however, incorporating it within my workflow is causing issues in that I am not able to write general purpose functions.
I want a function that would automatically highlight the header and the last row of the table. I am able to do this but I have to specify the name of the first column name. This is simply too much work, is there a work around?
library(tidyverse)
require(flextable)
require(rlang)
# Function that works
my_table <- function(x){
require(flextable)
require(rlang)
x %>%
flextable() %>%
# Header colour and bold
bg(bg = "#e05297", part = "header") %>%
flextable::color(color = "white", part = "header") %>%
# Last row bold and highlight
bold(i = ~rowname == "Total", bold = TRUE) %>%
bg(i = ~rowname == "Total",
bg = "grey",
part = "body")
}
mtcars %>%
rownames_to_column() %>%
adorn_totals("row") %>%
my_table()
# This is a general purpose function which is not working
my_table <- function(x){
require(flextable)
require(rlang)
first_col_name <- colnames(x) %>% .[1]
x %>%
flextable() %>%
# Header colour and bold
bg(bg = "#e05297", part = "header") %>%
flextable::color(color = "white", part = "header") #%>%
# Last row bold and highlight
bold(i = ~eval(rlang::sym(first_col_name)) == "Total", bold = TRUE) %>%
bg(i = ~eval(rlang::sym(first_col_name)) == "Total",
bg = "grey",
part = "body")
}
Any ideas how to make the general purpose my_table function to work
i argument also accepts position (row number) of the dataframe to highlight so you may use nrow to get the last row in the dataframe.
library(flextable)
library(janitor)
my_table <- function(x){
x %>%
flextable() %>%
# Header colour and bold
bg(bg = "#e05297", part = "header") %>%
flextable::color(color = "white", part = "header") %>%
bold(i = nrow(x), bold = TRUE) %>%
bg(i = nrow(x),bg = "grey",part = "body")
}
mtcars %>%
rownames_to_column() %>%
adorn_totals("row") %>%
my_table()

ggplot plots within a table

Problem
I would like to produce a good looking table which has ggplots within the cells of one column. One key element is that I would like to create a pdf output of this table eventually.
What I have tried so far
Hopefully the example below is understandable. Essentially I found that I can achieve what I want using the gt package. The problem is this creates a html widget which you then have to use phantomJS and webshot to export as a pdf.
library(dplyr)
library(purrr)
library(gt)
library(ggplot2)
dat = tibble(
RowLabel = letters[1:5],
Numeric = seq(100,500,100)
) %>%
mutate(
plotData = RowLabel %>% map(function(pos){
tibble(y=runif(10)*100) %>%
arrange(desc(y)) %>%
mutate(x=row_number())
}),
plot_obj = plotData %>% map(function(df){
df %>%
ggplot(aes(x=x,y=y))+
geom_col()
}),
plot_grob = plot_obj %>% map(cowplot::as_grob)
)
tab = dat %>%
select(RowLabel, Numeric) %>%
mutate(
ggplot = NA
) %>%
gt() %>%
text_transform(
locations = cells_body(vars(ggplot)),
fn = function(x) {
dat$plot_obj %>%
map(ggplot_image, height = px(50))
}
)
tab
What do I want
I would like an output which is similar to the above example. However, I would like a solution which does not require me to use html widgets and can be saved directly as a pdf without the use of other programs. Is this possible to do using ggplot? I have started to learn more about grids/grobs/gtables etc but have not made any meaningful progress.
Thanks in advance!
Perhaps you could tweak the gtsave() function to suit? E.g.
library(dplyr)
library(purrr)
library(gt)
library(ggplot2)
dat = tibble(
RowLabel = letters[1:5],
Numeric = seq(100,500,100)
) %>%
mutate(
plotData = RowLabel %>% map(function(pos){
tibble(y=runif(10)*100) %>%
arrange(desc(y)) %>%
mutate(x=row_number())
}),
plot_obj = plotData %>% map(function(df){
df %>%
ggplot(aes(x=x,y=y))+
geom_col()
}),
plot_grob = plot_obj %>% map(cowplot::as_grob)
)
tab = dat %>%
select(RowLabel, Numeric) %>%
mutate(
ggplot = NA
) %>%
gt() %>%
text_transform(
locations = cells_body(vars(ggplot)),
fn = function(x) {
dat$plot_obj %>%
map(ggplot_image, height = px(50))
}
)
tab %>%
gt::gtsave(filename = "test.pdf", vwidth = 180, vheight = 250)
(R v4.0.3 / gt v0.2.2)

Arrange gt tables side by side or in a grid or table of tables

I'd like to generate a set of gt table objects in a grid or side-by-side. For example, the code below uses the group_by argument to vertically separate them. But what if I wanted them separated side-by-side?
mtcars2 <-
mtcars %>%
mutate(good_mpg = ifelse(mpg > 20, "Good mileage", "Bad mileage"),
car_name = row.names(.))
mtcars2 %>%
group_by(good_mpg) %>%
slice_max(order_by = hp, n=5) %>%
arrange(hp) %>%
select(car_name, hp) %>%
gt() %>%
data_color(columns = c("hp"),
colors = col_numeric(palette = "Blues",
domain = c(0, 400)))
You can do this by using as_raw_html() for the internal tables, and fmt_markdown(columns = TRUE) in the top-level table.
hp_table <- function(x){
gt(x) %>%
data_color(columns = c("hp"),
colors = col_numeric(palette = "Blues",
domain = c(0, 400))) %>%
tab_options(column_labels.hidden = TRUE) %>%
as_raw_html() # return as html
}
good_mpg_table <-
mtcars %>%
mutate(good_mpg = ifelse(mpg > 20, "Good mileage", "Bad mileage"),
car_name = row.names(.)) %>%
filter(good_mpg == "Good mileage") %>%
head(5) %>%
arrange(hp) %>%
select(car_name, hp) %>%
hp_table()
bad_mpg_table <-
filter(good_mpg == "Bad mileage") %>%
head(5) %>%
arrange(hp) %>%
select(car_name, hp) %>%
hp_table()
data_tables <- data.frame(good_table = good_mpg_table,
bad_table = bad_mpg_table)
data_tables %>%
gt() %>%
fmt_markdown(columns = TRUE) %>% #render cell contents as html
cols_label(good_table = "High mileage",
bad_table = "Low mileage")
#Daniel, thank you for sharing this! This can come in handy.
To make the code a little bit more compact you could use group_map (or do) to generate the two tables within the dplyr workflow, then join them as you did:
library(dplyr)
library(gt)
library(scales)
hp_table <- function(x){
gt(x) %>%
data_color(columns="hp",
colors=col_numeric(palette="Blues", c(0, 400))) %>%
tab_options(column_labels.hidden = TRUE) %>%
as_raw_html()
}
mtcars %>%
mutate(good_mpg = ifelse(mpg > 20, "Good mileage", "Bad mileage"),
car_name = row.names(.)) %>%
arrange(hp) %>%
group_by(relevel(factor(good_mpg), "Good mileage")) %>%
slice_head(n=5) %>%
select(car_name, hp) %>%
group_map(~ hp_table(.x)) %>%
data.frame(.) %>%
setNames(., c("High mileage", "Low mileage")) %>%
gt() %>%
fmt_markdown(columns = TRUE)

Sharing of footnote between different part of tables using flextable

I need to create table with same footnote being placed in both header and body of the table, I cannot figure out how to make it happen using flextable, what I can create is something as below:
library(flextable)
library(dplyr)
library(tidyr)
data(iris)
iris %>%
as_tibble %>%
gather(.,key = variable,value = value,-Species) %>%
group_by(Species,variable) %>%
summarise(value=formatC(mean(value),digits = 2,format = 'f')) %>%
ungroup %>%
spread(.,key = variable,value = value) %>%
flextable %>%
footnote(.,part = 'header',i = 1,j = c(2:5),
value = as_paragraph(c('Rounded to two decimal places')),
ref_symbols = c('*'),
inline=FALSE) %>%
footnote(.,part = 'body',i = c(1:3),j = 1,
value = as_paragraph(c('Rounded to two decimal places')),
ref_symbols = c('*'),
inline=FALSE)
Currently I created two footnotes with the same statement for header and body, I wonder if I can merge the two statements into one.
Thanks!
(I did not imagine footnotes would be repeated when this function has been implemented but) by using merge_v, you can merge them if identical:
library(flextable)
library(dplyr)
library(tidyr)
data(iris)
iris %>%
as_tibble %>%
gather(.,key = variable,value = value,-Species) %>%
group_by(Species,variable) %>%
summarise(value=formatC(mean(value),digits = 2,format = 'f')) %>%
ungroup %>%
spread(.,key = variable,value = value) %>%
flextable %>%
footnote(.,part = 'header',i = 1,j = c(2:5),
value = as_paragraph(c('Rounded to two decimal places')),
ref_symbols = c('*'),
inline=FALSE) %>%
footnote(.,part = 'body',i = c(1:3),j = 1,
value = as_paragraph(c('Rounded to two decimal places')),
ref_symbols = c('*'),
inline=FALSE) %>%
merge_v(part = "footer")

Resources