'Kable table: how to keep duplicate rownames, add arrows to indicate effect direction and add values to the pack_rows header row

I am trying to format an ANOVA style output for my paper using kableExtra. I want to make it automatic to avoid mistakes.

I need to glue together several ANOVA tables from different models using the same response variables. I did this using pack_rows but since the variables' names are the same, they get numbered. I also want to round the p-values and make the significant values bold. I managed to automatize only the first (following fixp function here) I also want to include an extra column with R2 for the entire model which would be shown in the same row as the pack_rows heading.

Summary:

I want my kable table to do:

  1. keep the duplicate rownames without numbering them
  2. round the p-values, substitute very small values with <0.001, and make significant values bold
  3. add arrows indicating the effect direction (in my real data some of the variables are multi-level factors so double arrow is necessary, otherwise I would go for +/-)
  4. add a column with values on the same level as the pack_rows heading

What I tried:

  1. save rownames for each table separately as well as add it directly to kable(file, rownames=c())
  2. managed to round the p-values but when the small ones are substituted with <0.001, they become factor and column_spec doesn't work on it if I want to make bold values less than 0.05
  3. not sure if it is even possible
  4. tried with row_packs(extra_latex_after) but it doesn't work. Not even sure what it does

Any help much appreciated!

An example using the airquality data:

knitr::opts_chunk$set(echo = TRUE)
library(ggplot2)
head(airquality)
library(MuMIn)
library(tidyverse)
library(kableExtra)
library(lme4)


lm1 <- lmer(Temp ~ Ozone*Solar.R+
              (1|Month), data=airquality)
table1 <- car::Anova(lm1, type = 2, test.statistic="F")
rsq1 <- r.squaredGLMM(lm1)
table1$`F` <- as.numeric(table1$`F`)
table1$Df <- as.numeric(table1$Df)
table1$`Pr(>F)` <- as.numeric(table1$`Pr(>F)`)
lm2 <- lmer(Wind ~ Ozone*Solar.R+
              (1|Month), data=airquality)
table2 <- car::Anova(lm2, type = 2, test.statistic="F")
rsq2 <- r.squaredGLMM(lm2)
table2$`F` <- as.numeric(table2$`F`)
table2$Df <- as.numeric(table2$Df)
table2$`Pr(>F)` <- as.numeric(table2$`Pr(>F)`)

fixp <- function(x, dig=3){
  x <- as.data.frame(x)
  if(substr(names(x)[ncol(x)],1,2) != "p-value")
    warning("The name of the last column didn't start with Pr. This may indicate that p-values weren't in the last row, and thus, that this function is inappropriate.")
  x[,ncol(x)] <- round(x[,ncol(x)], dig)
  for(i in 1:nrow(x)){
    if(x[i,ncol(x)] == 0)
      x[i,ncol(x)] <- paste0("< .", paste0(rep(0,dig-1), collapse=""), "1")
  }
  x
}

kable(rbind(fixp((table1[, c(1,2,4)])), fixp((table2[, c(1,2,4)]))), digits=3, format="html", 
      booktabs=T)%>%
  kable_styling()  %>% 
  pack_rows("Sleep", 1, 3, extra_latex_after = `r rsq1[,2]`) %>%
  pack_rows("Body weight", 4, 6, extra_latex_after = `r rsq2[,2]`)

Output of the script: Output

Table copied from HTML version to XLS and the last two columns added + bold p-values: Mored desired output

(when I upload this to R and make a kable table, the last column has just the heading and values are in column 2)



Solution 1:[1]

Some ideas for 2) via the broom::tidy() and 3)

library(kableExtra)
library(broom)
library(dplyr)
table_tidy <- table1 %>% broom::tidy() %>% rbind(table2 %>% broom::tidy())

cell_bold <- table_tidy  %>% pull(p.value)  < 0.05



table_tidy %>% mutate(across("p.value", ~ifelse(.x <0.001, "<0.001", as.character(round(.x, 3))))) %>% 
 # arrows
  mutate(effect_direction = case_when(
    statistic < 0 ~  "-",
    statistic > 0 ~  "+",
    statistic == 0 ~ "\\Updownarrow"
  )) %>% 
  kbl(.,digits=3, format="html", booktabs=T)%>%
  kable_styling() %>% 
  column_spec(5, bold = cell_bold)%>% 
 pack_rows("Sleep", 1, 3) %>% 
 pack_rows("Body weight", 4, 6)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1