vincentarelbundock / tinytable

Simple and Customizable Tables in `R`
https://vincentarelbundock.github.io/tinytable
GNU General Public License v3.0
211 stars 18 forks source link

Styling individual cells based on their value #329

Closed dhicks closed 2 months ago

dhicks commented 2 months ago

This is a followup to this Bsky thread.

Here's a motivating example. I'm preparing a correlation table for a journal article. Because looking at a wall of correlations is godsawful, I want to put correlations in bold if their absolute value is greater than 0.8.

To set up, let's create a correlation table using mtcars. I'm going to coerce to a tibble just so I can convert rownames into a column in a single line.

library(tinytable)
library(tibble)

## A correlation table
dataf = mtcars |> 
    cor() |> 
    as_tibble(rownames = 'variable')
dataf
# A tibble: 11 × 12
   variable    mpg    cyl   disp     hp    drat     wt    qsec
   <chr>     <dbl>  <dbl>  <dbl>  <dbl>   <dbl>  <dbl>   <dbl>
 1 mpg       1     -0.852 -0.848 -0.776  0.681  -0.868  0.419 
 2 cyl      -0.852  1      0.902  0.832 -0.700   0.782 -0.591 
 3 disp     -0.848  0.902  1      0.791 -0.710   0.888 -0.434 
 4 hp       -0.776  0.832  0.791  1     -0.449   0.659 -0.708 
 5 drat      0.681 -0.700 -0.710 -0.449  1      -0.712  0.0912
 6 wt       -0.868  0.782  0.888  0.659 -0.712   1     -0.175 
 7 qsec      0.419 -0.591 -0.434 -0.708  0.0912 -0.175  1     
 8 vs        0.664 -0.811 -0.710 -0.723  0.440  -0.555  0.745 
 9 am        0.600 -0.523 -0.591 -0.243  0.713  -0.692 -0.230 
10 gear      0.480 -0.493 -0.556 -0.126  0.700  -0.583 -0.213 
11 carb     -0.551  0.527  0.395  0.750 -0.0908  0.428 -0.656 
# ℹ 4 more variables: vs <dbl>, am <dbl>, gear <dbl>, carb <dbl>

The suggestion on Bsky was to follow the conditional styling tutorial. Even setting aside how to exclude variable and account for absolute value, it's not clear how this is supposed to work, because I want to consider every column (except variable), not just certain particular columns. Something like which(dataf > .8) returns the index of elements in unlist(dataf), a 1-dimensional vector.

which(dataf > .8)
 [1]   1   2   3   4   5   6   7   8   9  10  11  12  24  25  26
[16]  35  36  39  46  48  60  69  72  84  96 108 120 132

which does have an argument that makes it return the coordinates of TRUE cells, in a sparse matrix format:

which(dataf > .8, arr.ind = TRUE)
      row col
 [1,]   1   1
[all of col 1 is TRUE because of the way string comparisons work]
[12,]   1   2
[13,]   2   3
[14,]   3   3
[15,]   4   3
[16,]   2   4
[17,]   3   4
[and so on]

But using this to pass columns and rows doesn't seem to work. Everything ends up in bold. I'm guessing the values passed to i and j are handled (separately) as the unique rows and columns where styling should be applied? Rather than as paired row and column coordinates.

tt(dataf) |> 
    style_tt(i = which(dataf > .8, arr.ind = TRUE)[,1], 
             j = which(dataf > .8, arr.ind = TRUE)[,2],
             bold = TRUE)

I took a couple of minutes to trace down through the call to style_tt, trying to figure out how i and j are handled when they're both integer vectors. But I ran into the generic style_eval and don't have time to dig further today.

Self-contained MWE:

library(tinytable)
library(tibble)

## A correlation table
dataf = mtcars |> 
    cor() |> 
    as_tibble(rownames = 'variable')

## Goal: highlight cells where abs(cor) > .8
tt(dataf) |> 
    style_tt(i = which(dataf > .8, arr.ind = TRUE)[,1], 
             j = which(dataf > .8, arr.ind = TRUE)[,2],
             bold = TRUE)
vincentarelbundock commented 2 months ago

Ah, I see what you mean. That's a reasonable use-case, and it is not easy with the current CRAN version.

However, I just pushed a new feature to Github to allow i to accept a logical matrix. To use it, install the development version:

remotes::install_github("vincentarelbundock/tinytable")

Restart R completely for the change to take effect. Then,

library(tinytable)
cormat <- data.frame(cor(mtcars[1:5]))

tt(cormat, digits = 2) |>
  style_tt(abs(cormat) > .8, color = "red")
Screenshot 2024-08-25 at 3 42 36 PM