Formatting Numbers in Flextable for Specific Columns - r

I'm using R version 3.6.1 in RStudio. I have flextable version 0.5.5 and officer version 0.3.5.
I'm having difficulty with formatting my numbers in flextables within RMarkdown. By default, all numbers show up with 3 decimal places. For some of my numbers, this is fine (and actually preferred), but for others, I want to remove the decimals.
Using the advice found here I was able to adjust my table so that all numbers are rounded to the nearest whole number. My code is below (example table used for reproduciblility; otherwise formatting is the same as my current code).
ft_test <- head(iris) %>% flextable() %>%
hline(part = 'header', border = fp_border(color = "black", width = 3)) %>%
align(align ='center', part = 'all') %>%
align(j = 1, align ='left', part = 'all') %>%
set_formatter_type(fmt_double = "%.0f")
ft_test
However, I only want certain columns to be whole numbers, and other columns to still have decimals. I've tried using the j argument to call certain columns:
ft_test <- head(iris) %>% flextable() %>%
hline(part = 'header', border = fp_border(color = "black", width = 3)) %>%
align(align ='center', part = 'all') %>%
align(j = 1, align ='left', part = 'all') %>%
set_formatter_type(fmt_double = "%.0f", j = 2)
ft_test
... but then I get an error telling me j = 2 is an unused argument.
Any suggestions for how to adjust the numbers of only some columns? Thanks in advance for your help!

You can not use argument j as it is not an argument of set_formatter_type. The function is setting formatters for one or several data type. In your case, it's better to use colformat_num.
library(flextable)
library(officer)
library(magrittr)
ft_test <- head(iris) %>% flextable() %>%
hline(part = 'header', border = fp_border(color = "black", width = 3)) %>%
align(align ='center', part = 'all') %>%
align(j = 1, align ='left', part = 'all') %>%
colformat_num(j = c("Sepal.Length", "Sepal.Width",
"Petal.Length", "Petal.Width"), digits = 1)
ft_test
You can learn more about formatting content here: https://davidgohel.github.io/flextable/articles/display.html

Related

Fix flextable dimensions in powerpoint

I am using flextable and officer in my shiny app to allow users to create powerpoint slides from the app. I am having a bit of a problem with flextable adjusting itself to the dimensions of the placeholder specified in the slide master of my ppt template.
Also I am aware that autofit in flextable does not work for pptx.
Below are two examples, in the first example (with the mtcars data) the table seems to be fine in terms of automatically adjusting the font sizes to match the width and height given.
df <- as_tibble(mtcars, rownames = "cars") %>%
mutate(Date = as.Date("2020-01-01"))
select(-4,5)
flextable::flextable(df) %>%
fit_to_width(max_width = 5.928819) %>%
height(height = 4.6319444/nrow(df)) %>%
color(i=~str_detect(cars,"Merc"), j=~cars, color = "red", part = "body") %>%
bg(i = ~str_detect(cars,"Merc"), j=~cars, bg="black", part = "body") %>%
color(i=~str_detect(cars,"Hornet"), j=~cars, color = "blue", part = "body") %>%
color(i=~mpg < 20, j=~mpg, color = "green", part = "body") %>%
#my_theme() %>%
print(preview = "pptx")
However in the second example with the air quality data the table is always longer that the height specified and the font size does not automatically adjust. Can anyone shed light on why one table works but the other doesnt?
df4 <- mutate(airquality, Ozone = 1000*Ozone) %>%
head(30) %>%
mutate(word = "ABCDEF")
flextable::flextable(df4) %>%
fit_to_width(max_width = 5.9288199) %>%
height(height =4.6319444/(nrow(df4))) %>%
add_header_row(top = TRUE, values = c("quality", "time", "rand"), colwidths = c(4,2,1)) %>%
add_footer_lines("some text can go here") %>%
colformat_num(j=~Ozone, big.mark = ",") %>%
colformat_double(j=~Wind, suffix = " $") %>%
vline_left(border = fp_border(color = "red", style="solid", width = 1)) %>%
vline_right(border = fp_border(color = "red", style="solid", width = 1)) %>%
vline(j=~Temp, border = fp_border()) %>%
set_header_labels(Temp = "Temperature") %>%
#hrule(rule = "exact", part = "body") %>%
print(preview = "pptx")

Passing columns to kableExtra arguments within dplyr

I'm trying to use kable and kableExtra to format tables created using pipes, and I can't get the conditional formatting arguments (row_spec, column_spec) to accept variables piped from the generated code.
In the toy example below I create a variable called bg within the dataframe that I want to use to create bands of background colour, but row_spec and column_spec don't seem to recognize that as a variable. Note that creating the variable outside of the pipes isn't an option - the actual use case is much more complicated than that, and the variables used in the process don't exist before that.
library(kableExtra)
set.seed(111)
df = data.frame(var1 = sort(sample(LETTERS[1:3],10,TRUE)),
var2 = sample(1:4,10,TRUE),
var3 = runif(10,0,1))
df %>%
mutate(bg = cumsum(!duplicated(var1))%%2)%>%
kable() %>%kable_styling()%>%
column_spec(1,color=bg)
Error in ensure_len_html(color, nrows, "color") : object 'bg' not found
EDIT: You can do it in two steps easily enough, so I'll include that here, as well as the resulting table that I'm looking for
d = df %>%
mutate(bg = cumsum(!duplicated(var1))%%2)
kable(d) %>% kable_styling(full_width=FALSE) %>%
row_spec(which(d$bg==1),background=grey(0.75))
This might work:
library(tidyverse)
library(kableExtra)
set.seed(111)
df = data.frame(var1 = sort(sample(LETTERS[1:3],10,TRUE)),
var2 = sample(1:4,10,TRUE),
var3 = runif(10,0,1))
df %>%
mutate(bg = cumsum(!duplicated(var1))%%2) %>%
mutate(bg = cell_spec(bg, color = spec_color(bg))) %>%
kable(escape = F) %>% kable_styling()
Output:
You may find more information here (page 16): https://haozhu233.github.io/kableExtra/awesome_table_in_pdf.pdf
Version with background color:
df %>%
mutate(bg = cumsum(!duplicated(var1))%%2) %>%
mutate(bg = cell_spec(bg, color = "white", bold = T,
background = spec_color(bg))) %>%
kable(escape = F) %>% kable_styling()
Output with background color:

reduce vertical padding in as_flex_table

as_flex_table_ex1 <-
trial %>%
select(trt, age, grade) %>%
tbl_summary(by = trt,missing = "no") %>%
add_p() %>%
as_flex_table() %>%
valign(j = 1,valign = "center")
save_as_docx(as_flex_table_ex1,path = "1.docx")
Now - when I open the table in word, the padding between grade I,II and III is very "airy". How can I reduce this padding? I tried using the padding(as_flex_table_ex1,padding.top=0,padding.bottom = 0,part="all"), but I cant really see a difference in the rendered docx.
So after a bit of research. This seems to do the trick.
as_flex_table_ex1 <-
trial %>%
select(trt, age, grade) %>%
tbl_summary(by = trt,missing = "no") %>%
add_p() %>%
as_flex_table() %>%
valign(j = 1,valign = "center")
as_flex_table_ex2 <- as_flex_table_ex1 %>%
hrule(rule = "exact") %>%
height(height=max(dim_pretty(.,part="body")$heights+0.3*max(dim_pretty(.,part="body")$heights)))
print(as_flex_table_ex2,preview="docx")
The dim_pretty factor multiplied by 0.3 is completely arbitrary. If someone is up to finding a legible height that automatically allows for different fonts and sizes it would be great.
I've run into the same issue. These are the commands I use to try to achieve a more compact table (much of what you've already tried). This is available in the function gtsummary::theme_gtsummary_compact().
x %>%
flextable::fontsize(size = 8, part = "all") %>%
flextable::padding(padding.top = 0, part = "all") %>%
flextable::padding(padding.bottom = 0, part = "all")
I have had more success using a R Markdown Word templates, which helps direct the formatting in the Word doc.
https://bookdown.org/yihui/rmarkdown-cookbook/word-template.html

Loop through columnames to format colums with R-Package formattable kableExtra (R dplyr)

Hei,
To compare several variants of data I produced a HTML report.
Given a special catagory some indexes in the database should be the same. To detect errors / incorrect entries in the database I compare the different categories in a table.
For better reading, it would be fine, to have coloured tables. This can be done easily with the formattable-Package.
My dataset:
require(tidyverse)
require(formattable)
require(kableExtra)
require(knitr)
df1 <- data.frame(V1 = c(68,sample(c("J","N"),size=15,replace = TRUE)),
V2 = c(10,sample(c("J","N"),size=15,replace = TRUE)),
V3 = c(1,sample(c("J","N"),size=15,replace = TRUE))
)
It has - in this example - 3 differnt variants. Only one is recomended. It is supposed, that the variant with the highest N (=first entry in each Vx-Column) is the real one.
My formated table is produced with this code:
df1 %>%
mutate(
V2 = ifelse((as.character(V2) == as.character(V1)) == FALSE,
cell_spec(V2, color = "red",bold = TRUE),
cell_spec(V2, color = "black",bold = FALSE)),
V3 = ifelse((as.character(V3) == as.character(V1)) == FALSE,
cell_spec(V3, color = "red",bold = TRUE),
cell_spec(V3, color = "black",bold = FALSE))
) %>%
kable(format = "html", escape = FALSE) %>%
kable_styling(c("striped", "condensed"), full_width = FALSE) %>%
row_spec(1, bold = T, color = "white", background = "#D7261E")
Two questions:
How to mutate in a loop?
This is necessary because the different categories I have to investigate can have up to 18 different variants. In each dataset, V1 is everytime the reference variant.
As you can see (run the code!) the first line (the "N"s) is coded in the wrong matter. Is it possible to compare from the second line on only (first line is set to TRUE by default)
This would be fine, because the first line is now formated in a matter that does not really make sense.
Thank you!
To answer your two questions:
Instead of looping over the columns, you can use mutate_all
Just take a copy of the first column and mutate it back in later
I have first made your cell_spec calls into functions to reduce clutter in the code.
red <- function(x) cell_spec(x, color = "red", bold = TRUE)
black <- function(x) cell_spec(x, color = "black", bold = FALSE)
c1 <- as.character(df1[[1]])
Now we can do this:
df1 %>%
select(-V1) %>%
mutate_all(function(x) ifelse(as.character(x) != df1[[1]], red(x), black(x))) %>%
mutate(V1 = black(c1)) %>%
mutate_all(function(x) `[<-`(x, 1, " ")) %>%
select(V1, V2, V3) %>%
kable(format = "html", escape = FALSE) %>%
kable_styling(c("striped", "condensed"), full_width = FALSE) %>%
row_spec(1, bold = T, color = "white", background = "#D7261E")
Which gives this result:
Thank you, #AllanCameron!
I 'm not familiar to the package purrr - I really should do more studies about it.
Your idea with purrr::map_dfc solved the problem.
Instead of the first column I need the first row (the digit-row), and of course with grepl it is possible to solve this. The condition in the ifelse-Statement is a little bit longer then.
My final solution is then:
df1 %>%
map_dfc(function(x) ifelse(as.character(x) != as.character(df1$V1) & !grepl("[[:digit:]]",x),
mark_true(x), mark_false(x))) %>%
select(V1, everything()) %>%
kable(format = "html", escape = FALSE) %>%
kable_styling(c("striped", "condensed"), full_width = FALSE) %>%
row_spec(1, bold = T, color = "white", background = "#D7261E")
Thank you very much!

How to adjust vertical alignment in a flex table

I am trying to generate a flex table in HTML using R Markdown that has several cells that are merged across multiple rows using ReporteRs::spanFlexTableRows(). However, when I merge the cells together, the numbers displayed in the merged cells appear at the top of the cells. Moreover, the border between the two sets of (adjacent) merged cells disappears. I would like to know:
How to center the contents of each merged cell vertically, and
How to retain the border between the two merged cells
The following R script illustrates the issue I am having:
library(tidyverse)
library(ReporteRs)
mtcars[1:4,] %>%
mutate(
x = row.names(mtcars[1:4,]),
var1 = 1,
var2 = 2
) %>%
select(
x, mpg, cyl, disp, var1, var2
) %>%
make_ft %>%
spanFlexTableRows(j=5, from=1, to= 4) %>%
spanFlexTableRows(j=6, from=1, to= 4)
Thanks for your help!
I have a solution to a closely related question -- merging and vertical alignment using officer/flextable rather than ReporteRs. Since officer/flextable are intended to replace ReporteRs I think this is reasonable to post here.
Vertical alignment can intuitively be done in the github (as of 2019-02-10) version of flextable using valign():
library(tidyverse)
library(officer)
library(flextable)
mtcars[1:4,] %>%
mutate(
x = row.names(mtcars[1:4,]),
var1 = 1,
var2 = 2
) %>%
select(
x, mpg, cyl, disp, var1, var2
) %>%
flextable() %>%
flextable::merge_at(j=5, i=1:4) %>%
flextable::merge_at(j=6, i=1:4) %>%
valign(j = 5:6, valign = 'top') -> myft
myft
Vertical alignment in flextable was historically (as of 2019-02-10 CRAN version) part of the flextable::rotate() function. Here's your example:
library(tidyverse)
# library(ReporteRs)
library(officer)
library(flextable)
# turn into flextable, merge and apply a vertical alignment
# (note that center vertical alignment appears to be the default in flextables
# so despite your goals i do top alignment here)
mtcars[1:4,] %>%
mutate(
x = row.names(mtcars[1:4,]),
var1 = 1,
var2 = 2
) %>%
select(
x, mpg, cyl, disp, var1, var2
) %>%
flextable() %>%
flextable::merge_at(j=5, i=1:4) %>%
flextable::merge_at(j=6, i=1:4) %>%
rotate(j = 5:6, align = 'top', rotation = 'tblr') -> myft
# write to a docx using officer
doc = read_docx()
doc = flextable::body_add_flextable( doc, myft )
print(doc, target = "ftex.docx" )

Resources