I would like to use R to make a barplot of ~100,000 numerical entries. The plot will be dense, which is what I want. So far I am using the following code:
sample_var <- c(2,5,3,2,3,2,6,10,20,...) #Filled with 100,000 entries
barplot(sample_var)
The resulting plot is just what I want, but it is a square, whereas I want a long rectangle. Is there a way to set the dimensions of the barplot? I would like to specific an aspect ratio of 10:1 for length x height, or a specific pixel setting of 1000px x 10px. I tried using xlim in the barplot function statement, but get an "invalid xlim" warning.
Any help is appreciated!
Set the width and hight when outputting to a file:
png(filename="figures.png", width=800, height=200, bg="white")
sample_var <- c(2,5,3,2,3,2,6,10,20)
barplot(sample_var)
dev.off()
Related
I am generating a heatmap in R with lots of rows.
TL;DR how do I get the real size of a plot in R?
df=data.frame(one=1:100,two=101:200,three=201:300)
names=1:100
names=paste0("Cell",names)
rownames(df)=(names)
pheatmap(df,scale="row")
default image fits in window, but we can't read row names.
pheatmap(df,scale="row",cellheight = 10)
changing the cell height lets us read row names but now the image doesn't fit in the window!
In this example i am using pheatmap, but also run into this stuff with other plot generating packages.
While I've grown to expect frustrating behavior like this from R and by trial and error could make an appropriately size image for the plot, this seems like something that I should be able to get from the program?
Is there a way to get the dimensions of the plot automatically so that I can create correctly size PDF or PNG for it?
The function pheatmap uses grid graphics to draw its plots, and specifies the size of its elements in "bigpts" where 72 "bigpts" == 1 inch. If you have lots of rows and specify a reasonable row height, this will exceed the plotting window.
Because it is specified as a gtree, we can actually access the height and width of the components and use them to set the dimensions of our png or pdf.
This function will harvest the total height and width in inches of a plot, returning them in a named list:
get_plot_dims <- function(heat_map)
{
plot_height <- sum(sapply(heat_map$gtable$heights, grid::convertHeight, "in"))
plot_width <- sum(sapply(heat_map$gtable$widths, grid::convertWidth, "in"))
return(list(height = plot_height, width = plot_width))
}
We can use this to specify the dimensions of our plotting device:
my_plot <- pheatmap(df,scale="row", cellheight = 10)
plot_dims <- get_plot_dims(my_plot)
png("plot.png", height = plot_dims$height, width = plot_dims$width, units = "in", res = 72)
my_plot
dev.off()
Which gives the desired plot
Note that this is not a general solution for R plots, but specific to pheatmap objects.
While par() has been very useful in defining the number of plots in a single page, I was wondering if there would be a nice and quick trick to set the proper margins for multiplot pages. For instance,
dev.off()
par(mfrow =c(3,3),mar=c(0,0,0,0),pty="s")
x <- c(1:10)
y <- c(1:10)
for (i in 1:9){
plot(x,y)
}
In this rough example, the x and y labels don't appear and the distance between plots is very close.
In the end, it becomes a rule of thumb trying to fix with mar=c() that all elements of the plot are visually set in one page. So, is there any quicker way to determine the margins depending on the size of the labels, axes, and number of plots?
Thanks!
When I try to align an image plot with a xy plot by their x-axis there is a small misalignment between the x-values. Can't figure out how to get rid of it!
Notice the misalignment of the vertical lines from the bottom plot with the x-axis on the top plot.
par(mfrow=c(2,1))
par(mar=c(0,5,5,5))
image(x=1:100,z=replicate(10, rnorm(100)))
par(mar=c(5,5,0,5))
par(xaxs="i")
plot(1:100,rnorm(100))
abline(v=1:100)
Gurus, help!! Thank you!!
PS: Could not figure it out with the post "plot-time-series-and-image-in-r-so-that-x-axis-labels-line-up-perfectly", still having issues!
The problem is that image draws data as a grid of cells which have a given width. If the length of x in image equals to nrow(z) then it specifies the midpoints of the cells. In your example this gives you rectangles centered around 1:100, effectively resulting in the x-axis covering the range from 0.5 to 100.5, which gives the observed misalignment.
In order to match the ranges in both plots you need to specify xlim to plot accordingly. In the following example I use n = 10 to make things more obvious.
par(mfrow=c(2,1))
par(mar=c(0,5,5,5))
image(x=1:n,z=replicate(10, rnorm(n)))
par(mar=c(5,5,0,5))
par(xaxs="i")
plot(1:n,rnorm(n), xlim=c(.5,n+.5))
abline(v=1:n)
Okay, so this is one of those things I had probably figured out on the past and then completely forgot about.
The trick is not only calling par(xaxs="i") but also enforcing both xlims!
par(mfrow=c(2,1))
par(mar=c(0,5,5,5))
image(x=1:100,z=replicate(10, rnorm(100)),xlim=c(0,100))
par(mar=c(5,5,0,5))
par(xaxs="i")
plot(1:100,rnorm(100),xlim=c(0,100))
abline(v=1:100)
I am trying to plot a variable over time using ggplot2.
My current plot looks like this:
However, I want the scaled values with significant numbers shown on the axis. The scale needs to be shown at the top left corner. Something like:-
I don't want to scale the plot. The axis needs to show the significant numbers and the exponential scale needs to be shown at the top left.
Is this what you had in mind (my own MWE):
library(ggplot2)
df <- data.frame( x=rnorm(10), y=seq(1e8,10e8,by=1e8))
p <- ggplot(df)+
geom_point(aes(x=x,y=y/1e8))+
geom_text(aes(x=-Inf,y=Inf,label=as.character(paste("10^8"))), parse=T, hjust=1, vjust=0.8, size=5/14*10)+
scale_y_continuous(name="y")
gt <- ggplot_gtable(ggplot_build(p))
gt$layout$clip[gt$layout$name == "panel"] <- "off"
grid.draw(gt)
Basically divide by the exponent, and then write it manually at the top left. The parse=T renders the power as an actual power. That factor of 5/14 was mentioned on another post as being the rough factor by which geom_text size relates to the size you set text to be elsewhere on the graph (no idea why but I've found doing 5/14*text-size-you-want gives a decent looking geom_text). Finally the last three lines prevent the off-plot-panel text being cropped out (you wouldn't see the geom_text otherwise). Hope this gives you something to work with.
I am adding text to different panels of a xyplot in lattice and was wondering if anyone knows a way to not specify a x and y coordinates or is there something similar to legend where you can say upper left or upper right,etc?
I ask because I want to use scales=free in the plotting code, but when I do the text in the mytext code ends up covering up parts of the graph and doesn't make for a good plot. I would like to have a way to plot the graphs without making individual plots because in my real dataset I have up to 10 grouping factor levels (sams in the code). The example provided is not as extreme as the real data.
Example data
d_exp<-data.frame(sams=c(rep("A",6),rep("B",6),rep("C",6)),
gear=c(rep(1:2,9)),
fraction=c(.12,.61,.23,.05,.13,.45,0.3,.5,.45,.20,.35,.10,.8,.60,.10,.01,.23,.03),
interval=c(rep(c(0,10,20),6)))
d_exp<-d_exp[order(d_exp$sams,d_exp$gear,d_exp$interval),]
Plot with scales=same. mytext x and y coordinates are specified.
mytext<-c("N=3","N=35","N=6")
panel.my <- function(...) {
panel.superpose(col=c("red","blue"),lwd=1.5,...)
panel.text(x=2.5,y=0.5,labels=mytext[panel.number()],cex=.8)
}
xyplot(fraction~interval | sams, data=d_exp,groups=gear,type="l",
scales=list(relation="same",y=list(alternating=1,cex=0.8),x=list(alternating=1,cex=.8,abbreviate=F)),
strip = strip.custom(bg="white", strip.levels = T),drop.unused.levels=T,as.table=T,
par.strip.text=list(cex=0.8),panel=panel.my)
Same thing with scales=free. Text is in odd places because all text has the same coordinates.
xyplot(fraction~interval | sams, data=d_exp,groups=gear,type="l",
scales=list(relation="free",y=list(alternating=1,cex=0.8),x=list(alternating=1,cex=.8,abbreviate=F)),
strip = strip.custom(bg="white", strip.levels = T),drop.unused.levels=T,as.table=T,
par.strip.text=list(cex=0.8),panel=panel.my)
Thanks for any help.
You can use grid.text() to specify units in a range-independent way. For example
library(grid)
panel.my <- function(...) {
panel.superpose(col=c("red","blue"),lwd=1.5,...)
grid.text(x=.5,y=.8,label=mytext[panel.number()])
}
With grid.text the x and y values use npc units by default which range from 0 to 1. So x=.5 means centered and y=.8 means 80% of the way to the top.