Superimpose text across panels - r

I made a matrix of figures using par(mfrow = (3, 4)). Now I want to overlay each row with texts like this:
What is the simplest way to achieve this?
Thank you!

OPTION 1
Add the text after plotting all figures:
par(list(mfrow = c(3, 4),
mar=c(2,2,1,1)))
lapply(1:12,FUN=function(x) plot(1:100,runif(100),cex=0.2))
##You will have to manually adjust these values to fit your figure
xval = -150
yval = 0.5
y_incr = 1.59
text(x=xval, y=yval, labels="TextToAdd3",col=rgb(0,0,1,0.5), cex=3, xpd=NA)
text(x=xval, y=yval+y_incr, labels="TextToAdd2",col=rgb(0,0,1,0.5), cex=3, xpd=NA)
text(x=xval, y=yval+y_incr*2, labels="TextToAdd1",col=rgb(0,0,1,0.5), cex=3, xpd=NA)
OPTION 2
Centre caption on the left margin every time you plot in the third column. This means less stuffing around with manually adjusting values (plot looks the same as above):
par(list(mfrow = c(3, 4),
mar=c(2,2,1,1)))
texts=list("TextToAdd1",
"TextToAdd3",
"TextToAdd3")
for(i in 1:12){
plot(1:100,runif(100),cex=0.2)
if((i+1)%%4==0){
mtext(text=texts[[i/3]],side=2,line=par()$mar[2], las=1,col=rgb(0,0,1,0.5), cex=3,adj=0.5)
}
}

Related

R multi boxplot in one graph with value (quantile)

How to create multiple boxplot with value shown in R ?
Now I'm using this code
boxplot(Data_frame[ ,2] ~ Data_frame[ ,3], )
I tried to use this
boxplot(Data_frame[ ,2] ~ Data_frame[ ,3], )
text(y=fivenum(Data_frame$x), labels =fivenum(Data_frame$x), x=1.25)
But only first boxplot have value. How to show value in all boxplot in one graph.
Thank you so much!
As far as I understand your question (it is not clear how the fivenum summary should be displayed) here is one solution. It presents the summary using the top axis.
x <- data.frame(
Time = c(1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3),
Value = c(5,10,15,20,30,50,70,80,100,5,7,9,11,15,17,19,17,19,100,200,300,400,500,700,1000,200))
boxplot(x$Value ~ x$Time)
fivenums <- aggregate(x$Value, by=list(Time=x$Time), FUN=fivenum)
labels <- apply(fivenums[,-1], 1, function(x) paste(x[-1], collapse = ", "))
axis(3, at=fivenums[,1],labels=labels, las=1, col.axis="red")
Of course you can additionally play with the font size or rotation for this summary. Moreover you can break the line in one place, so the label will have smaller width.
Edit
In order to get what have you posted in the comment below you can add
text(x = 3 + 0.5, y = fivenums[3,-1], labels=fivenums[3,-1])
and you will get
however it won't be readable for other boxplots.

Clusters labels in dendrogram

I'm wondering - is there any way how to add labels of clusters into dendrogram. See simple example:
hc = hclust(dist(mtcars))
plot(hc, hang = -1)
rect.hclust(hc, k = 3, border = "red")
Desired output should look like this:
Thanks for any suggestions!
You need to get the coordinates of the place to put your clusters' labels:
First axis:
As you are calling rect.hclust, you might as well assign the result so you can use it to find the beginning of clusters (the first one begins at 1 the 2nd at 1 + the length of the first, etc.
rh <- rect.hclust(hc, k = 3, border = "red")
beg_clus <- head(cumsum(c(1, lengths(rh))), -1)
Second axis:
You just want to be above the red rectangle, which is at the middle of the height where you have k-1 clusters and the height where you have k clusters. Let's say you're aiming at 4/5 of the distance instead of 1/2:
y_clus <- weighted.mean(rev(hc$height)[2:3], c(4, 1))
Putting the labels:
text(x=beg_clus, y=y_clus, col="red", labels=LETTERS[1:3], font=2)
An alternative to adding text labels is in the mjcgraphics package that deals with cluster labels. See https://github.com/drmjc/mjcgraphics and https://rdrr.io/github/drmjc/mjcgraphics/man/rect.hclust.labels.html
rect.hclust.labels(hc, k=3, border = 1 ) # adds labels to clusters

avoiding over-crowding of labels in r graphs

I am working on avoid over crowding of the labels in the following plot:
set.seed(123)
position <- c(rep (0,5), rnorm (5,1,0.1), rnorm (10, 3,0.1), rnorm (3, 4, 0.2), 5, rep(7,5), rnorm (3, 8,2), rnorm (10,9,0.5),
rep (0,5), rnorm (5,1,0.1), rnorm (10, 3,0.1), rnorm (3, 4, 0.2), 5, rep(7,5), rnorm (3, 8,2), rnorm (10,9,0.5))
group <- c(rep (1, length (position)/2),rep (2, length (position)/2) )
mylab <- paste ("MR", 1:length (group), sep = "")
barheight <- 0.5
y.start <- c(group-barheight/2)
y.end <- c(group+barheight/2)
mydf <- data.frame (position, group, barheight, y.start, y.end, mylab)
plot(0,type="n",ylim=c(0,3),xlim=c(0,10),axes=F,ylab="",xlab="")
#Create two horizontal lines
require(fields)
yline(1,lwd=4)
yline(2,lwd=4)
#Create text for the lines
text(10,1.1,"Group 1",cex=0.7)
text(10,2.1,"Group 2",cex=0.7)
#Draw vertical bars
lng = length(position)/2
lg1 = lng+1
lg2 = lng*2
segments(mydf$position[1:lng],mydf$y.start[1:lng],y1=mydf$y.end[1:lng])
segments(mydf$position[lg1:lg2],mydf$y.start[lg1:lg2],y1=mydf$y.end[lg1:lg2])
text(mydf$position[1:lng],mydf$y.start[1:lng]+0.65, mydf$mylab[1:lng], srt = 90)
text(mydf$position[lg1:lg2],mydf$y.start[lg1:lg2]+0.65, mydf$mylab[lg1:lg2], srt = 90)
You can see some areas are crowed with the labels - when x value is same or similar. I want just to display only one label (when there is multiple label at same point). For example,
mydf$position[1:5] are all 0,
but corresponding labels mydf$mylab[1:5] -
MR1 MR2 MR3 MR4 MR5
I just want to display the first one "MR1".
Similarly the following points are too close (say the difference of 0.35), they should be considered a single cluster and first label will be displayed. In this way I would be able to get rid of overcrowding of labels. How can I achieve it ?
If you space the labels out and add some extra lines you can label every marker.
clpl <- function(xdata, names, y=1, dy=0.25, add=FALSE){
o = order(xdata)
xdata=xdata[o]
names=names[o]
if(!add)plot(0,type="n",ylim=c(y-1,y+2),xlim=range(xdata),axes=F,ylab="",xlab="")
abline(h=1,lwd=4)
dy=0.25
segments(xdata,y-dy,xdata,y+dy)
tpos = seq(min(xdata),max(xdata),len=length(xdata))
text(tpos,y+2*dy,names,srt=90,adj=0)
segments(xdata,y+dy,tpos,y+2*dy)
}
Then using your data:
clpl(mydf$position[lg1:lg2],mydf$mylab[lg1:lg2])
gives:
You could then think about labelling clusters underneath the main line.
I've not given much thought to doing multiple lines in a plot, but I think with a bit of mucking with my code and the add parameter it should be possible. You could also use colour to show clusters. I'm fairly sure these techniques are present in some of the clustering packages for R...
Obviously with a lot of markers even this is going to get smushed, but with a lot of clusters the same thing is going to happen. Maybe you end up labelling clusters with a this technique?
In general, I agree with #Joran that cluster labelling can't be automated but you've said that labelling a group of lines with the first label in the cluster would be OK, so it is possible to automate some of the process.
Putting the following code after the line lg2 = lng*2 gives the result shown in the image below:
clust <- cutree(hclust(dist(mydf$position[1:lng])),h=0.75)
u <- rep(T,length(unique(clust)))
clust.labels <- sapply(c(1:lng),function (i)
{
if (u[clust[i]])
{
u[clust[i]] <<- F
as.character(mydf$mylab)[i]
}
else
{
""
}
})
segments(mydf$position[1:lng],mydf$y.start[1:lng],y1=mydf$y.end[1:lng])
segments(mydf$position[lg1:lg2],mydf$y.start[lg1:lg2],y1=mydf$y.end[lg1:lg2])
text(mydf$position[1:lng],mydf$y.start[1:lng]+0.65, clust.labels, srt = 90)
text(mydf$position[lg1:lg2],mydf$y.start[lg1:lg2]+0.65, mydf$mylab[lg1:lg2], srt = 90)
(I've only labelled the clusters on the lower line -- the same principle could be applied to the upper line too). The parameter h of cutree() might have to be adjusted case-by-case to give the resolution of labels that you want, but this approach is at least easier than labelling every cluster by hand.

R par(omd) does not contain within mfrow

I would like two plots to show up in two seperate spaces in the plot so I do:
par(mfrow=c(1,2))
plot(1:10,1:10)
Now I would like the second plot to be about 25% shorter than the first plot so I adjust omd:
tmp <- par()$omd
tmp[4] <- 0.75
par(omd=tmp)
plot(1:10,1:10)
The problem is that the second plot shows up ontop of the first plot. How do I avoid this margin issue?
Maybe try using layout instead?
layout(matrix(c(1, 1, 0, 2), ncol = 2L), widths = c(1,1),heights = c(0.5,1))
par(mar = c(3,2,2,2))
plot(1:10,1:10)
par(mar = c(3,2,2,2))
plot(1:10,1:10)
I guess maybe you'd want to set the heights to c(0.2,0.8) to get a 25% reduction?
Edit
But I don't think that omd does what you think it does. It changes the region inside the outer margins, which will always include both plot regions when setting par(mfrow = c(1,2)). What you really want to change, I think is plt, which alters the size of the current plotting region (using quartz, as I'm on a mac):
quartz(width = 5,height = 5)
par(mfrow=c(1,2))
vec <- par("plt")
plot(1:10,1:10)
par(plt = vec * c(1,1,1,0.75))
plot(1:5,1:5)

spplot() - make color.key look nice

I'm afraid I have a spplot() question again.
I want the colors in my spplot() to represent absolute values, not automatic values as spplot does it by default.
I achieve this by making a factor out of the variable I want to draw (using the command cut()). This works very fine, but the color-key doesn't look good at all.
See it yourself:
library(sp)
data(meuse.grid)
gridded(meuse.grid) = ~x+y
meuse.grid$random <- rnorm(nrow(meuse.grid), 7, 2)
meuse.grid$random[meuse.grid$random < 0] <- 0
meuse.grid$random[meuse.grid$random > 10] <- 10
# making a factor out of meuse.grid$ random to have absolute values plotted
meuse.grid$random <- cut(meuse.grid$random, seq(0, 10, 0.1))
spplot(meuse.grid, c("random"), col.regions = rainbow(100, start = 4/6, end = 1))
How can I have the color.key on the right look good - I'd like to have fewer ticks and fewer labels (maybe just one label on each extreme of the color.key)
Thank you in advance!
[edit]
To make clear what I mean with absolute values: Imagine a map where I want to display the sea height. Seaheight = 0 (which is the min-value) should always be displayed blue. Seaheight = 10 (which, just for the sake of the example, is the max-value) should always be displayed red. Even if there is no sea on the regions displayed on the map, this shouldn't change.
I achieve this with the cut() command in my example. So this part works fine.
THIS IS WHAT MY QUESTION IS ABOUT
What I don't like is the color description on the right side. There are 100 ticks and each tick has a label. I want fewer ticks and fewer labels.
The way to go is using the attribute colorkey. For example:
## labels
labelat = c(1, 2, 3, 4, 5)
labeltext = c("one", "two", "three", "four", "five")
## plot
spplot(meuse.grid,
c("random"),
col.regions = rainbow(100, start = 4/6, end = 1),
colorkey = list(
labels=list(
at = labelat,
labels = labeltext
)
)
)
First, it's not at all clear what you are wanting here. There are many ways to make the color.key look "nice" and that is to understand first the data being passed to spplot and what is being asked of it. cut() is providing fully formatted intervals like (2.3, 5.34] which will need to be handled a different way, increasing the margins in the plot, specific formatting and spacing for the labels, etc. etc. This just may not be what you ultimately want.
Perhaps you just want integer values, rounded from the input values?
library(sp)
data(meuse.grid)
gridded(meuse.grid) = ~x+y
meuse.grid$random <- rnorm(nrow(meuse.grid), 7, 2)
Round the values (or trunc(), ceil(), floor() them . . .)
meuse.grid$rclass <- round(meuse.grid$random)
spplot(meuse.grid, c("rclass"), col.regions = rainbow(100, start = 4/6, end = 1))

Resources