How to rotate a table left margin name with knitr and xtable? - r

I'm trying to get the results of the table command nicely printed on a pdf by using knitr and xtable.
As a toy example let's say I want to get the table created with
table(c(2,5,5,5,5,7,7,7,7,NA),c(1,5,2,2,2,2,7,7,NA,NA))
I would like to get something like this:
As you can see var1 is rotated 90 degrees counterclockwise.
How can I get it?
Similar results, with less or more lines are OK too.
I've being trying different methods I've found.
I've created this Rnw file,
\documentclass{article}
\usepackage{booktabs}
\usepackage{rotating}
\begin{document}
<<r table, results='asis', echo=FALSE>>=
library(knitr)
library(xtable)
var1 <- c(2,5,5,5,5,7,7,7,7,NA)
var2 <- c(1,5,2,2,2,2,7,7,NA,NA)
print(xtable(table(var1,var2)))
print.xtableFtable(xtableFtable(ftable(var1,var2),
method = "row.compact"))
print.xtableFtable(xtableFtable(ftable(var1,var2),
method = "row.compact"), rotate.rownames = TRUE)
print.xtable(xtable(table(var1,var2)), include.rownames=T,include.colnames=T)
#
\end{document}
You can see below the result of the three methods I've tried.
I can't get the expected result.
Any solution with other common package or kable it's OK too.
I guess it could be done with \rotatebox{90} but I don't know how to force xtable to use it nor how to tell xtable to place the left title on the left instead of just being on the top-right.

Here's one possibility. Disclaimer: I am the package author.
library(huxtable)
var1 <- c(2,5,5,5,5,7,7,7,7,NA)
var2 <- c(1,5,2,2,2,2,7,7,NA,NA)
tbl <- table(var1 = var1, var2 = var2)
ht <- as_hux(tbl)
ht <- cbind(rep('', 4), ht)
ht[2,1] <- 'var1'
ht <- rbind(rep('', 6), ht)
ht[2,2] <- '' # get rid of "rownames"
ht[1,3] <- 'var2'
colspan(ht)[1,3] <- 4
rowspan(ht)[3, 1] <- 3
rotation(ht)[3, 1] <- 90
right_border(ht)[,2] <- 1
bottom_border(ht)[5, -1] <- 1
bottom_border(ht)[2, -1] <- 1
ht
When used in an rmarkdown PDF document, this produces:

Related

R Markdown List of Kabels output to PDF

I have a 500 x 500 dataset which I would like to be represented in kables in my PDF output. I've arbitrarily chosen 50 x 50 Kables for the output.
Each kabel cell will have an image in it^. Some of these examples will work with pure text so including the image is required in the minimum code.
In the code below, there is the setup and the 3 approaches I've taken:
the 500x500 approach which is obviously too big,
the list method which I can't get to actually render the tables,
the separate, dynamically named, objects^ approach which will render a table if called by exact name but can't get to render from a dynamic name.
The generation of the kables are in different code chunks to the output for ease of positioning in the PDF.
I have tried all these with results = 'asis' and it made no difference.
---
title: "Minimum Example"
date: "2022-11-19"
output: pdf_document
---
\section*{A Bit of Setup}
\renewcommand{\arraystretch}{1.7}
```{r setup}
library(kableExtra)
library(dplyr)
library(tidyr)
library(stringr)
library(png)
library(knitr)
# Image is displayed in each cell of the Kable, creating an example image
png(file="./ExampleImage.png")
par(bg='grey')
bob <- plot.new()
dev.off()
# Define size of data which needs to be represented in a kable.
totalSize_x <- 500
totalSize_y <- 500
```
\section*{Approach 1: Way too big to be displayed}
```{r Approach 1: The Big Boy}
TheBigBoi <- matrix('\\includegraphics[scale=1]{./ExampleImage.png}',totalSize_x, totalSize_y)
kable(TheBigBoi,
format = "latex",
escape = FALSE,
longtable = FALSE) |>
column_spec(1,border_left = T) |>
column_spec(totalSize_y,border_right = T) |>
kable_styling(latex_options=c("scale_down"))
# Gives the Errors
# Error: C stack usage 12426279 is too close to the limit
# Execution halted
```
\section*{Approach 2: A Nice List}
```{r Approach 2: A Nice List}
# Personally, I want this one to work as it's the least stupid solution.
maxKableCells <- 50 # As in that looks good for the images, I understand Kables can be bigger if they want to
TheLittleOne <- matrix('\\includegraphics[scale=1]{./ExampleImage.png}', maxKableCells, maxKableCells)
num_iterations <- (totalSize_x/maxKableCells) * (totalSize_y/maxKableCells)
myList <- vector(mode = "list", length = num_iterations)
for (i in 1:(num_iterations)){
myList[i] <- kable(TheLittleOne,
format = "latex",
escape = FALSE,
longtable = FALSE) |>
column_spec(1,border_left = T) |>
column_spec(maxKableCells,border_right = T) |>
kable_styling(latex_options=c("scale_down"))
}
```
Gonna put some text here
```{r Approach 2 Output}
myList[1] # - ! Undefined control sequence.
#l.177 ``\textbackslash begin\{table\}\n
# \textbackslash centering\n\textbacksl...
```
```{r Approach 2 Output Continued}
noquote(myList[1]) # - just prints out the latex code with extra slashes
```
\section*{Approach 3: Objectively the worst way to acheive this}
```{r Approach 3: Individual Kable Objects AKA Foolishness}
# The most stupid solution but the closest to working
maxKableCells <- 50 # As in that looks good for the images, I understand Kables can be bigger if they want to
TheLittleOne <- matrix('\\includegraphics[scale=1]{./ExampleImage.png}', maxKableCells, maxKableCells)
num_iterations <- (totalSize_x/maxKableCells) * (totalSize_y/maxKableCells)
for (i in 1:(num_iterations)){
assign(paste0('kable_', i), kable(TheLittleOne,
format = "latex",
escape = FALSE,
longtable = FALSE) |>
column_spec(1,border_left = T) |>
column_spec(maxKableCells,border_right = T) |>
kable_styling(latex_options=c("scale_down")))
}
```
Some filler text
```{r Approach 3 Output}
kable_1 # - This one works!
for (i in 1:num_iterations){
paste0('kabel_', i) #Returns nothing
print(paste0('kabel_', i)) # Prints like "kabel_1"
print(noquote(paste0('kabel_', i))) #Prints like kabel_1
noquote(paste0('kabel_', i)) # Returns nothing
}
```
^ I am aware this is a stupid thing to do, this whole thing is stupid but sometimes the tools we use are not our own choice.

knitr changes (1) to <ol> when rendering html?

The following content of a .Rmd file:
---
title: "Untitled"
output:
html_document: default
---
```{r cars}
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
knitr::kable(mtcars, format = "html")
```
Will show ordered lists <ol><li></li></ol> in the am column, instead of the numbers in brackets (as produced with the sprintf) after rendering to html.
Is this intended? How can I work around this and have numbers in brackets show as they are in the html output?
The output of knitr::kable seems to be fine, showing:
<td style="text-align:left;"> (1) </td>
Details:
Using knitr 1.20
RStudio Server 1.1.453
note that removing format = "html" does not resolve the issue as in the real-life context I would like to do advanced formatting with css e.g. based on the classes of the produced tables
A quick workaround solution based on Michael Harper's accepted answer may be a method like so:
replacechars <- function(x) UseMethod("replacechars")
replacechars.default <- function(x) x
replacechars.character <- function(x) {
x <- gsub("(", "&lpar;", x, fixed = TRUE)
x <- gsub(")", "&rpar;", x, fixed = TRUE)
x
}
replacechars.factor <- function(x) {
levels(x) <- replacechars(levels(x))
x
}
replacechars.data.frame <- function(x) {
dfnames <- names(x)
x <- data.frame(lapply(x, replacechars), stringsAsFactors = FALSE)
names(x) <- dfnames
x
}
Example use:
mtcars <- datasets::mtcars
# Create a character with issues
mtcars$am <- sprintf("(%s)", as.character(mtcars$am))
# Create a factor with issues
mtcars$hp <- as.factor(mtcars$hp)
levels(mtcars$hp) <- sprintf("(%s)", levels(mtcars$hp))
replacechars(mtcars)
If you don't want to remove the format="html" argument, you could try using the HTML character entities for the parentheses (&lpar and &rpar) and then add the argument escape = FALSE:
```{r cars}
mtcars$am <- sprintf("&lpar;%s&rpar;", as.character(mtcars$am))
knitr::kable(mtcars, format = "html", escape = FALSE)
```
Still not entirely sure of what is causing the error though. It seems that the specific combination of parentheses is being processed strangely by knitr.
An alternative solution is to escape the parentheses, e.g.,
mtcars$am <- sprintf("\\(%s)", as.character(mtcars$am))
Then you won't need escape = FALSE.
See https://pandoc.org/MANUAL.html#backslash-escapes in Pandoc's Manual.

Loop in rmarkdown

I am relatively new to r and rmarkdown so I apologise in advance if this is a stupid question. This is a simple replication of a bigger dataset.
I have three columns in a dataframe:
df <- data.frame( c(a, b), c(c, d), c(e, NA))
names(df) <- c("X", "Y", "Z")
I want to show them in a rmarkdown file as follows:
I like a b.
This is c
This is e
This is d
I have written a function that includes
X <- 0
for (i in 1:nrow(df)) {
X[i] <- df$X[[i]] }
Y <- 0
for (i in 1:nrow(df)) {
Y[i] <- df$Y[[i]] }
X <- 0
for (i in 1:nrow(df)) {
Z[i] <- df$Z[[i]] }
And in the markdown file (the bit I'm struggling with)
I like `r X` ### This is fine
``` {r}
for (i in 1:nrow(df)) {
Y[i]
Z[i] } ### Doesn't work and I want to include text i.e. This is
```
I want to make some sort of loop so it prints the element in row 1 of column Y then Z, then the next row etc. and skip ifNA
Any help whatsoever would be majorly appreciated! :)
First, I'd give you some tips in your first loop. If you want to pass a data.frame column to a vector, you can vectorize it. I recommend you check this later. Hence, instead of:
X <- 0
for (i in 1:nrow(df)) {
X[i] <- df$X[[i]] }
try to do:
X <- vector("numeric", nrow(df)) #suggestion to create a empty numerical vector
X <- as.numeric(df$X)
Answering your main question, you can name your code chunk to keep the things organized. Use eval=FALSE if you desire only the output and not the code printed. Now, you have your vectors and can use #jason suggestion:
I like `r X`
```{r code_chunk1, eval=FALSE}
paste0("This is ", X)
paste0("This is ", Y)
paste0("This is ", paste(Z,collapse = " ")) # if you want them all in the same line
}
```
Avoid the operator, it can produce unexpected results and create problems without you noticing! Visit this.
There is no need to use loops. However, the elements of df need to be re-arranged to get printed row-wise.
The rmarkdown file below reproduces the expected result:
---
title: Loop in rmarkdown
output: html_document
---
```{r, echo=FALSE}
df <- data.frame( c("a", "b"), c("c", "d"), c("e", NA))
names(df) <- c("X", "Y", "Z")
```
I like `r df$X`
```{r, echo=FALSE, warning=FALSE}
library(magrittr) # use piping to improve readability
df[, 2:3] %>% # treat columns like a matrix
t() %>% # change from row first to column first order
as.vector() %>% # flatten into vector
na.omit() %>% # drop NAs
paste("This is", .) %>% # Prepend text
knitr::kable(col.names = NULL) # print as table
```
The output is
Note that knitr::kable(col.names = NULL) is used to create inline text, i.e., text output not wrapped in a verbatim element.
Alternatively, the chunk option results='asis' can be used:
```{r, echo=FALSE, warning=FALSE, results='asis'}
library(magrittr) # use piping to improve readability
df[, 2:3] %>% # treat columns like a matrix
t() %>% # change from row first to column first order
as.vector() %>% # flatten into vector
na.omit() %>% # drop NAs
paste("This is", ., collapse = " \n") %>% # Prepend text and collapse into one string
cat() # use cat() instead of print()
```
Note that the 2 blanks before \n are required to indicate a line break in rmarkdown.

knitr how to get exponent/footnotes in latex table using xtable

I would like to have a footnote in a cell of a latex table. Usually I would do it with:
... & $^{1)} $ ...
How can I get this using xtable? The following is a minimal example and I tried several ways to get what I want:
\documentclass[9pt]{article}
\begin{document}
<<echo=TRUE>>=
df0 <- data.frame(x=c(1:5), y=c('a','b','c','d','e'))
df0$y <- as.character(df0$y)
df0$y[df0$y=="a"] <- "$^{1)}$"
df0$y[df0$y=="b"] <- "$\\^{2)}$"
df0$y[df0$y=="c"] <- "$^3$"
df0$y[df0$y=="d"] <- "$\\^4$"
df0$y[df0$y=="e"] <- "\\$\\^4\\$"
df0
#
<<echo=FALSE, results="asis">>=
xtable(df0)
#
\end{document}
Any hint appreciated.
See ?print.xtable and check out the sanitize options.
Use your original LaTeX method of inclusion then something like this should work...
<<echo=FALSE, results="asis">>=
require(xtable)
tbl <- xtable(df0)
print(tbl, type="latex", sanitize.text.function=function(x){x})
#

Test statistic (e.g. chisquare test) inside latex table using the tables-package in R/Knitr/Rstudio

I would like to use the tabular()-function from the tables-package to do a cross-tabulation of two variables (e.g. v1 and v2), and present the p-value of the chisq-test in the table. It is easy to get the crosstabulation, but I cant get the p-value inside the table. This is what I've been trying, without any luck:
\documentclass{article}
\begin{document}
<<echo=TRUE,message=FALSE>>=
library(Hmisc)
library(tables)
v1 <- sample(letters[1:2],200,replace=TRUE)
v2 <- sample(month.name[1:3],200,replace=TRUE)
df <- data.frame(v1,v2)
#
It is straight forward to get the crosstabulation:
<<results='asis'>>=
latex( tabular( Factor(v1) ~ Factor(v2) , data=df) )
#
But I cant get the p-value inside the table:
<<results='asis'>>=
latex( tabular( Factor(v1)*chisq.test(v1,v2)$p.value ~ Factor(v2) , data=df) )
#
\end{document}
I don't know how to do it with tables::tabular but this will do it with Hmisc::summary.formula.reverse assuming you have your system configured to produce pdf files through latex(). I had to do searching of the Rhelp archives to figure out that the 'exclude1' argument needed to go in the latex argument list. Once you go back through the documentation exclude1 does appear in the usage example of latex.summary.formula.reverse although I thought I was reading the help page for summary.rms:
library(Hmisc)
latex(summary( v2 ~ v1, data=df, method="reverse" ,test=TRUE), exclude1=FALSE)
You can intercept the latex output "along the way" if you want to embed it in a longer document by assigning the output to a named file.
latex(summary( v2 ~ v1, data=df, method="reverse" ,test=TRUE), exclude1=FALSE, file="")
#--------
% latex.default(cstats, title = title, caption = caption, rowlabel = rowlabel, col.just = col.just, numeric.dollar = FALSE, insert.bottom = legend, rowname = lab, dcolumn = dcolumn, extracolheads = extracolheads, extracolsize = Nsize, ...)
%
\begin{table}[!tbp]
\caption{Descriptive Statistics by v2\label{summary}}
\begin{center}
\begin{tabular}{lcccc}
\hline\hline
\multicolumn{1}{l}{}&\multicolumn{1}{c}{February}&\multicolumn{1}{c}{January}&\multicolumn{1}{c}{March}&\multicolumn{1}{c}{Test Statistic}\tabularnewline
&\multicolumn{1}{c}{{\scriptsize $N=56$}}&\multicolumn{1}{c}{{\scriptsize $N=73$}}&\multicolumn{1}{c}{{\scriptsize $N=71$}}&\tabularnewline
\hline
v1~:~a&43\%~{\scriptsize~(24)}&47\%~{\scriptsize~(34)}&44\%~{\scriptsize~(31)}&$ \chi^{2}_{2}=0.21 ,~ P=0.901 $\tabularnewline
~~~~b&57\%~{\scriptsize~(32)}&53\%~{\scriptsize~(39)}&56\%~{\scriptsize~(40)}&\tabularnewline
\hline
\end{tabular}
\end{center}
Numbers after percents are frequencies.\\\noindent Test used:\\Pearson test\end{table}
You can also paste text from a chi-squared statistic into a caption via xtable(). For example:
#sample data
var1<-sample(c('A', 'B'), 10, replace=T)
var2<-sample(c('Red', 'Blue'), 10, replace=T)
#join in frequency table
tab<-table(var1, var2)
#conduct chisq.test
test<-chisq.test(tab)
#show values of chisq.test()
name(test)
#Use xtable, use print.xtable for further manipulations
out<-xtable(tab, caption=paste('Important table, chi-squared =', test$statistic, ', p=', test$p.value,',' ,test$parameter, 'df', sep=' '))
#print
out

Resources