36

How would you you make an image from a matrix in R?

Matrix values would correspond to pixel intensity on image (although I am just interested in 0,1 values white or black at the moment.), while column and row numbers correspond to vertical and horizontal location on the image.

By make an image I mean display it on the screen and save it as a jpg.

Sandipan Dey
  • 21,482
  • 2
  • 51
  • 63
Adam SO
  • 9,821
  • 8
  • 28
  • 27
  • the answers here should be useful: http://stackoverflow.com/questions/5554165/visualize-2-variable-joint-probability-mass-function-in-r/5554352#5554352 – Chase Apr 12 '11 at 17:00
  • save it as a PNG rather than JPEG which is no good for crisp "drawings" like this – mdsumner Apr 12 '11 at 22:49

7 Answers7

38

You can display it on the screen easiest using 'image':

m = matrix(runif(100),10,10)
par(mar=c(0, 0, 0, 0))
image(m, useRaster=TRUE, axes=FALSE)

You can also have a look at the raster package...

Tom Wenseleers
  • 7,535
  • 7
  • 63
  • 103
Spacedman
  • 92,590
  • 12
  • 140
  • 224
27

Set up a plot with no margin:

par(mar = rep(0, 4))

Image the matrix with greyscale, like spacedman's answer but completely filling the device:

m = matrix(runif(100),10,10)
image(m, axes = FALSE, col = grey(seq(0, 1, length = 256)))

Wrap that in a call to png() to create the file:

png("simpleIm.png")
par(mar = rep(0, 4))
image(m, axes = FALSE, col = grey(seq(0, 1, length = 256)))
dev.off()

If you need to do this with spatial axes (defaults to [0,1] for X and Y) then use the image.default(x, y, z, ...) form where x and y give the central positions of the pixels in z. x and y can be of length dim(z) + 1 to give corner coordinates for that convention.

Centres of pixels (this is the default for image):

x <- seq(0, 1, length = nrow(m))
y <- seq(0, 1, length = ncol(m))
image(x, y, m, col = grey(seq(0, 1, length = 256)))

Corners of pixels (need 1 extra x and y, and 0 is now the very bottom left corner):

x <- seq(0, 1, length = nrow(m) + 1)
y <- seq(0, 1, length = ncol(m) + 1)
image(x, y, m, col = grey(seq(0, 1, length = 256)))

Note that from R 2.13 image.default gains an argument useRaster which uses the very efficient newish graphics function rasterImage rather than the old image which is effectively multiple calls to rect under the hood to draw every pixel as a polygon.

mdsumner
  • 29,099
  • 6
  • 83
  • 91
  • This worked for me. However, I had to reverse order the rows & transpose the matrix to get the image to display w/ the correct orientation. I don't know if others will have to do the same. – gung - Reinstate Monica Aug 16 '16 at 17:26
13

I do a matrix (where the vertical axis increases going down) one of two ways. Below is the first way using heatmap.2(). It has more control over how the numeric values are formatted in the plot (see the formatC statement below), but is a little harder to deal with when changing the layout.

 library(gplots)

 #Build the matrix data to look like a correlation matrix
 x <- matrix(rnorm(64), nrow=8)
 x <- (x - min(x))/(max(x) - min(x)) #Scale the data to be between 0 and 1
 for (i in 1:8) x[i, i] <- 1.0 #Make the diagonal all 1's

 #Format the data for the plot
 xval <- formatC(x, format="f", digits=2)
 pal <- colorRampPalette(c(rgb(0.96,0.96,1), rgb(0.1,0.1,0.9)), space = "rgb")

 #Plot the matrix
 x_hm <- heatmap.2(x, Rowv=FALSE, Colv=FALSE, dendrogram="none", main="8 X 8 Matrix Using Heatmap.2", xlab="Columns", ylab="Rows", col=pal, tracecol="#303030", trace="none", cellnote=xval, notecol="black", notecex=0.8, keysize = 1.5, margins=c(5, 5))

enter image description here

bill_080
  • 4,692
  • 1
  • 23
  • 30
4

Try levelplot:

library(lattice)
levelplot(matrix)
rockswap
  • 623
  • 1
  • 7
  • 17
4

You can create a heatmap of the matrix.

library(pheatmap)

# Create a 10x10 matrix of random numbers
m = matrix(runif(100), 10, 10)

# Save output to jpeg
jpeg("heatmap.jpg")

pheatmap(m, cluster_row = FALSE, cluster_col = FALSE, color=gray.colors(2,start=1,end=0))

dev.off()

See ?pheatmap for more options.

stepthom
  • 1,432
  • 2
  • 16
  • 27
2

Here's the second way (again, where the vertical axis increases going down). This method is easier to layout, but has less control over the format of the numeric values displayed in the plot.

 library(plotrix)

 #Build the matrix data to look like a correlation matrix
 n <- 8
 x <- matrix(runif(n*n), nrow=n)
 xmin <- 0
 xmax <- 1
 for (i in 1:n) x[i, i] <- 1.0 #Make the diagonal all 1's

 #Generate the palette for the matrix and the legend.  Generate labels for the legend
 palmat <- color.scale(x, c(1, 0.4), c(1, 0.4), c(0.96, 1))
 palleg <- color.gradient(c(1, 0.4), c(1, 0.4), c(0.96, 1), nslices=100)
 lableg <- c(formatC(xmin, format="f", digits=2), formatC(1*(xmax-xmin)/4, format="f", digits=2), formatC(2*(xmax-xmin)/4, format="f", digits=2), formatC(3*(xmax-xmin)/4, format="f", digits=2), formatC(xmax, format="f", digits=2))

 #Set up the plot area and plot the matrix
 par(mar=c(5, 5, 5, 8))
 color2D.matplot(x, cellcolors=palmat, main=paste(n, " X ", n, " Matrix Using Color2D.matplot", sep=""), show.values=2, vcol=rgb(0,0,0), axes=FALSE, vcex=0.7)
 axis(1, at=seq(1, n, 1)-0.5, labels=seq(1, n, 1), tck=-0.01, padj=-1)

 #In the axis() statement below, note that the labels are decreasing.  This is because
 #the above color2D.matplot() statement has "axes=FALSE" and a normal axis()
 #statement was used.
 axis(2, at=seq(1, n, 1)-0.5, labels=seq(n, 1, -1), tck=-0.01, padj=0.7)

 #Plot the legend
 pardat <- par()
 color.legend(pardat$usr[2]+0.5, 0, pardat$usr[2]+1, pardat$usr[2], paste(" ", lableg, sep=""), palleg, align="rb", gradient="y", cex=0.7)

enter image description here

bill_080
  • 4,692
  • 1
  • 23
  • 30
0

With ggplot2:

library(tidyverse)
n <- 12
m <- matrix(rnorm(n*n),n,n)
rownames(m) <- colnames(m) <- 1:n
df <- as.data.frame(m) %>% gather(key='y', value='val')
df$y <- as.integer(df$y)
df$x <- rep(1:n, n)
ggplot(df, aes(x, y, fill= val)) + 
   geom_tile() +
   geom_text(aes(x, y, label=round(val,2))) +
   scale_fill_gradient(low = "white", high = "red") + 
   theme_bw() 

enter image description here

Sandipan Dey
  • 21,482
  • 2
  • 51
  • 63