gbrlrz017
gbrlrz017

Reputation: 199

How to color specific cells in a Data Frame / Table in R?

I would like to color specific cells in the following dataframe. For example, in the inputval column, I would like to highlight cells in the range of [0.8, 0.9) magenta, and the cells in that same column in the range of [0.7, 0.8) blue. Similarly, I'd like the output column cells with a value of 1 to be colored magenta and those with value 4 to be colored blue. For the rest of the cells in the dataframe, I would like them to remain white.

I have the following reproducible code which highlights by row only, and limits me to coloring in magenta and white only. How can I add another color and do so by cell?

set.seed(123)
df <- data.frame(id       = sample(1:100, 20, replace = TRUE),
                 inputval = sample(seq(0, 1, by=0.01), 20, replace = TRUE),
                 outcome  = sample(1:4, 20, replace = TRUE))

cols <- with(df, ifelse(outcome == 1, 'magenta', 'white'))

library('htmlTable')
htmlTable(as.matrix(df), col.rgroup = cols)

I realize that the issue for adding different colors is with the ifelse call in with that limits me to just magenta and white. How can I add another condition here?

While I know what's causing the multiple color issue, I'm pretty clueless about how to color only specific cells.

This is the same example as the accepted answer to this question. Thanks!

Upvotes: 12

Views: 38929

Answers (3)

rawr
rawr

Reputation: 20811

My answer below is really dumb.. Here is the proper way:

This feature is built into htmlTable via the css.cell argument:

The css.cell element allows you to add any possible CSS style to your table cells. If you provide a vector the vector it is assummed that the styles should be repeated throughout the columns. If you provide a matrix of the same size as your x argument. If have ncol(x) + 1 the first row will correspond to the rowname style. Correspondingly if the size is nrow(x) + 1 it is assummed that the first row is the header row.

So Basically you just need to define a matrix of styles for each cell:

x <- head(cars)

## indices defining where the styles go
where <- rbind(c(2,2), c(2,1), c(5,2))
style <- c('background-color: red; color: white;',
           'border: solid 1px;',
           'font-weight: 900; color: blue;')

css.cell <- matrix('', nrow(x), ncol(x))
css.cell[where] <- style

#      [,1]                 [,2]                                  
# [1,] ""                   ""                                    
# [2,] "border: solid 1px;" "background-color: red; color: white;"
# [3,] ""                   ""                                    
# [4,] ""                   ""                                    
# [5,] ""                   "font-weight: 900; color: blue;"      
# [6,] ""                   ""                                

htmlTable(head(cars), css.cell = css.cell)

enter image description here

It's hard to tell unless you are swapping back and forth, but the spacing in this table and the similar one below is slightly different. The inject_div example looks a little more centered.


A little late, but @CyrusMohammadian made a comment on my other answer, and since the comment/question is the same as this one, I'll add the answer here rather than editing my answer which was for a (slightly) different question.

Tables can get complex, and everyone has different features they want. I don't think it is possible for Max to have solutions built-in for them all.

Therefore, the easiest way I think would be to (hackily) inject some html/css into your table (you could probably also do this after running htmlTable, ie directly in the html code, but this is easier I think):

#' Inject div
#' 
#' Inject an html division tag with style attribute.
#' 
#' @param x a matrix or data frame
#' @param where an \code{nx2} matrix of row and column indices or vector (of
#' the form c(row, col, row, col, ...)) specifying which cells to select
#' @param style vector of character string(s) applied to each cell, recycled
#' if necessary

inject_div <- function(x, where, style = 'background-color: lightgrey; border: solid 1px') {
  if (!all(sapply(style, nzchar)))
    return(x)
  where <- matrix(where, ncol = 2L, byrow = !is.matrix(where))
  style <- rep_len(style, nrow(where))
  if (length(where) > 0)
    x[where] <- sprintf('<div style=\'%s\'>%s</div>',
                      gsub(';*$', ';', style), x[where])
  x
}

library('htmlTable')
htmlTable(inject_div(head(cars), cbind(2,2)))

enter image description here

htmlTable(inject_div(head(cars), where = c(2,2,2,1,5,2),
                     ## equivalently
                     # where = rbind(c(2,2), c(2,1), c(5,2))
                     style = c('background-color: red; color: white;',
                               'border: solid 1px;',
                               'font-weight: 900; color: blue;')))

enter image description here

Upvotes: 6

lukeA
lukeA

Reputation: 54237

Have you considered DT?

library(DT)
datatable(df, rownames = FALSE) %>%
  formatStyle(columns = "inputval", 
              background = styleInterval(c(0.7, 0.8, 0.9)-1e-6, c("white", "lightblue", "magenta", "white"))) %>%
  formatStyle(columns = "outcome", 
              background = styleEqual(c(1, 4), c("magenta", "lightblue"))) 

enter image description here

Upvotes: 20

Chris Watson
Chris Watson

Reputation: 1367

For adding additional colors, you would need more conditions, e.g. if you want to have a different color for an outcome of 1 and a specific inputval:

cols <- with(df, ifelse(outcome == 1,
                        ifelse(inputval == 5, 'magenta', 'red'),
                        'white')

So this should color anything with outcome == 0 as white, and if outcome == 1 and inputval == 5, it will be magenta; the others will be red.

For your other question, you can use a combination of rgroup and cgroup to specify which rows/columns you want to color, see the vignette, or see here: http://cran.r-project.org/web/packages/htmlTable/vignettes/tables.html

Upvotes: 4

Related Questions