Drawing ggplot Footer Using linesGrob within grobTree - r

I have created an extensive theme to plot in ggplot similar to Five Thirty Eight. Without using ggthemes, how can I utilize linesGrob to draw a line above my footer that is centered and goes 85% of the way across the plot without touching the edges of the plot? I am striving for the footer similar to this plot.
I can edit the font text, color, and size so don't worry about that.
So far, what I have is this:
data(iris)
library(ggplot2)
library(grid)
library(gridExtra)
plot20 <- ggplot(iris,aes(x=Petal.Length,y=Sepal.Length,color=Species)) +
geom_point(alpha=0.5,size=5) +
ylab("") +
xlab("") +
theme(panel.grid.minor.y=element_blank(),
panel.grid.major.x=element_line(color="#D2D2D2",size=0.7),
panel.grid.major.y=element_line(color="#D2D2D2",size=0.7),
panel.grid.minor.x=element_blank(),
panel.background = element_rect(fill = '#F0F0F0',colour=NA),
plot.background = element_rect(fill = '#F0F0F0', colour=NA, size = 4),
legend.background=element_rect(fill="#F0F0F0"),
legend.key=element_blank(),
legend.title=element_text(face="bold"),
axis.text=element_text(face="bold"),
legend.position="none",
axis.ticks=element_blank())
#Plot Header
my_g2 <- grobTree(rectGrob(gp=gpar(fill='#F0F0F0',col=NA)),
textGrob("Iris Dataset",x=0.115, vjust = -0.5,gp=gpar(fontsize=18,fontface="bold")),
textGrob("This is a subheader for the iris dataset",x=0.235,vjust=1.5,gp=gpar(fontsize=14)))
#Plot Footer
my_g1 <- grobTree(rectGrob(gp=gpar(fill="#F0F0F0",col=NA)),
textGrob(" medavis6",x=0,hjust=0,gp=gpar(col="darkorange",fontsize=8,fontface="bold")),
textGrob("Source: R",x=.85,hjust=-1.06,gp=gpar(col="black",fontsize=8)))
#Plot All Together
allplot <- grid.arrange(my_g2,plot20,my_g1,heights=c(1.17,11,0.5))
Which gives me this.
I think I should be using linesGrob() within my footer grobTree(), but whenever I try to do it I cannot make it appear in my plots. I'm not sure if my rectGrob() is plotting over the top of it or what is happening.
Thanks for any and all help and please, let me know if you need any clarification. Also, if any of my code is poorly written, I'm always looking for constructive criticism to make it better!

I also used linesGrob
#Plot Footer
my_g1 <- grobTree(rectGrob(gp=gpar(fill="#F0F0F0",col=NA)),
linesGrob(unit(c(.05, .95), "npc"), unit(1, "npc"),
gp = gpar(col = 'lightgrey', lwd = 4)),
textGrob(" medavis6",x=0,hjust=0,gp=gpar(col="darkorange",fontsize=8,fontface="bold")),
textGrob("Source: R",x=.85,hjust=-1.06,gp=gpar(col="black",fontsize=8)))
#Plot All Together
allplot <- grid.arrange(my_g2,plot20,my_g1,heights=c(1.17,11,0.5))
grid.draw(allplot)

Related

Is it possible to make these adjustments to a ggcorrplot() while preserving a complete theme?

I've created a correlation matrix:
cor_matrix = cor(qual_colleges_all_data_clean[ ,c(4,5,8,9,10,12,13,14,16,20,21,22,23,25)], method='pearson',use='complete.obs')
ggcorrplot(cor_matrix, hc.order = TRUE, method ='circle', type='lower', colors = c("darkblue", "white", "red")) +
labs(title = "Correlation Matrix:", subtitle = "Select Variables") +
theme_minimal() +
theme(
legend.key.width = unit(0.6, "cm"),
legend.key.height = unit(1.3, "cm"),
axis.text.x = element_text(angle = 45) #to put x-axis labels at 45 angles
)
Here's what it looks like:
Initially the x-axis labels were all horizontal and thus were a jumbled mess, so I angled them. But as you can see, they're overlapping the plot. The problem is that I need to preserve theme_minimal() if at all possible. I've tried every theme() adjustment I could find online, but I cannot make anything work. So, I have three questions:
How can adjust the x-axis headings while preserving theme_minimal()?
How can I widen the plot while preserving theme_minimal()? My hope is that will space the circles a bit more suitably.
How can I eliminate the Var2 and Var1 axis labels while preserving theme_minimal()?
I realize these are basic questions, so I really appreciate you taking the time to help me. Thanks in advance!

Is it possible to specify the size / layout of a single plot to match a certain grid in R?

I use R for most of my data analysis. Until now I used to export the results as a CSV and visualized them using Macs Numbers.
The reason: The Graphs are embeded in documents and there is a rather large border on the right side reserved for annotations (tufte handout style). Between the acutal text and the annotations column there is white space. The plot of the graphs needs to fit the width of text while the legend should be placed in the annotation column.
I would prefer to also create the plots within R for a better workflow and higher efficiency. Is it possible to create such a layout using plotting with R?
Here is an example of what I would like to achieve:
And here is some R Code as a starter:
library(tidyverse)
data <- midwest %>%
head(5) %>%
select(2,23:25) %>%
pivot_longer(cols=2:4,names_to="Variable", values_to="Percent") %>%
mutate(Variable=factor(Variable, levels=c("percbelowpoverty","percchildbelowpovert","percadultpoverty"),ordered=TRUE))
ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
geom_col(position=position_dodge(width=0.85),width=0.8) +
labs(x="County") +
theme(text=element_text(size=9),
panel.background = element_rect(fill="white"),
panel.grid = element_line(color = "black",linetype="solid",size= 0.3),
panel.grid.minor = element_blank(),
panel.grid.major.x=element_blank(),
axis.line.x=element_line(color="black"),
axis.ticks= element_blank(),
legend.position = "right",
legend.title = element_blank(),
legend.box.spacing = unit(1.5,"cm") ) +
scale_y_continuous(breaks= seq(from=0, to=50,by=5),
limits=c(0,51),
expand=c(0,0)) +
scale_fill_manual(values = c("#CF232B","#942192","#000000"))
I know how to set a custom font, just left it out for easier saving.
Using ggsave
ggsave("Graph_with_R.jpeg",plot=last_plot(),device="jpeg",dpi=300, width=18, height=9, units="cm")
I get this:
This might resample the result aimed for in the actual case, but the layout and sizes do not fit exact. Also recognize the different text sizes between axis titles, legend and tick marks on y-axes. In addition I assume the legend width depends on the actual labels and is not fixed.
Update
Following the suggestion of tjebo I posted a follow-up question.
Can it be done? Yes. Is it convenient? No.
If you're working in ggplot2 you can translate the plot to a gtable, a sort of intermediate between the plot specifications and the actual drawing. This gtable, you can then manipulate, but is messy to work with.
First, we need to figure out where the relevant bits of our plot are in the gtable.
library(ggplot2)
library(gtable)
library(grid)
plt <- ggplot(mtcars, aes(factor(cyl), fill = factor(vs))) +
geom_bar(position = position_dodge2(preserve = "single"))
# Making gtable
gt <- ggplotGrob(plt)
gtable_show_layout(gt)
Then, we can make a new gtable with prespecified dimensions and place the bits of our old gtable into it.
# Making a new gtable
new <- gtable(widths = unit(c(12.5, 1.5, 4), "cm"),
heights = unit(9, "cm"))
# Adding main panel and axes in first cell
new <- gtable_add_grob(
new,
gt[7:9, 3:5], # If you see the layout above as a matrix, the main bits are in these rows/cols
t = 1, l = 1
)
# Finding the legend
legend <- gt$grobs[gt$layout$name == "guide-box"][[1]]
legend <- legend$grobs[legend$layout$name == "guides"][[1]]
# Adding legend in third cell
new <- gtable_add_grob(
new, legend, t = 1, l = 3
)
# Saving as raster
ragg::agg_png("test.png", width = 18, height = 9, units = "cm", res = 300)
grid.newpage(); grid.draw(new)
dev.off()
#> png
#> 2
Created on 2021-04-02 by the reprex package (v1.0.0)
The created figure should match the dimensions you're looking for.
Another option is to draw the three components as separate plots and stitch them together in the desired ratio.
The below comes quite close to the desired ratio, but not exactly. I guess you'd need to fiddle around with the values given the exact saving dimensions. In the example I used figure dimensions of 7x3.5 inches (which is similar to 18x9cm), and have added the black borders just to demonstrate the component limits.
library(tidyverse)
library(patchwork)
data <- midwest %>%
head(5) %>%
select(2,23:25) %>%
pivot_longer(cols=2:4,names_to="Variable", values_to="Percent") %>%
mutate(Variable=factor(Variable, levels=c("percbelowpoverty","percchildbelowpovert","percadultpoverty"),ordered=TRUE))
p1 <-
ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
geom_col() +
scale_fill_manual(values = c("#CF232B","#942192","#000000"))
p_legend <- cowplot::get_legend(p1)
p_main <- p1 <-
ggplot(data=data, mapping=aes(x=county, y=Percent, fill=Variable)) +
geom_col(show.legend = FALSE) +
scale_fill_manual(values = c("#CF232B","#942192","#000000"))
p_main + plot_spacer() + p_legend +
plot_layout(widths = c(12.5, 1.5, 4)) &
theme(plot.margin = margin(),
plot.background = element_rect(colour = "black"))
Created on 2021-04-02 by the reprex package (v1.0.0)
update
My solution is only semi-satisfactory as pointed out by the OP. The problem is that one cannot (to my knowledge) define the position of the grob in the third panel.
Other ideas for workarounds:
One could determine the space needed for text (but this seems not so easy) and then to size the device accordingly
Create a fake legend - however, this requires the tiles / text to be aligned to the left with no margin, and this can very quickly become very hacky.
In short, I think teunbrand's solution is probably the most straight forward one.
Update 2
The problem with the left alignment should be fixed with Stefan's suggestion in this thread

Keeping plot spacing intact using Cowplot align

I am trying to make some 'close stacked' plots using Cowplot. I can get the effect I want using the following MWE:
library(ggplot2)
library(cowplot)
disp_plot <- ggplot(data=mtcars,aes(x= hp,y=disp)) + geom_point()+
theme(plot.margin = unit(c(0.3,0.3,0.0,0.15), "cm"),
axis.text.x =element_blank(),axis.title.x =element_blank())
mpg_plot <- ggplot(data=mtcars,aes(x= hp,y=mpg)) + geom_point()+
theme(plot.margin = unit(c(0.0,0.3,0.0,0.15), "cm"),
axis.text.x =element_blank(),axis.title.x =element_blank())
qsec_plot <- ggplot(data=mtcars,aes(x= hp,y=qsec)) + geom_point()+
theme(plot.margin = unit(c(0.0,0.3,0.3,0.15), "cm"))
mp <- plot_grid(disp_plot, mpg_plot, qsec_plot,ncol = 1,align='v', axis = 'l')
Notice that mpg is not the same vertical scale as the others. To get the plots to be the same vertical size as well, I'd like to use "align='hv'" in plot_grid. However, this causes the spacing set by the margins to revert and the figures fly apart again. Is there a way I can preserve my spacing?
Not cowplot, but this seems to work.
library(egg)
library(grid)
grid.draw(ggarrange(plots=list(disp_plot, mpg_plot, qsec_plot)))

How to remove extra space left by setting "theme(legend.title = element_blank())“ in ggplot2

there!
I want to remove legend title in ggplot2. I used legend.title = element_blank(), but there is still some extra space at the top of the legend box where the legend title originally was. Actually, I don't want that space. Anyone has ideas how to solve it? Thanks!
Here is the code to replicate the unwanted extra space:
library(ggplot2)
library(grid)
df1 <- data.frame(
sex = factor(c("Female","Female","Male","Male")),
time = factor(c("Lunch","Dinner","Lunch","Dinner"), levels=c("Lunch","Dinner")),
total_bill = c(13.53, 16.81, 16.24, 17.42)
)
# A basic graph
lp <- ggplot(data=df1, aes(x=time, y=total_bill, group=sex, shape=sex)) + geom_line() + geom_point()
lp + theme(legend.title=element_blank(),
legend.background = element_rect(colour = "black", size = 0.1),
legend.key.size = unit(0.4, "lines"))
the code snippet is taken from http://www.cookbook-r.com/Graphs/Legends_(ggplot2)/ with a little modification.
The extra space exists between the first legend key and the top border of the legend box. I think it's because I resize the key size. When I remove `legend.key.size = unit(0.4, "lines"), everything is fine. However, I want to keep that line in my specific figure. I'm not sure if there is any way to solve that problem.
This is my first answer ever.
theme(legend.margin=margin(t=-0.25,l=0.05,b=0.0,r=0.05, unit='cm'))
The negative moves the space inwards.
how about
p + theme(legend.position="none")
where p is the ggplot.

Remove facet_wrap labels completely

I'd like to remove the labels for the facets completely to create a sort of sparkline effect, as for the audience the labels are irrelevant, the best I can come up with is:
library(MASS)
library(ggplot2)
qplot(week,y,data=bacteria,group=ID, geom=c('point','line'), xlab='', ylab='') +
facet_wrap(~ID) +
theme(strip.text.x = element_text(size=0))
So can I get rid of the (now blank) strip.background completely to allow more space for the "sparklines"?
Or alternatively is there a better way to get this "sparkline" effect for a large number of binary valued time-series like this?
For ggplot v2.1.0 or higher, use element_blank() to remove unwanted elements:
library(MASS) # To get the data
library(ggplot2)
qplot(
week,
y,
data = bacteria,
group = ID,
geom = c('point', 'line'),
xlab = '',
ylab = ''
) +
facet_wrap(~ ID) +
theme(
strip.background = element_blank(),
strip.text.x = element_blank()
)
In this case, the element you're trying to remove is called strip.
Alternative using ggplot grob layout
In older versions of ggplot (before v2.1.0), the strip text occupies rows in the gtable layout.
element_blank removes the text and the background, but it does not remove the space that the row occupied.
This code removes those rows from the layout:
library(ggplot2)
library(grid)
p <- qplot(
week,
y,
data = bacteria,
group = ID,
geom = c('point', 'line'),
xlab = '',
ylab = ''
) +
facet_wrap(~ ID)
# Get the ggplot grob
gt <- ggplotGrob(p)
# Locate the tops of the plot panels
panels <- grep("panel", gt$layout$name)
top <- unique(gt$layout$t[panels])
# Remove the rows immediately above the plot panel
gt = gt[-(top-1), ]
# Draw it
grid.newpage()
grid.draw(gt)
I'm using ggplot2 version 1 and the commands required have changed.
Instead of
ggplot() ... +
opts(strip.background = theme_blank(), strip.text.x = theme_blank())
you now use
ggplot() ... +
theme(strip.background = element_blank(), strip.text = element_blank())
For more detail see http://docs.ggplot2.org/current/theme.html
Sandy's updated answer seems good but, possibly has been rendered obsolete by updates to ggplot? From what I can tell the following code (a simplified version of Sandy's original answer) reproduces Sean's original graph without any extra space:
library(ggplot2)
library(grid)
qplot(week,y,data=bacteria,group=ID, geom=c('point','line'), xlab='', ylab='') +
facet_wrap(~ID) +
theme(strip.text.x = element_blank())
I am using ggplot 2.0.0.
As near as I can tell, Sandy's answer is correct but I think it's worth mentioning that there seems to be a small difference the width of a plot with no facets and the width of a plot with the facets removed.
It isn't obvious unless you're looking for it but, if you stack plots using the viewport layouts that Wickham recommends in his book, the difference becomes apparent.

Resources