R: gplots, barplots: how to fix bar width independent of paper setting? - r

I use the gplots package to output barplots. I use it inside a for-loop, so rest of the code is omitted to make it more clear:
library("gplots")
pdf(file = "/Users/Tim/desktop/pgax.pdf", onefile = TRUE, paper = "special")
par(mfrow = c(4,2)) #figures arranged in 2 rows and 2 columns
par(las=2) #perpendicular labels on x-axis
barplot2(expression,ylab = expression(expression),main = graph.header, cex.names =0.85, beside = TRUE, offset = 0, xpd = FALSE,axis.lty = 0, cex.axis = 0.85, plot.ci = TRUE,ci.l = expression - sd.value, ci.u = expression + sd.value, col = colors,width = 1,names.arg = c(etc))
Now when I specify the papersize at a4, and print out in two columns the bars are made so they fill up the full space assigned. If I only have a few bars in each graws, the width is too big compared to the height. I know I should be using xlimit and width = amongst and perhaps even the aspect ratio?, but I can't get the results I wanted. And unconvenient way is to specify the height and width output of the paper, and manually I adjust it for the number of bars in the plots each time. But this doesnt seem appropiate. Does someone know a convenient way to fix width bars in my plots?
All help is much appreciated!

Although bar plots with wide bars can look silly, a wide bar plot with a few narrow bars in it will likely look even sillier. That leaves you specifying the width of the plot (via the width argument to pdf).
It may be prettiest to keep all your plots the same size, in which case you just give width a fixed value. If you do want narrower plots when there are less bars, you need a line of code like
plot_width <- 3 + 0.5 * nlevels(x_variable)

Related

how to create discrete legend in pheatmap

I want to create a discrete legend (not continuous) in pheatmap. So for this code,
m=as.matrix(c(1:100))
breaks=c(1,5,10,50,80,100)
color=c("red","blue","green","yellow","orange")
pheatmap(m,cluster_rows=FALSE, cluster_cols=FALSE,breaks=breaks,color=color)
the legend looks like this:
But I want it to look like this where the size of each rectangle is the same:
Can you point me to the options in pheatmap that will make this possible? I cannot figure it out. Thank you muchly,
Well, the function itself really does not want to accommodate such a legend. There is no way to pass in any combination of arguments to make it discrete as far as I can tell and all the plotting functions it relies on seem to be locked so you can't really adjust their behavior.
But, the good news is that the function uses grid graphics to make the output. We can hack at the grid objects left on the grid tree to remove the legend they drew and draw our own. I've created a function to do this.
changeLegend<-function(breaks, color) {
tree <- grid.ls(viewport=T, print=F)
#find legend
legendvp <- tail( grep("GRID.VP", tree$name), 1)
#get rid of grobs in this viewport
drop <- tree$name[grepl(tree$vpPath[legendvp],tree$vpPath) &
grepl("grob",tree$type)]
sapply(drop, grid.remove)
#calculate size/position of labels
legend_pos = seq(0,to=1,length.out=length(breaks))
brat = seq(0,to=1,length.out=length(breaks))
h = 1/(length(breaks)-1)
#render legend
seekViewport(tree$name[legendvp])
grid.rect(x = 0, y = brat[-length(brat)],
width = unit(10, "bigpts"), height = h, hjust = 0,
vjust = 0, gp = gpar(fill = color, col = "#FFFFFF00"))
grid.text(breaks, x = unit(12, "bigpts"),
y = legend_pos, hjust = 0,)
}
Since they didn't really name any of their viewports, I had to make some guesses about which viewport contained which objects. I'm assuming the legend will always be the last viewport and that it will contain two globs, one for the box of color and one for the text in the legend. I remove those items, and then re-draw a new legend using the breaks and colors passed in. Here's how you would use that function with your sample
library(pheatmap)
library(grid)
mm <- as.matrix(c(1:100))
breaks <- c(1,5,10,50,80,100)
colors <- c("red","blue","green","yellow","orange")
pp<-pheatmap(mm,cluster_rows=FALSE, cluster_cols=FALSE,
breaks=breaks, color=colors, legend=T)
changeLegend(breaks, colors)
And that produces
Because we are hacking at undocumented grid objects, this might not be the most robust method, but it shows off how flexible grid graphics are

R Barplot with one bar - how to plot correctly

I want to plot a regular bar plot in R, but with just one bar. What I don't like is the fact that the bar width gets to be the width of the whole plot. I want the bar to be "thinner", but I don't know how to do it.
Default command is:
barplot(percentage, col=c("brown4"))
where percentage is a fraction. I tried using xlim parameter, but it gets very messy (bar goes completely to the right or left). For example, I tried:
barplot(percentage, col=c("brown4"), xlim=c(0.5,1))
but this stretches the bar even more. I am an R noob.
You may try to play around with the width of the device you plotting to. E.g.:
# plot to a Windows graphic device
windows(height = 10, width = 4)
barplot(0.5)
# plot to PDF
pdf(height = 10, width = 2)
barplot(0.5)
dev.off()
You may also try width together with xlim
barplot(0.5, width = 0.1, xlim = c(0, 1))
The width argument to barplot says:
width optional vector of bar widths. Re-cycled to length the number of bars drawn. Specifying a single value will have no visible effect unless xlim is specified.
So specify both width and xlim. The interaction of these two is not obvious (to me) so you will probably need to play around with them until they look like you want them to.
percentage <- 0.25
barplot(percentage, width=0.2, xlim=c(0,1.2), col="brown4")

Shrinking plot area using plot() in R

I would like to shrink the width of my plot. In other words, I am looking to reduce the space between ticks on the x-axis, so that the points on the graph get closer to each other. I want the line to look more "compressed" on the x-axis. Is there a way of doing that?
Here is my current plot code:
plot(ret.bull, type = "l", xlab = "Time", ylab = "Return", xaxt = "n")
abline(a = mean(ret.bull), b = 0, lty = 2)
I want to get something in those lines:
Thank you,
You can change the aspect ratio by setting the asp argument to the plot function. You can also open your graphics device at whatever dimensions you want (so a smaller width) using something like:
dev.new( width=4, height=6 )
Or you can just make one of the margins larger to reduce the space for the plot:
par(mar=c(5,4,2,10)+0.1)
Also the squishplot function in the TeachingDemos package is another way to change the aspect ratio (and put the empty space in the margins rather than leaving a large amount of white space inside the plotting region).
As Roland says, you just need to set your xlim. Plotting a single vector vertically, the default xlim is c(1, length(ret.bull). You probably want something like
ret.bull <- rnorm(100) ## for reproducibility
plot(ret.bull, type = "l", xlim = c(-length(ret.bull), 2*length(ret.bull)))

How to reduce the height of the heatmap itself while increasing the height of the horizontal side bar?

In the heatmap.2 function in the gplots package, how can I reduce the height of the rows in the heatmap itself while increasing the height of the horizontal side bar (denoted by ColSideColors argument)?
library('gplots')
prob_matrix=replicate(100, rnorm(20))
ids=paste("patient",c(1:100),sep="_")
type_colors=sample(c("red","blue","cyan","pink","yellow","green"), length(ids), replace = TRUE, prob = NULL)
heatmap.2(prob_matrix, ColSideColors=type_colors,RowV=NULL,trace="none")
I would like the horizontal side bar generated by ColSideColors=subtype_colors to be increased in height, and the rows of the heatmap reduced in height.
For extension, how can I also edit the width of each column independent of the size of the width of the pdf or graphics device to which I output the plot? So, in the above, I would want to make the width of the columns of the prob_matrix heatmap greater.
I think its hard coded into the function. Roundabout line 196 of the heatmap.2 function there is:
if (!missing(ColSideColors)) {
if (!is.character(ColSideColors) || length(ColSideColors) !=
nc)
stop("'ColSideColors' must be a character vector of length ncol(x)")
lmat <- rbind(lmat[1, ] + 1, c(NA, 1), lmat[2, ] +
1)
lhei <- c(lhei[1], 0.2, lhei[2])
}
And that 0.2 is the height assigned to the colour bars. If you make a copy of heatmap.2 and edit it you can set that to something else. Create a new argument, ColSideHeight maybe, with default 0.2, and change that 0.2 to ColSideHeight.

How can I make my vertical labels fit within my plotting window?

I'm creating a histogram in R which displays the frequency of several events in a vector. Each event is represented by an integer in the range [1, 9]. I'm displaying the label for each count vertically below the chart. Here's the code:
hist(vector, axes = FALSE, breaks = chartBreaks)
axis(1, at = tickMarks, labels = eventTypes, las = 2, tick = FALSE)
Unfortunately, the labels are too long, so they are cut off by the bottom of the window. How can I make them visible? Am I even using the right chart?
Look at help(par), in particular fields mar (for the margin) and oma (for outer margin).
It may be as simple as
par(mar=c(5,3,1,1)) # extra large bottom margin
hist(vector, axes = FALSE, breaks = chartBreaks)
axis(1, at = tickMarks, labels = eventTypes, las = 2, tick = FALSE)
This doesn't sound like a job for a histogram - the event is not a continuous variable. A barplot or dotplot may be more suitable.
Some dummy data
set.seed(123)
vec <- sample(1:9, 100, replace = TRUE)
vec <- factor(vec, labels = paste("My long event name", 1:9))
A barplot is produced via the barplot() function - we provide it the counts of each event using the table() function for convenience. Here we need to rotate labels using las = 2 and create some extra space of the labels in the margin
## lots of extra space in the margin for side 1
op <- par(mar = c(10,4,4,2) + 0.1)
barplot(table(vec), las = 2)
par(op) ## reset
A dotplot is produced via function dotchart() and has the added convenience of sorting out the plot margins for us
dotchart(table(vec))
The dotplot has the advantage over the barplot of using much less ink to display the same information and focuses on the differences in counts across groups rather than the magnitudes of the counts.
Note how I've set the data up as a factor. This allows us to store the event labels as the labels for the factor - thus automating the labelling of the axes in the plots. It also is a natural way of storing data like I understand you to have.
Perhaps adding \n into your labels so they will wrap onto 2 lines? It's not optimal, but it may work.
You might want to look at this post from Cross Validated

Resources