Align a piechart in plot area - r

I'm generating a pie chart that I would like to incorporate into a Shiny page. The plot area (i.e.: the white space surrounding the plot) will be fluid. I'd like the plot itself (the grey area and the pie chart) to align to the left-hand side of the plot area, instead of appearing centered. Any ideas?
ggplot(treeData, aes(x = "", fill = ClassName)) +
ggtitle("Species distribution") +
geom_bar(width = 1) +
coord_polar("y", start=0) +
xlab("") + ylab("") +
theme(
axis.text.x=element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank(),
plot.margin=unit(c(25,0,0,0), "pt")) +
scale_fill_manual(values= c("#ffff00", "#008080", "#00ff00"))

The more detailed explanation for why this answer works is provided here. In brief, you need to place the plot into a grid that can expand as you resize the enclosing image.
library(ggplot2)
library(grid)
library(gtable)
# some test data
animals <- as.data.frame(
table(Species =
c(rep("Moose", sample(1:100, 1)),
rep("Frog", sample(1:100, 1)),
rep("Dragonfly", sample(1:100, 1))
)))
# make the pie chart
g <- ggplot(animals, aes(x = "", y = Freq, fill = Species)) +
geom_bar(width = 1, stat = "identity") +
coord_polar("y", start=0) +
theme(
axis.text.x=element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
panel.grid = element_blank(),
plot.margin=unit(c(25,0,0,0), "pt"))
# set the desired width and height of the
# pie chart (need to play around to find
# numbers that work
plotwidth = unit(6.1, "inch")
plotheight = unit(5, "inch")
# place into matrix that can expand
grob <- ggplotGrob(g)
mat <- matrix(list(grob, nullGrob(), nullGrob(), nullGrob()), nrow = 2)
widths <- unit(c(1, 1), "null")
widths[1] <- plotwidth
heights <- unit(c(1, 1), "null")
heights[1] <- plotheight
gm <- gtable_matrix(NULL, mat, widths, heights)
grid.newpage()
grid.draw(gm)

You can try to change the margins by adding
theme(plot.margin = unit(c(5.5, 100, 5.5, -100), "points"))
or use the cowplot function
cowplot::plot_grid(your_plot, NULL, ncol=2, rel_widths = c(2,1))

Related

How to avoid axis line disappearing when using ggarrange?

I'd like to arrange two facet plots using ggarrange (in order to get x axes aligned).
library(egg)
library(ggplot2)
p1 <- ggplot(warpbreaks) +
geom_bar(aes(x = wool)) +
facet_wrap(~tension, ncol = 2, scales = "free_x") +
theme_bw() +
theme(axis.line = element_line(colour = "black", size = .1),
panel.border = element_blank(),
strip.background = element_blank())
p2 <- ggplot(warpbreaks) +
geom_bar(aes(x = tension)) +
facet_wrap(~wool) +
theme_bw() +
theme(axis.line = element_line(colour = "black", size = .1),
panel.border = element_blank(),
strip.background = element_blank())
ggarrange(p1, p2, ncol = 2)
Works great, but unfortunately the vertical axis lines disappeared. This does not happen when using grid.arrange, but at least for my real data there the x axes are not aligned, hence my wish to use ggarrange. Is there a way to keep the axis lines?
tl;dr: setting panel.background = element_blank() should restore the axes.
I think it's a combination of a clipping issue in ggplot2 (the y axis line can be clipped by the plot panel, cutting its width in half), and egg::gtable_frame placing the axis below the plot panel.
library(egg)
library(ggplot2)
p1 <- ggplot(warpbreaks) +
geom_bar(aes(x = wool)) +
facet_wrap(~tension, ncol = 2, scales = "free_x") +
theme_bw() +
theme(axis.line = element_line(colour = alpha("red", 0.5), size = 5),
panel.border = element_blank(),
panel.background = element_rect(fill = alpha("white", 0.5),
linetype = 2, colour = "black"),
strip.background = element_blank())
p1
g1 <- ggplotGrob(p1)
gg <- gtable_frame(g1)
grid.newpage()
grid.draw(gg)

ggplot2: Make the filled area of the spatial plot flush with facet strip [duplicate]

I am creating some maps and want to remove all margins between the plot region and panel border.
This is the minimal example to reproduce my question
library(ggplot2)
library(grid)
df <- expand.grid(list(x = seq(1, 10), y = seq(1, 10), z = seq(1, 2)))
p <- ggplot(df) + geom_tile(aes(x, y)) + facet_wrap(~z)
p <- p + theme_minimal() + xlab('') + ylab('')
p <- p + theme(axis.text = element_blank(),
panel.grid = element_blank(),
axis.ticks = element_blank(),
panel.border = element_rect(colour = 'black', fill = 'transparent'),
panel.margin = unit(0, 'mm'))
p + ylim(2, 6) + xlim(2, 6)
This is the result of my codes.
How could I remove all white areas in the figure above? Thanks for any suggestions.
(Alright, here's my comment as an answer..)
Just add the following to the plot:
+ scale_y_continuous(expand = c(0,0)) + scale_x_continuous(expand = c(0,0))

Adjust widths of multiple plots made by lapply, arrangeGrob

I have code to make plots following:
My question is how to adjust the widths of 1st plot and last plot in the figure made?
dat=data.frame(x=rep(c("M","D"),each=60),y=rep(rep(c(4,6,8,10,12),each=12),2),z=runif(120,0,100),s=rep(c(1:4),each=3,len=120))
gp=lapply(split(dat,dat$y),function(dfr){
g=ggplot(data = dfr, aes(s,z)) +
geom_point(aes(shape=x,colour=x),size=4)+
ylim(0,100)+
xlab("Int segs")+
ggtitle(paste(dfr[1,2],"hours"))
return(g)})
tiff(file="Pas.tiff",width=60, height = 22,units="cm",res=300)
require(gridExtra)
rg=arrangeGrob(grobs=list(gp$`4` + theme(legend.position="none"),
gp$`6` + theme(legend.position="none",
axis.title.y = element_blank(),
axis.text.y = element_blank()),
gp$`8` + theme(legend.position="none",
axis.title.y = element_blank(),
axis.text.y = element_blank()),
gp$`10` + theme(legend.position="none",
axis.title.y = element_blank(),
axis.text.y = element_blank()),
gp$`12` + theme(axis.title.y = element_blank(),
axis.text.y = element_blank())),ncol=5,
top=textGrob("Pas rate",
gp=gpar(fontsize=20,fontface="bold"),
y = unit(.4, "cm")),
theme(margin(t=70,r=0.2,b=0.5,l=0.3,unit="mm")))
grid.newpage()
#grid.draw(cbind(lg, rg, size = "last"))
grid.draw(rg)
dev.off()
i'd use cbind() here (if facetting isn't an option)
gp[[1]] <- gp[[1]] + theme(legend.position="none")
gp[[5]] <- gp[[5]] + theme(axis.title.y = element_blank(),
axis.text.y = element_blank())
gp[c("6", "8", "10")] <- lapply(gp[c("6", "8", "10")], "+", e2 = theme(legend.position="none",
axis.title.y = element_blank(),
axis.text.y = element_blank()))
grid.newpage()
grid.draw(do.call(gridExtra::cbind.gtable, lapply(gp, ggplotGrob)))
I suggest you use gtable: you can extract legend and y.label from a first "dummy" plot, then plot each graph within a cell and with the same dimensions (having removed both legend and y.label from all of them, including 1st and last) to then add y.label and legend into separate cells.
require(ggplot2)
require(gtable)
require(grid)
library(dplyr)
library(scales)
dat <- data.frame(x=rep(c("M","D"),each=60),
y=rep(rep(c(4,6,8,10,12),each=12),2),
z=runif(120,0,100),
s=rep(c(1:4),each=3,len=120))
gp <- lapply(split(dat,dat$y),function(dfr){
g=ggplot(data = dfr, aes(s,z)) +
geom_point(aes(shape=x,colour=x),size=4)+
ylim(0,100)+
xlab("Int segs")+
ggtitle(paste(dfr[1,2],"hours"))
return(g)
})
tiff(file="Pas.tiff",width=60, height = 22,units="cm",res=300)
## Step 1:
## "dummy" plot, just to take y axis title/text and legend
dummy <- ggplotGrob(gp[[1]] + theme(panel.background = element_blank()))$grobs
legend <- dummy[[which(sapply(dummy, function(x) x$name) == "guide-box")]]
ytitle <- dummy[[grep("axis.title.y",sapply(dummy, function(x) x$name))]]
yticks <- dummy[[2]]
## Step 2:
## actual plots, all of them without y axis title/text and legend
pp <- 1
tab <- ggplotGrob(gp[[pp]] + theme(legend.position="none",
axis.title.y = element_blank(),
axis.text.y = element_blank()))
for(pp in 2:length(gp)){
tab <- gtable_add_cols(tab, unit(1,"null"))
tab <- gtable_add_grob(tab, ggplotGrob(gp[[pp]] + theme(legend.position="none",
axis.title.y = element_blank(),
axis.text.y = element_blank())),
t = 1, l = ncol(tab), b=nrow(tab), r=ncol(tab))
}
## Step 2:
## adding back ytitle, yticks and legend
## add narrow column to the left and put yticks labels withint
tab <- gtable_add_cols(tab, unit(1,"cm"), pos=0)
tab <- gtable_add_grob(tab, yticks,
t = 3, l = 2, b=nrow(tab)-3, r=1)
## add narrow column to the left and put y.axis label withint
ab <- gtable_add_cols(tab, unit(1,"cm"), pos=0)
tab <- gtable_add_grob(tab, ytitle,
t = 1, l = 1, b=nrow(tab), r=1)
## add narrow column to the right and put legend within
tab <- gtable_add_cols(tab, unit(1.5,"cm"))
tab <- gtable_add_grob(tab, legend,
t = 1, l = ncol(tab), b=nrow(tab), r=ncol(tab))
grid.newpage()
grid.draw(tab)
dev.off()

How to arrange plots with shared axes?

I am trying to create an arrangement of three scatterplots with shared axes and marginal histograms. This seems like it should be simple, but it's giving me fits. I have tried approaches with gridExtra and gtable, both of which have gotten the general arrangement like I want but with alignments and plot sizes that were off.
There are many other posts related to this question and I have experimented with the answers given to many of them, especially answers from #baptiste here and here. The latter's approach to controlling width might be the key to the alignments, but I haven't understood it well enough to adapt it to my problem.
A minimal working example follows, preceded by its result. The result has quite a few problems:
The bottom right plot is bigger than the other two. This has been an issue with all approaches, including grid.arrange(), with which I placed a blank rectangle grob to balance the scatter plots.
The sizes of the maringal histograms are way off and not what I would expect from the code, which is that their narrow dimension would be about 1/4 the size of the scatter plots' width.
The spacing of the plots is too wide.
None of the marginal histograms align correctly with the scatter plots.
The marginal histogram on the right (c) is so narrow that it doesn't show up at all in the png, yet it does show up (though still too narrow) in the RStudio plot viewer, though only when "zoomed".
Bonus points for an answer that will align the plots' axes correctly even if the width of the axes labels varies between plots as such would be much more adaptable for future problems.
library(ggplot2)
library(grid)
library(gtable)
data <- data.frame(a = rnorm(100, 30, 3), b = rnorm(100, 40, 5), c=rnorm(100, 50, 3))
b_a.scatter <- ggplot(data, aes(x=a, y=b)) + geom_point() + coord_equal(ratio=1, xlim=c(0,100), ylim=c(0,100)) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
plot.margin = unit(c(1,0,-0.5,1), "cm")
)
c_a.scatter <- ggplot(data, aes(x=a, y=c)) + geom_point() + coord_equal(ratio=1, xlim=c(0,100), ylim=c(0,100)) +
theme(plot.margin = unit(c(-0.5,0,0.5,1), "cm"))
c_b.scatter <- ggplot(data, aes(x=b, y=c)) + geom_point() + coord_equal(ratio=1, xlim=c(0,100), ylim=c(0,100)) +
theme(
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(-0.5,0,0.5,0), "cm")
)
a.hist <- ggplot(data, aes(x=a)) + geom_histogram() + coord_equal(xlim=c(0,100), ratio=1/4) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(0,0,1,1), "cm")
)
b.hist <- ggplot(data, aes(x=b)) + geom_histogram() + coord_equal(xlim=c(0,100), ratio=1/4) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.y = element_blank(),
axis.title.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = unit(c(0,0,1,0), "cm")
)
c.hist <- ggplot(data, aes(x=c)) + geom_histogram() + coord_flip(xlim=c(0,100)) +
theme(
axis.text.x = element_blank(),
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
plot.margin = unit(c(0,1,0,0), "cm")
)
blankPanel <- grid.rect(gp=gpar(col="white"))
gt <- gtable(widths = unit(rep(1,9), "null"),
heights = unit(rep(1,9), "null"),
respect=T)
gl <- list(ggplotGrob(b_a.scatter),
ggplotGrob(c_a.scatter), ggplotGrob(c_b.scatter), ggplotGrob(c.hist),
ggplotGrob(a.hist), ggplotGrob(b.hist))
gt <- gtable_add_grob(gt, gl,
l=c(1,1,5,9,1,5),
r=c(4,4,8,9,4,8),
t=c(1,5,5,5,9,9),
b=c(4,8,8,8,9,9))
grid.newpage()
png('multiplot.png')
grid.draw(gt)
dev.off()
Here's one approach: combine graphs column by column using rbind, then cbind the three columns. Dummy gtables are provided for the empty cells, they only contain layout information.
library(ggplot2)
library(gtable)
d <- data.frame(a = rnorm(100, 30, 3),
b = rnorm(100, 40, 5),
c=rnorm(100, 50, 3))
theme_set(theme_bw() +
theme(plot.background=element_rect(colour="red",size = 2)))
## define simpler plots
a <- ggplot(d, aes(x=a, y=b)) + geom_point() + xlim(0,100)+ ylim(0,90)
b <- ggplot(d, aes(x=a, y=c)) + geom_point() + xlim(0,100) + ylim(0,90)
c <- ggplot(d, aes(x=b, y=c)) + geom_point() + xlim(0,100) + ylim(0,90)
ah <- ggplot(d, aes(x=a)) + geom_histogram() +
xlim(0,100)+ ylim(0,2000000)
bh <- ggplot(d, aes(x=b)) + geom_histogram() +
xlim(0,100) + ylim(0,2000000)
ch <- ggplot(d, aes(x=c)) + geom_histogram() +
coord_flip(xlim=c(0,200000))
pl <- lapply(list(a,b,c,ah,bh,ch), ggplotGrob)
## function to create a dummy table (no grobs, zero size) of the right dim for (r/c)bind
dummy_gtable <- function(g){
gtable(widths=unit(rep(0,ncol(g)), 'null'), heights=unit(rep(0, nrow(g)), 'null'))
}
left <- rbind(pl[[1]],pl[[2]],pl[[4]])
middle <- rbind(dummy_gtable(pl[[1]]),pl[[3]],pl[[5]])
right <- rbind(dummy_gtable(pl[[1]]),pl[[6]], dummy_gtable(pl[[5]]))
grid.newpage()
grid.draw(cbind(left, middle, right))
Note that I'm using cbind and rbind from my experimental fork of gtable, because the released version does not use unit.pmax as unit comparison for widths and heights. Custom functions could be borrowed from another question to keep using the stable gtable version.

geom_tile and facet_grid/facet_wrap for same height of tiles

Using ggplot, I would like represent a graph tile with panel, but with same height tile for each panel.
I have this graph :
dataSta <- list(sites=rep(paste("S", 1:31),each=12), month=rep(1:12,31), value=round(runif(31*12, min=0, max=3000)), panel=c(rep("Group 1",16*12),rep("Group 2", 12*12), rep("Group 3", 3*12)))
library(ggplot2)
library(grid)
base_size <- 9
windows()
ggplot(data.frame(dataSta), aes(factor(month), sites)) +
geom_tile(aes(fill = value), colour = "black")+
facet_wrap(~panel, scale="free_y", nrow=3)+
theme_grey(base_size = base_size) +
labs(x = "",y = "") +
scale_x_discrete(expand = c(0, 0)) +
scale_y_discrete(expand = c(0, 0)) +
theme(legend.title = element_blank(),
axis.ticks = element_blank(),
axis.text.x = element_text(size = base_size *0.8, hjust = 0),
panel.margin = unit(0,"lines"),
strip.text = element_text(colour="red3", size=10, face=2))
But height of tiles is different between panel. I try to use facet_grid :
windows()
ggplot(data.frame(dataSta), aes(factor(month), sites)) +
geom_tile(aes(fill = value), colour = "black")+
facet_grid(panel~., scales="free_y", space="free")+
theme_grey(base_size = base_size) +
labs(x = "",y = "") +
scale_x_discrete(expand = c(0, 0)) +
scale_y_discrete(expand = c(0, 0)) +
theme(legend.title = element_blank(),
axis.ticks = element_blank(),
axis.text.x = element_text(size = base_size *0.8, hjust = 0),
panel.margin = unit(0,"lines"),
strip.text = element_text(colour="red3", size=10, face=2))
The problem with height of tiles is resolved, but labels of panel (Group 1 ... Group 3) are not on top of panel. Is it possible to change position of panel labels with facet_grid ? or combine facet_grid and facet_wrap ?
Thanks for your help, and sorry for my English !
You can look at what ggplot contains before plotting, and rescale the panels accordingly.
g <- ggplot_build(p)
## find out how many y-breaks are in each panel
## to infer the number of tiles
vtiles <- sapply(lapply(g$panel$ranges, "[[", "y.major"), length)
## convert the plot to a gtable object
gt <- ggplot_gtable(g)
## find out which items in the layout correspond to the panels
## we refer to the "t" (top) index of the layout
panels <- gt$layout$t[grepl("panel", gt$layout$name)]
## replace the default panel heights (1null) with relative sizes
## null units scale relative to each other, so we scale with the number of tiles
gt$heights[panels] <-lapply(vtiles, unit, "null")
## draw on a clean slate
library(grid)
grid.newpage()
grid.draw(gt)
It took me some time to find easier solution which is actually part of the facet_grid where you can set the space = "free_y". More info at recent question.
The ggforce package has a neat little function for this called facet_col. It requires no messing around with the grid package!
All you have to do is replace the call to facet_grid with the appropriate facet_col alternative:
library(ggplot2)
library(ggforce)
dataSta <- list(sites=rep(paste("S", 1:31),each=12),
month=rep(1:12,31), value=round(runif(31*12, min=0, max=3000)),
panel=c(rep("Group 1",16*12),rep("Group 2", 12*12), rep("Group 3", 3*12)))
base_size <- 9
ggplot(data.frame(dataSta), aes(factor(month), sites)) +
geom_tile(aes(fill = value), colour = "black") +
# Here's the line to alter:
facet_col(vars(panel), , scales = "free_y", space = "free") +
theme_grey(base_size = base_size) +
labs(x = "",y = "") +
scale_x_discrete(expand = c(0, 0)) +
scale_y_discrete(expand = c(0, 0)) +
theme(legend.title = element_blank(),
axis.ticks = element_blank(),
axis.text.x = element_text(size = base_size *0.8, hjust = 0),
panel.spacing = unit(0,"lines"),
strip.text = element_text(colour="red3", size=10, face=2))

Resources