0

I have been trying to learn how to format tables for output in R. I'm now trying to take the table in example and format the numbers in the columns. I would like to have two digits in some columns and none in some columns like the rank column. Also, I would like to keep the row names as Year Month. How would one go about creating a table like this?

Note: Also the "3" in the column 'sepal.with' is printed as "3" not "3.0".

library(gtable)
library(grid)
library(gridExtra)
library(zoo)

iris <- as.matrix(iris[1:4, 1:3])
rownames(iris)<-as.yearmon(seq(as.Date("2000/1/1"), as.Date("2000/4/1"), by = "month"))
RankColumn<-seq(1, 4, by = 1)
iris<-cbind(iris, RankColumn)
iris<- round(as.matrix(iris), digits=2)


# a simple function to scale each column to the range [0, 1]
norm <- function(x) {
apply(x, 2, function(y){(y-min(y))/(max(y)-min(y))})
}

bluecol <- colorRamp(c("#3366EE", "#AABBFF", "#DDDDFF"))(norm(iris))
bluecol <- rgb(bluecol[, 1], bluecol[, 2], bluecol[, 3], max=255)

tt <- ttheme_default(core=list(bg_params=list(fill=bluecol)))

g <- tableGrob(iris, theme=tt)
g <- gtable_add_grob(g,
grobs = rectGrob(gp = gpar(fill = NA, lwd = 2)),
t = 2, b = nrow(g), l = 1, r = ncol(g))
g <- gtable_add_grob(g,
grobs = rectGrob(gp = gpar(fill = NA, lwd = 2)),
t = 1, l = 1, r = ncol(g))
grid.draw(g)
user2946746
  • 1,740
  • 3
  • 21
  • 36

2 Answers2

3

Something like this may work to get you the format you're looking for.

library(gtable)
library(grid)
library(gridExtra)
library(zoo)

iris <- as.matrix(iris[1:4, 1:3])
rownames(iris)<-as.yearmon(seq(as.Date("2000/1/1"), as.Date("2000/4/1"), by = "month"))
RankColumn<-seq(1, 4, by = 1)
iris<-cbind(iris, RankColumn)

# Create the matrix
iris<- as.matrix(iris)


# a simple function to scale each column to the range [0, 1]
norm <- function(x) {
  apply(x, 2, function(y){(y-min(y))/(max(y)-min(y))})
}

# function to format columns
format.column <- function(matrix, colnum, rounding, decimals){
  formatted <- format(round(as.numeric(matrix[,colnum]), digits = rounding),nsmall = decimals)
  return(formatted)
}

bluecol <- colorRamp(c("#3366EE", "#AABBFF", "#DDDDFF"))(norm(iris))
bluecol <- rgb(bluecol[, 1], bluecol[, 2], bluecol[, 3], max=255)

tt <- ttheme_default(core=list(bg_params=list(fill=bluecol)))

# Set formatting for individual columns 
# Adjust these or add additional columns to format as necessary
iris[,1] <- format.column(matrix = iris, colnum = 1, rounding = 2, decimals = 2)
iris[,2] <- format.column(matrix = iris, colnum = 2, rounding = 2, decimals = 1)

g <- tableGrob(iris, theme=tt)
g <- gtable_add_grob(g,
                     grobs = rectGrob(gp = gpar(fill = NA, lwd = 2)),
                     t = 2, b = nrow(g), l = 1, r = ncol(g))
g <- gtable_add_grob(g,
                     grobs = rectGrob(gp = gpar(fill = NA, lwd = 2)),
                     t = 1, l = 1, r = ncol(g))
grid.draw(g)
Matt Jewett
  • 3,249
  • 1
  • 14
  • 21
0

While functional and flexible, I found Matt's format function a bit over complicated. A little sprintf magic should suffice. Also, converting the yearmon objects to character will keep the format.

library(gtable)
library(grid)
library(gridExtra)
library(zoo)

data(iris)
iris <- iris[1:4, 1:3]
rownames(iris) <- as.character(as.yearmon(
  seq(as.Date("2000/1/1"), as.Date("2000/4/1"), by = "month")))
iris$RankColumn <- 1:nrow(iris)

# a simple function to scale each row or column to the range [0, 1]
# will convert characters to numerics if in a sensible format
norm <- function(x, mar=2) {
    rnames <- rownames(x)
    x <- apply(x, 2, as.numeric)
    x <- apply(x, mar, function(y){(y-min(y))/(max(y)-min(y))})
    rownames(x) <- rnames
    x
}

# function to pad with zero
# by default does not pad integers
zeropad <- function(x, nz=1, exc.int=TRUE) {
    if (is.integer(x) & exc.int) {
        x
    } else { 
        sprintf(paste0("%.", nz, "f"), x)
    }
}

bluecol <- colorRamp(c("#3366EE", "#AABBFF", "#DDDDFF"))(norm(iris))
bluecol <- rgb(bluecol[, 1], bluecol[, 2], bluecol[, 3], max=255)

tt <- ttheme_default(core=list(bg_params=list(fill=bluecol)))

# convert floats to zero-padded characters
iris[1:ncol(iris)] <- sapply(iris, zeropad, 2)

g <- tableGrob(iris, theme=tt)
g <- gtable_add_grob(g,
                     grobs = rectGrob(gp = gpar(fill = NA, lwd = 2)),
                     t = 2, b = nrow(g), l = 1, r = ncol(g))
g <- gtable_add_grob(g,
                     grobs = rectGrob(gp = gpar(fill = NA, lwd = 2)),
                     t = 1, l = 1, r = ncol(g))
plot.new()
grid.draw(g)

(edited to work exclusively with data frames. norm and zeropad made a bit 'smarter')

enter image description here

AkselA
  • 8,153
  • 2
  • 21
  • 34
  • I believe you have to change the line "apply(iris[, -4], 2, zeropad)" to mapply(zeropad, iris[, -4], 2) – user2946746 May 24 '17 at 14:51
  • @user2946746: Both will work. If you want to pad with two zeros you can do: `apply(iris[, -4], 2, zeropad, 2)`, `mapply(zeropad, iris[, -4], 2)` or `sapply(iris[, -4], zeropad, 2)`. They will all give the same result. I tend to use `apply` on arrays and matrices and `*apply` (inc. `mapply` and `sapply`) on list like objects (inc. data frames). – AkselA May 24 '17 at 15:50
  • Thanks, I tried to format each one column differently. iris[, c(1)] <- mapply(zeropad, iris[, c(1)], 0 ). I lose the column by column colouring. Any idea why? – user2946746 May 24 '17 at 15:54
  • If you format the columns before you apply the colour function it won't work properly, as the formatting converts the numbers to characters, and you can't apply mathematical operations on characters (at least not directly). If the characters are of a format that can be understood as numeric, they can be converted. Try: `ch <- c("1.1", "2.1"); sum(ch); sum(as.numeric(ch))`. I'll edit the function so it will attempt the conversion. – AkselA May 24 '17 at 16:11
  • I figured it out a solution too. iris[]<-cbind( mapply(zeropad, iris[, 1], 1), mapply(zeropad, iris[, 2], 2), mapply(zeropad, iris[, 3], 3), mapply(zeropad, iris[, 4], 4) ) – user2946746 May 24 '17 at 16:22