I have this plot which I want to save as PDF.
pdf(file="pie_charts.pdf", width=8, height=5, onefile=F)
layout(matrix(c(1,2,3,3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(mar=c(0,0,0,0), xpd=TRUE)
pie(c(1,9),col=c("black","white"))
pie(c(1,3),col=c("black","white"))
plot.new()
legend(x="center", ncol=2,legend=c("Black","Whtie"),fill=c("black","white"), bty = "n",cex=1.3)
dev.off()
And this is what I am getting
It looks quite good but I want to eliminate as much empty space as possible between the individual pie charts as well as between them and the legend. Any suggestions?
With layout(), I think you may be a bit limited with changing margins to squeeze the pie charts together.
This isn't an elegant solution but it works. I went in to the pie() function and modified the xlim arguments. This was my only change.
In other words, pie has this inside its function:
xlim <- ylim <- c(-1, 1)
Change the xlim to shift the pie charts left or right.
I made mypieleft() and mypieright().
mypieleft<-function(blah blah){
[untouched code from pie]
# xlim <- ylim <- c(-1, 1)
xlim <- c(-1.20, 0.80)
ylim <- c(-1, 1)
[untouched code from pie]
}
and
mypieright<-function(blah blah){
[untouched code from pie]
# xlim <- ylim <- c(-1, 1)
xlim <- c(-0.75, 1.25)
ylim <- c(-1, 1)
[untouched code from pie]
}
Then change your code slightly:
layout(matrix(c(1,2,3,3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(oma=c(0,0,0,0), xpd=TRUE)
mypieleft(c(1,9),col=c("black","white"))
mypieright(c(1,3),col=c("black","white"))
plot.new()
legend(x="center", ncol=2,legend=c("Black","Whtie"),fill=c("black","white"), bty = "n",cex=1.3)
I get this image.
Just increase the radius of the pies:
layout(matrix(c(1, 2, 3, 3), ncol=2, byrow=TRUE), heights=c(4, 1))
par(mar=c(0, 1, 0, 0)) # increase left margin to accommodate text
pie(c(1, 9), col=c("black","white"), radius=1)
par(mar=c(0, 0, 0, 1)) # increase right margin to accommodate text
pie(c(1, 3), col=c("black", "white"), radius=1)
plot.new()
legend(x="center", ncol=2, legend=c("Black", "White"),
fill=c("black", "white"), bty="n", cex=1.3)
See the radius arg at ?pie.
Related
I want to produce two figures using base R, both showing barplots. The first figure should contain two bar plots and the second figure should contain four bar plots.
I used par(mfrow = c(...)) to arrange multiple bar plots in one figure.
I don't have trouble to produce the figures themselves, but when I save the figures bar widths and tick labels are different in size.
To my understanding, when I produce the second figure with four bar plots and chose double the width of the first figure when exporting, bars and labels should be displayed with the same size in the file. However, the labels are much smaller and the bars have a different width in the second figure. Can anyone tell me why?
Here a simple example:
png(filename="plot1.png", width=200, height=300, bg="white")
par(mfrow = c(1, 2), mar = c(1, 2, 1, 1), oma = c(0, 0, 0, 0))
barplot(height = c(2,3), width = 1, xlim = c(0,2))
barplot(height = c(2,3), width = 1, xlim = c(0,2))
dev.off()
png(filename="plot2.png", width=400, height=300, bg="white")
par(mfrow = c(1, 4), mar = c(1, 2, 1, 1), oma = c(0, 0, 0, 0))
barplot(height = c(2,3), width = 1, xlim = c(0,2))
barplot(height = c(2,3), width = 1, xlim = c(0,2))
barplot(height = c(2,3), width = 1, xlim = c(0,2))
barplot(height = c(2,3), width = 1, xlim = c(0,2))
dev.off()
Plot 1:
Plot 2:
Probably par and pdf(width, height) should be equal.
png(filename="plot1.png", width=400, height=300, bg="white")
par(mfrow=c(1, 4), mar=c(1, 2, 1, 1), oma=c(0, 0, 0, 0))
replicate(2, barplot(height=c(2,3), width=1, xlim=c(0,2)))
dev.off()
png(filename="plot2.png", width=400, height=300, bg="white")
par(mfrow=c(1, 4), mar=c(1, 2, 1, 1), oma=c(0, 0, 0, 0))
replicate(4, barplot(height=c(2,3), width=1, xlim=c(0,2)))
dev.off()
Another solution is to use layout.
def.par <- par(no.readonly=TRUE) # save par default, for resetting...
# 1 x 2 plot
layout(matrix(c(1:2, 0, 0), nrow=1, ncol=4, byrow=TRUE))
layout.show(n=2) # to inspect layout # MARK
replicate(2, barplot(height=c(2,3), width=1, xlim=c(0,2)))
# 1 x 4 plot
layout(matrix(c(1:4), nrow=1, ncol=4, byrow=TRUE))
layout.show(n=4) # to inspect layout
replicate(4, barplot(height=c(2,3), width=1, xlim=c(0,2)))
# 2 x 4 plot
layout(matrix(c(1:2, 0, 0, 3:6), nrow=2, ncol=4, byrow=TRUE))
layout.show(n=6) # to inspect layout
replicate(2, barplot(height=c(2,3), width=1, xlim=c(0,2)))
replicate(4, barplot(height=c(2,3), width=1, xlim=c(0,2)))
par(def.par) # reset to default
However, both solutions bring a half empty plot 1, the reason can be seen in code above at # MARK.
We could use the magick package to "chop" the first plot to the desired content. First we create the *.pngs with the second method.
clr <- "#ED7C22" # color
png(filename="plot1.png", width=400, height=300, bg="white")
layout(matrix(c(1:2, 0, 0), nrow=1, ncol=4, byrow=TRUE))
replicate(2, barplot(height=c(2,3), width=1, xlim=c(0,2), col=clr, border=0))
dev.off()
png(filename="plot2.png", width=400, height=300, bg="white")
layout(matrix(c(1:4), nrow=1, ncol=4, byrow=TRUE))
replicate(4, barplot(height=c(2,3), width=1, xlim=c(0,2), col=clr, border=0))
dev.off()
Now, using image_chop we trim plot1.png to it's left half.
library(magick)
(i <- image_read("plot1.png"))
i.chopped <- image_chop(i, "200x+200") # says: trim by 200px at pos. 200
Finally, we export the chopped image.
image_write(i.chopped, path="plot1.ch.png", format="png")
Plot 1 ("chopped")
Plot 2
So i have this numeric variables which reflect percentages
data1.pct<-19
data2.pct<-5
data3.pct<-76
class1.pct<-35
class2.pct<-18
class3.pct<-47
Now i am using this code to generate barplot
CairoPDF(paste('data1/', data, '_plot1.pdf', sep=''), family='sans', pointsize=9, width=6, height=3.25)
par(mar=(c(4, 4, 1, 13) + 0.1), mgp=c(3, 2, 0), xpd=TRUE)
barplot(cbind(
c(data1.pct, data2.pct, data3.pct),
c(class1.pct, class2.pct, class3.pct)), col=c("firebrick3", "dodgerblue3", "mistyrose1"), ylim=c(0,100), space=c(0,1)
)
legend("topright", inset=c(-0.55, 0), legend=c("not attend", "refused", "attend"), col=c("mistyrose1", "dodgerblue3", "firebrick3"), lty=1, lwd=2, bty='n')
dev.off()
and the result is
I would like to add corresponding percentages inside barplot, that is numbers/percentages in my variables. So My output should be:
I would like to use barplot funcion to do this and NOT ggplot2
I have tried adding percentages with
text(mydata, 0, round(data1.pct), 1),cex=1,pos=3) but this is not right.
To get the y-values for the text, you can use cumsum along with tail and head to get the midpoints of each bar section.
par(mar=(c(4, 4, 1, 13) + 0.1), mgp=c(3, 2, 0), xpd=TRUE)
## Make the matrix for barplot
mat <- cbind(c(data1.pct, data2.pct, data3.pct), c(class1.pct, class2.pct, class3.pct))
## Get the y-values for text
ys <- apply(mat, 2, function(x) c(x[1]/2, head(cumsum(x),-1) + tail(x,-1)/2))
## Make barplot, store x data
xs <- barplot(mat, col=c("firebrick3", "dodgerblue3", "mistyrose1"), ylim=c(0,100), space=c(0,1))
## Add text
text(rep(xs, each=nrow(ys)), c(ys), labels=c(mat))
legend("topright", inset=c(-0.55, 0), legend=c("not attend", "refused", "attend"), col=c("mistyrose1", "dodgerblue3", "firebrick3"), lty=1, lwd=2, bty='n')
I would like to control the height of the colorbar to the extent of the plot window.
For example, if I use:
if (dev.cur() == 1) x11(width=10,height=9)
par(mfrow=c(2,1))
How can I increase the height of the colorbar to be same height as the plots window? e.g.
plot(r, legend.only=TRUE, legend.width=1.4, legend.shrink=1,
col=colorRampPalette(c("darkred", "red3", "orange2", "orange",
"yellow", "lightskyblue","steelblue3",
"royalblue3", "darkblue"))(12),
breaks=brks, axis.args=list(at=seq(80, 205, by=10),
labels=seq(80, 205, by=10), cex.axis=0.9),
legend.args=list(text='Precipitation (mm)', side=4, font=2,
line=2.3, cex=0.8))
Many thanks
AZ.
If I understand correctly, you want to plot a full-height legend beside two plots whose layout is defined by par(mfrow=c(2, 1).
One way to achieve this is to generate the two plots, then set par(new=FALSE) and plot the raster again with legend.only=TRUE.
library(raster)
r <- raster(matrix(runif(100), ncol=10))
# Set layout; ensure appropriate space at right for legend
par(mfrow=c(2, 1), mar=c(2, 3, 1, 3))
# Plot raster
plot(r, legend=FALSE, zlim=c(0, 1), las=1)
# Plot second object
plot(runif(10), runif(10), pch=21, cex=2, las=1,
bg=rev(terrain.colors(10)[sample(10, 10, replace=T)]))
# Revert to c(1, 1) layout and adjust legend margins
par(mfrow=c(1, 1), mar=c(2, 0, 1, 0), new=FALSE)
# Plot legend
plot(r, legend.only=TRUE, legend.shrink=1, legend.width=2, zlim=c(0, 1),
axis.args=list(at=pretty(0:1), labels=pretty(0:1)),
legend.args=list(text='Whatever', side=4, font=2, line=2.3))
If you are plotting multiple raster objects with matching extents and resolution, you might consider rasterVis::levelplot, which has a RasterStack method:
library(rasterVis)
s <- stack(replicate(2, raster(matrix(runif(100), nc=10))))
levelplot(s, layout=c(1, 2), names.attr=c('One', 'Two'),
at=seq(0, 1, length.out=100),
par.strip.text=list(font=2, cex=1.2))
# Plotting titles for vertical colorkeys is a little fiddly...
grid::grid.text('Precipitation (mm)', rot=90, y=unit(0.5, "npc"),
x=unit(0.95, "npc"))
You can suppress panel labels with par.strip.text=list(cex=0), and specify a colour ramp with col.regions:
levelplot(s, layout=c(1, 2),
col.regions=colorRampPalette(c('darkred', 'red3', 'orange2', 'orange',
'yellow', 'lightskyblue', 'steelblue3',
'royalblue3', 'darkblue')),
at=seq(0, 1, length.out=100), par.strip.text=list(cex=0),
scales=list(alternating=FALSE))
# Also demonstrating how to adjust fontface and size for legend title
grid::grid.text('Precipitation (mm)', y=unit(0.5, "npc"),
rot=90, x=unit(0.95, "npc"), gp=gpar(fontsize=14, font=2))
Earlier I asked about creating a gradient of n values in base graphics (LINK). Now I'd like to create a gradient legend that goes with it. My ideal would be something like ggplot2's gradient legends:
Here's some code similar to what I'm working with:
colfunc <- colorRampPalette(c("red", "blue"))
plot(1:20, 1:20, pch = 19, cex=2, col = colfunc(20))
Here is an example of how to build a legend from first principles using rasterImage from grDevices and layout to split the screen
layout(matrix(1:2,ncol=2), width = c(2,1),height = c(1,1))
plot(1:20, 1:20, pch = 19, cex=2, col = colfunc(20))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
plot(c(0,2),c(0,1),type = 'n', axes = F,xlab = '', ylab = '', main = 'legend title')
text(x=1.5, y = seq(0,1,l=5), labels = seq(0,1,l=5))
rasterImage(legend_image, 0, 0, 1,1)
Late to the party, but here is a base version presenting a legend using discrete cutoffs. Thought it might be useful for future searchers.
layout(matrix(1:2,nrow=1),widths=c(0.8,0.2))
colfunc <- colorRampPalette(c("white","black"))
par(mar=c(5.1,4.1,4.1,2.1))
plot(1:10,ann=FALSE,type="n")
grid()
points(1:10,col=colfunc(10),pch=19,cex=1.5)
xl <- 1
yb <- 1
xr <- 1.5
yt <- 2
par(mar=c(5.1,0.5,4.1,0.5))
plot(NA,type="n",ann=FALSE,xlim=c(1,2),ylim=c(1,2),xaxt="n",yaxt="n",bty="n")
rect(
xl,
head(seq(yb,yt,(yt-yb)/10),-1),
xr,
tail(seq(yb,yt,(yt-yb)/10),-1),
col=colfunc(10)
)
mtext(1:10,side=2,at=tail(seq(yb,yt,(yt-yb)/10),-1)-0.05,las=2,cex=0.7)
And an example image:
The following creates a gradient color bar with three pinpoints without any plot beforehand and no alien package is needed. Hope it is useful:
plot.new()
lgd_ = rep(NA, 11)
lgd_[c(1,6,11)] = c(1,6,11)
legend(x = 0.5, y = 0.5,
legend = lgd_,
fill = colorRampPalette(colors = c('black','red3','grey96'))(11),
border = NA,
y.intersp = 0.5,
cex = 2, text.font = 2)
As a refinement of #mnel's great answer, inspired from another great answer of #Josh O'Brien, here comes a way to display the gradient legend inside the plot.
colfunc <- colorRampPalette(c("red", "blue"))
legend_image <- as.raster(matrix(colfunc(20), ncol=1))
## layer 1, base plot
plot(1:20, 1:20, pch=19, cex=2, col=colfunc(20), main='
Awesome gradient legend inside')
## layer 2, legend inside
op <- par( ## set and store par
fig=c(grconvertX(c(0, 10), from="user", to="ndc"), ## set figure region
grconvertY(c(4, 20.5), from="user", to="ndc")),
mar=c(1, 1, 1, 9.5), ## set margins
new=TRUE) ## set new for overplot w/ next plot
plot(c(0, 2), c(0, 1), type='n', axes=F, xlab='', ylab='') ## ini plot2
rasterImage(legend_image, 0, 0, 1, 1) ## the gradient
lbsq <- seq.int(0, 1, l=5) ## seq. for labels
axis(4, at=lbsq, pos=1, labels=F, col=0, col.ticks=1, tck=-.1) ## axis ticks
mtext(sq, 4, -.5, at=lbsq, las=2, cex=.6) ## tick labels
mtext('diff', 3, -.125, cex=.6, adj=.1, font=2) ## title
par(op) ## reset par
I am trying to remove all margins and the "figure region" of a plot in R, so that the plot region comprises the entire graphic device. I thought the code below would do it, but there is still a border around my plot (wider on left/bottom, thinner on top/right). Thanks
par(oma=c(0, 0, 0, 0))
par(mar=c(0, 0, 0, 0))
par(plt=c(0, 1, 0, 1))
Thought I would add a picture to show my progress. The xaxs and yaxs removed nearly all border from the top and right- there is still a border on the left and bottom.
The relevant portion of my script is below.
png("Test.png",
width = 256, height = 256,
units = "px", pointsize = 6.4,
bg = "black", res = NA)
par(mar=c(0, 0, 0, 0), xaxs='i', yaxs='i')
smoothScatter(lhb$px, lhb$pz, nrpoints=0, xlim=c(-3,3), ylim=c(0,5),
main="", xlab="", ylab="", axes=FALSE,
colramp=colorRampPalette(c("black", "#202020", "#736AFF", "cyan", "yellow", "#F87431", "#FF7F00", "red", "#7E2217"))
)
segments(.83, 1.597, .83, 3.436, col = par("fg"), lty = par("lty"), lwd = par("lwd"))
segments(-.83, 1.597, -.83, 3.436, col = par("fg"), lty = par("lty"), lwd = par("lwd"))
segments(-.83, 3.436, .83, 3.436, col = par("fg"), lty = par("lty"), lwd = par("lwd"))
segments(-.83, 1.597, .83, 1.597, col = par("fg"), lty = par("lty"), lwd = par("lwd"))
dev.off()
One issue is fundamentally not getting what plt does. From ?par we have:
‘plt’ A vector of the form ‘c(x1, x2, y1, y2)’ giving the
coordinates of the plot region as fractions of the current
figure region.
So your plot region is of zero size if you do par(plt=c(1, 1, 1, 1)), so that doesn't seem to be the way to go. This is because the figure region contains the plot region.
This plot seems to cover the entire region, without any margins:
op <- par(mar = rep(0, 4))
plot(1:10)
par(op)
it covers it so well you can't see the axes or the box:
This assumes the default for 0 outer margin (oma). Is this what you were looking for?
We can see that just adjusting the plot margins, as above, we also change the plt parameter as a side effect:
> par("plt")
[1] 0.1173174 0.9399106 0.1457273 0.8828467
> op <- par(mar = rep(0, 4))
> par("plt")
[1] 0 1 0 1
> par(op)
> par("plt")
[1] 0.1173174 0.9399106 0.1457273 0.8828467
indicating that simply setting the plot margins is sufficient to get a plot/figure region encompassing the entire device.
Of course, there is still a bit of internal padding that insures the ranges of the axes are slightly large than the range of the data in both the x and y coordinates. But you can control this with xaxs and yaxs --- see ?par
Update: As the OP has shown the sort of figure they are trying to produce without margins, I can provide a reproducible example:
set.seed(1)
dat <- matrix(rnorm(100*100), ncol = 100, nrow = 100)
layout(matrix(1:2, ncol = 2))
image(dat)
op <- par(mar = rep(0, 4))
image(dat)
par(op)
layout(1)
which gives for comparison:
and showing just the full plotting region:
Try setting the clip region parameter 'xpd' to NA ( clipped to device).
par(xpd = NA)