When I render the following table as HTML, I get expected row grouping:
library(expss)
library(dplyr)
library(pander)
library(knitr)
test_data =
data.table(
division = c(rep("A",5),rep("B",5)),
group = rep(c(rep("alpha",2),rep("beta",3)),2),
subgroup = c("red","orange","yellow","green","blue",
"indigo","violet","black","white","brown"),
ins = rnorm(10),
outs = runif(10),
overs = seq(1,100,10)
)
test_data = apply_labels(test_data,
division = "Department",
group = "Center",
subgroup = "Team",
ins = "In-flow",
outs = "Out-flow",
overs = "Throughput")
test_table =
test_data %>%
tab_cells(ins,
outs,
overs) %>%
tab_cols(division %nest% group %nest% subgroup) %>%
tab_stat_fun(identity) %>%
tab_pivot() %>%
drop_rc() %>%
tab_transpose()
test_table
However, when I render it as a PDF, the row grouping fails, replaced with long strings of pipe-delimited characters.
pander(test_table)
I think this is more of a LaTeX than R problem, but I'm not sure if it doesn't lie in the middle with pander. I would prefer to output this as a PDF, given preferences from folks "up the ladder".
Default output of the expss doesn't support pdf/latex. But there is an excellent package huxtable which works with pdf:
So you need to use as_huxtable:
library(huxtable)
as_huxtable(test_table)
Related
I am creating a cross-tabulation table in R with the tbl_cross funtion using #daniel-d-sjoberg's gtsummary package. I am trying to figure out how to specify the output column widths with gtsummary so I can modify the width of the first column in the table. In addition, I want to take advantage of gtsummary's formatting options like bolding and breaking table captions over two lines (by specifying " \n" in the modify_caption statement). The problem is, I can't seem to break a caption over multiple lines AND specify a column width. My starting point was to use the following code, which breaks the caption, correctly, onto two lines:
library(tidyverse)
library(flextable)
library(gtsummary)
mytable <- iris %>%
mutate(Long.Petal = ifelse(Petal.Width > .2, "Yes", "No")) %>%
tbl_cross(
row = Long.Petal,
col = Species,
percent = "cell"
) %>%
modify_caption("This is line 1 \n This is lin 2")
This outputs the following table:
After reviewing the documentation, it looks like the only way I can find to modify the column widths is by converting the table to a flextable using gtsummary's as_flex_table and then specifying the column widths. So, to do this, I modified the code above to chane the width of the first column to 3 inches by adding two additional lines of code as indicated in the comments in the revised code below:
library(tidyverse)
library(flextable)
library(gtsummary)
mytable <- iris %>%
mutate(Long.Petal = ifelse(Petal.Width > .2, "Yes", "No")) %>%
tbl_cross(
row = Long.Petal,
col = Species,
percent = "cell"
) %>%
modify_caption("This is line 1 \n This is lin 2") %>%
as_flex_table() %>% #NEW CODE LINE 1
width(., 1, 3) #NEW CODE LINE 2
mytable
This code produces the output below, which has now incorrectly placed lines 1 and 2 of the table caption onto a single line.
Is there a way, preferably in gtsummary with tbl_cross or its options to specify the column widths AND break a table caption across multiple lines?
We may use set_caption from flextable
iris %>%
mutate(Long.Petal = ifelse(Petal.Width > .2, "Yes", "No")) %>%
tbl_cross(
row = Long.Petal,
col = Species,
percent = "cell"
) %>%
as_flex_table() %>%
set_caption(caption = "This is line 1 <br/>This is line 2",
html_escape = FALSE)
-output
After the helpful suggestions from both #akrun and #daniel-d-sjoberg, I was able to get a version of this working and output to Word in Quarto using the following code:
---
title: "Untitled"
format: docx
editor: visual
#filters:
# - docx-landscape.lua
---
```{r}
#| echo: false
#| message: false
library(tidyverse)
library(flextable)
library(gtsummary)
mytable <- iris %>%
mutate(Long.Petal = ifelse(Petal.Width > .2, "Yes", "No")) %>%
tbl_cross(
row = Long.Petal,
col = Species,
percent = "cell"
) %>%
as_flex_table() %>% #NEW CODE LINE 1
width(., 1, 1) %>% #NEW CODE LINE 2
set_caption(
as_paragraph(
as_chunk("caption \n caption 2", props = fp_text_default(font.family = "Cambria"))
), word_stylename = "Table Caption")
mytable
```
This produced:
Thanks for everyone's help!
UPDATE
After #TarJae's answer, I realized this solution doesn't support summary statistics in the captions from the gtsummary package. So, I've decided to slightly modify this solution and provide one that will allow the user to include these. This can be accomplished as follows:
library(gtsummary)
library(tidyverse)
library(flextable)
#| echo: false
mytemptable<-iris %>%
mutate(Long.Petal = ifelse(Petal.Width > .2, "Yes", "No")) %>%
tbl_cross(
row = Long.Petal,
col = Species,
percent = "cell"
) %>%
modify_caption("First Line of the Caption \n Second Line with the total sample size is {N}")
mytemptable %>%
as_flex_table() %>%
set_caption(
as_paragraph(
as_chunk(mytemptable$table_styling$caption[1], props = fp_text_default(font.family = "Cambria"))
), word_stylename = "Table Caption")
All I've done here is to initially generate the label using gtsummary. Then I convert the table to a flextable and pull in the flextable caption from the caption generated by by it (i.e., mytemptable$table_styling$caption1, in the example above).
Thanks to the hint of #DanielD.Sjoberg now we can do it this way. We first must transfer the gt_summary tbl to a gt object using as_gt() (and not only gt() -> this won't work) then we can use cols_width() function:
What is also important to select the correct column we could do:
taken from #DanielD.Sjoberg comment above:
"first column is named label. You can see all the underlying colnames with show_header_names(). There are a bunch of hidden columns ,so we can reference the column by their index number.
See here https://gt.rstudio.com/reference/cols_width.html
library(tidyverse)
library(gtsummary)
library(gt)
iris %>%
mutate(Long.Petal = ifelse(Petal.Width > .2, "Yes", "No")) %>%
tbl_cross(
row = Long.Petal,
col = Species,
percent = "cell"
) %>%
modify_caption("This is line 1 \n This is lin 2") %>%
as_gt() %>%
cols_width(
"label"~ px(250)
)
In kableExtra >= 0.8.0, the canonical way to insert a linebreak into text piped into a table from a kableExtra function such as add_header_above or pack_rows is to add an \n directly.
However, this appears not to work with the escape = FALSE argument, which is required if the text also contains LaTeX code.
How can one force linebreaks in kableExtra functions with escape = FALSE?
library(dplyr)
library(knitr)
library(kableExtra)
starwars %>%
filter(species == 'Gungan' | species == 'Droid') %>%
arrange(species) %>%
select(name, eye_color) %>%
kbl(booktabs = TRUE) %>%
pack_rows(
index = c(
'The droids: everybody\'s favourite' = 6,
'The Gungans: only beloved of \nthose aged under $3^2$' = 3),
escape = FALSE)
ISSUE
The issue at hand is that you wish to escape part of your header (i.e., the break) and not escape another part (i.e., the math code).
Further Complications
This core issue is further complicated by a number of factors:
when and how kableExtra is programmed to deal with escaping
a desire to have a solution that works for both html and LaTeX output
when and how R evaluates code
A SOLUTION
Here is a solution that will work for both html and LaTeX output, but it is not as clean and straight forward as your original code:
# a new version of `kableExtra::linebreak()` that takes into account what type
# of output is desired as well as how much escaping is necessary
linebreak2 <- function(x, double_escape = TRUE, ...) {
# if LaTeX insert text into a `\makecell[]{}` command and double escape
if(knitr::is_latex_output())
return(linebreak(x, double_escape = double_escape, ...))
# if html output just replace `\n`s with `<br/>`s
if(knitr::is_html_output())
return(gsub("\n", "<br/>", x))
# let x pass through for other types of output
return(x)
}
# build the index named vector outside the pipe flow
# in order to set the names using `linebreak2()`
index <- c(6, 3)
names(index) <- c(
'The droids: everybody\'s favourite',
linebreak2('The Gungans: only beloved of \nthose aged under $3^2$')
)
# proceed as before
starwars %>%
filter(species == 'Gungan' | species == 'Droid') %>%
arrange(species) %>%
select(name, eye_color) %>%
kbl(booktabs = TRUE) %>%
pack_rows(index = index, escape = FALSE)
PDF Output
HTML Output
You could use html line break tag <br/>:
starwars %>%
filter(species == 'Gungan' | species == 'Droid') %>%
arrange(species) %>%
select(name, eye_color) %>%
kbl(booktabs = TRUE) %>%
pack_rows(
index = c(
'The droids: everybody\'s favourite' = 6,
'The Gungans: only beloved of <br/> those aged under $3^2$' = 3),
escape = FALSE)
I have generated the following table using the Flextable package in R. I created a conditionally formatted column with LaTeX arrow symbols in it, however the symbols aren't displayed when I generate it as a flextable. Is there a way to fix this?
library(tidyverse)
library(flextable)
data.frame(one = c(10,20,30), two = c(30,20,6)) %>%
mutate(Trend = case_when(.[,2] == .[,1] ~ "$\\rightarrow$", .[,2] > .[,1] ~ "$\\nearrow$", TRUE ~ "$\\searrow$")) %>%
flextable()
It may be easier to do this with unicode values for the symbols
library(dplyr)
library(flextable)
data.frame(one = c(10,20,30), two = c(30,20,6)) %>%
mutate(Trend = ifelse(two == one, "\U2192", "\U2190")) %>%
flextable()
-output
I am using R version 3.5.2.
I would like to evaluate a string in a kable function, but I am having some issues. Normally, I can pass a string through a for loop using the get function but in the kableExtra::add_header_above function I get the following error:
Error: unexpected '=' in:"print(kable(df4,"html", col.names = c("zero","one")) %>% add_header_above(c(get("string") ="
I have tried a handful of techniques like creating a string outside of the kable function and calling it, using page breaks and print statements in the knit loop and trying the eval function as well. I have also added result ="asis" as suggested here
Here is a reproducible example:
```{r results="asis"}
library("knitr")
library("kableExtra")
df1 <- mtcars %>% dplyr::select(am,vs)
df1a <- df1 %>% mutate(type = "A")
df1b <- df1 %>% mutate(type = "B")
df1c <- df1 %>% mutate(type = "C")
df2 <- rbind(df1a,df1b,df1c)
vector <- as.vector(unique(df2$type))
for (variable in vector) {
df3 <- df2 %>% filter(type == (variable))
df4 <- table(df3$am,df3$vs)
print(kable(df4,"html", col.names = c("zero","one")) %>%
add_header_above(c(get("string") = 3)))
}
```
Ideally, I would like the header of the table to have the string name from the column type. Below is an example of what I want it to look it:
print(kable(df4,"html", col.names = c("zero","one")) %>%
add_header_above(c("A" = 3)))
I understand that the knitr function needs to be treated differently than regular R when using loops as found in this solution but I am still struggling to get the string to be evaluated correctly. Perhaps because the function requires a vecotr input, it is not evalauting it as a string?
You have to define your header as a vector. The name of the header should be the names of the vector and the value of the vector would be the number of columns the header will use.
The loop in the code should look like this:
for (variable in vector) {
df3 <- df2 %>% filter(type == (variable))
df4 <- table(df3$am,df3$vs)
header_temp = 3
names(header_temp) = get("variable")
print(kable(df4,"html", col.names = c("zero","one")) %>%
add_header_above(header_temp))
}
So first I define the number of columns the of the header in the variable header_temp and then i assign a name to it.
I am using ReporteRs to generate a flexTable using the following piece of code:
library( ReporteRs )
baseCellProp <- cellProperties( padding = 2 )
baseParProp <- parProperties(text.align = "center")
ft.list <- list(mua= 1, mub = 2, mug = 1e-7)
ft.data <- t(data.frame(ft.list))
ft.FlexTable <- FlexTable( data = ft.data,
add.rownames = TRUE,
header.columns = FALSE,
body.cell.props = baseCellProp,
body.par.props = baseParProp)
ft.FlexTable <- setFlexTableWidths(ft.FlexTable, widths =c(1,2))
ft.FlexTable
The problem I am having is all the numbers are coming out in scientific notation like this:
mua 1e+00
mub 2e+00
mug 1e-07
Is there a way to tell flexTable how to format numbers? Specifically do not apply the same format to all the rows, but apply a format that makes sense on a row-by-row basis?
I solved this problem using flextable in R Markdown document by adding set_formatter_type and controlling how all my double data type are presented. My output was padding everything with lots of zeros that I did not want.
```{r echo=FALSE, message=FALSE, warning=FALSE, results='asis'}
library(flextable)
library(tidyverse)
results <- as.tibble(read.csv("D:/ALLRESULTS.csv",header=TRUE))
results %>% na.omit() %>% filter(grepl("KEY-K",Alignment)) %>%
arrange(Year,Source) %>%
select(Source,Question,Year,Target,Actual,Highest,n) %>%
regulartable() %>%
set_formatter_type(fmt_double="%.01f") %>%
align(part="header",align="center") %>%
align(align="left") %>% autofit()
```
I solved my problem using a hint from r transposing a data frame. The problem was transposing a data frame frame coerces all the elements to characters. Using matrices solves that problem:
ft.list <- list(mua= 1, mub = 2, mug = 1e-7)
ft.data <- as.matrix(ft.list, rownames.force=TRUE )