beroe
beroe

Reputation: 12316

Vary colors of axis labels in R based on another variable

I usually use ggplot2, but in this case I am using the regular image() function to plot a heatmap of a large data set. I can label all the labels as red, but I want to label the y-axis with text of different colors based on a vector of color definitions that I generate:

grid = structure(c(1:12),.Dim = c(4,3))
labs = c("A","B","C")
image(1:4,1:3,grid,axes=FALSE, xlab="", ylab = "")
#This works but isn't the colors I want
axis(2,at=1:length(labs),labels=labs,las=2, adj=1,cex.axis=0.6,col.axis="red")

That generates the following image:

Example plot

I would like labels A and C to be black and B to be red. This is what I tried, but it gives a "wrong length" error...

axiscolors = c("black","red","black")
axis(2,at=1:length(labs),labels=labs,las=2, adj=1, cex.axis=0.6, col.axis=axiscolors)

This is the effect I am after with some "real" data...

actual heatmap

EDIT:

As a back-up, if this is possible in ggplot2, I might be willing to re-factor my code. There are a couple other applications I would use this for as well.

I figured out a way to plot a layer of red symbols over the top of the old labels, but would prefer a native method with the color vector, if possible...

sublabs = c("B")
axis(2,at=match(sublabs,labs),labels=sublabs,las=2, adj=1, cex.axis=0.6, col.axis="red")

Another way would be to use text() if I could put the labels outside the plot space...

text(c(1,1,1),c(1,2,3),labs,col=c("black","red","black"))

UPDATE: See below for a solution that works with ggplot2...

Upvotes: 16

Views: 41984

Answers (4)

thelatemail
thelatemail

Reputation: 93843

If you ignore the vectorised possibilities like text and mtext, you can get there by repeatedly calling axis. The overhead timewise will be very minimal and it will allow all the axis calculations to occur as they normally do. E.g.:

# original code
grid = structure(c(1:12),.Dim = c(4,3))
labs = c("A","B","C")
image(1:4,1:3,grid,axes=FALSE, xlab="", ylab = "")
axiscolors = c("black","red","black")

# new code    
Map(axis, side=2, at=1:3, col.axis=axiscolors, labels=labs, lwd=0, las=1)
axis(2,at=1:3,labels=FALSE)

Resulting in:

enter image description here

Upvotes: 14

beroe
beroe

Reputation: 12316

Accepting @thelatemail's answer as the most flexible, but it also turns out to be pretty simple using text() if you add xpd = TRUE to allow plotting outside the frame. Using mtext() can also work, but it doesn't allow you to rotate labels.

grid = structure(c(1:20),.Dim = c(4,5))
labs = c("A","B","C","D","E")
redlabs = c("B","D")
colorlist = c("black","red")
# one of many ways to generate the color labels
axiscolor = colorlist[labs %in% redlabs +1 ]

image(1:4,1:5,grid,axes=FALSE, xlab="", ylab = "")
axis(2,at=1:length(labs),labels=FALSE)

# This would work for sideways labels
# mtext(text=labs, side=2,at=1:length(labs),col=axiscolor,adj=.5)
text(labels=labs, col=axiscolor, x=rep(.45,length(labs)), y=1:length(labs), srt = 0, pos = 2, xpd = TRUE)

Solution using text()

UPDATE for ggplot2: You can use theme() and element_text to set the colors and other parameters. Something like this...

 p + theme(axis.text.y = element_text(color=axiscolor)) 

Upvotes: 5

Valeri Voev
Valeri Voev

Reputation: 2242

I like very much thelatemail's approach, and can only add a small refinement since the fixed "at" positions (as in the example above with at = 1:3) didn't work well for me. In my case, I needed to generate a barplot and provided my own values for space and width parameters. In the end what I used looks like (example with random data in which I wanted the bars and labels for positive (non-negative, to be more precise) data values to be green and red otherwise. For this example I also use the letters function to provide labels and rotate the labels using las = 2):

x <- rnorm(26)
color <- rep("green", length(x))
color[x < 0] <- "red"

par(mar=c(6,4.1,4.1,2.1))
barplot(x, las = 2, ylim = c(min(x)-0.5, max(x)+0.5), col = color, space = 0.5, width = 2)    
Map(function(x,y,z) 
axis(1,at=x,col.axis=y,labels=z,lwd=0,las=2),
seq(from = 2, by = 3, length.out = length(x)),
color,
letters
)
axis(1,at=seq(from = 2, by = 3, length.out = length(x)),labels=FALSE)

Upvotes: 3

Esti
Esti

Reputation: 31

You could specify a vector with the colors you want to apply to the labels and then use a loop to get the labels colored with axis(). In the following example, I use a different color for each level of a dot chart.

DF <- data.frame(habitat=c("Hab 1","Hab 2","Hab 3","Hab 4","Hab 5"), mean=c(0.53,0.28,0.30,0.35,0.39), color=colors()[c(24,257,26,504,652)])
> DF
habitat mean      color
1   Hab 1 0.53      black
2   Hab 2 0.28     green3
3   Hab 3 0.30       blue
4   Hab 4 0.35 orangered1
5   Hab 5 0.39     yellow

par(mar=c(7, 5, 4, 3))
dotchart(DF[,2], xlim=c(0.2,0.6), col=as.character(DF$color), pch=16, lcolor="white", xlab=colnames(DF[2])) # Plot the points 
for (j in 1:5){
axis(side=2, at=j, col.axis=as.character(DF$color)[j], labels=DF$habitat[j], las=1) # Add habitat as labels, each with corresponding color, on the left margin
} 

Upvotes: 3

Related Questions