Creating a facet theme/design plot in ggplot2 without using facet_ - r

Is there any possibility to create a facet_wrap looking plot in ggplot2 without using facet_wrap() The reason I would like to achieve this is to match some other design. In the plot without_facet below, can I somehow add "Setosa" in the top, so it looks like the with_facet plot, without using facet_wrap.
library(ggplot2)
df <- iris[iris$Species == 'setosa', ]
with_facet <- ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() +facet_wrap(~Species)
with_facet
without_facet <- ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point()

You can try
ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
ggtitle("setosa") +
theme(plot.title = element_text(hjust = 0.5))
A more "hackish"-one could be this hardcoded approach:
ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
ggtitle("setosa") +
geom_rect(xmin = 4.225, xmax = 5.875 , ymin=4.5, ymax=4.6, fill ="lightgrey") +
coord_cartesian(clip = 'off', expand = 0.05) +
theme(plot.title = element_text(hjust = 0.5, size = 12),
plot.margin = margin(t = 30, r = 20, b = 20, l = 20, unit = "pt"))

From Paleolimbot answer on Gitbub (https://github.com/tidyverse/ggplot2/issues/2344)
element_textbox <- function(...) {
el <- element_text(...)
class(el) <- c("element_textbox", class(el))
el
}
element_grob.element_textbox <- function(element, ...) {
text_grob <- NextMethod()
rect_grob <- element_grob(calc_element("strip.background", theme_bw()))
ggplot2:::absoluteGrob(
grid::gList(
element_grob(calc_element("strip.background", theme_bw())),
text_grob
),
height = grid::grobHeight(text_grob),
width = grid::unit(1, "npc")
)
}
From my original question, I added theme_bw()
library(ggplot2)
library(gridExtra)
df <- iris[iris$Species == 'setosa', ]
with_facet <- ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() +
facet_wrap(~Species) +
theme(plot.background = element_rect(color = 'black')) + theme_bw()
without_facet <- ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
ggtitle("setosa") +
theme_bw() +
theme(
plot.title = element_textbox(
hjust = 0.5, margin = margin(t = 5, b = 5), size = 10
),
)
grid.arrange(with_facet, without_facet)
Not identical, but works for my purpose.

This might be one option:
library(ggplot2)
df <- iris[iris$Species == 'setosa', ]
# with annotate:
with_annotate <-
ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
annotate('text', x = 5, y = 4.7, label = "setosa", size = 12)
with_annotate
#or if you do not want the heading to print over the plot area
with_coord_cart <-
ggplot(df, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point() +
annotate('text', x = 5, y = 4.7, label = "setosa", size = 8)+
coord_cartesian(ylim = c(2, 4.5), clip = 'off') +
theme(plot.margin = margin(2, 1, 1, 1, "lines"))
with_coord_cart
Which gives you:

Note: I had deleted it because it seemed no longer relevant after the original question was updated.
I am not sure I understand correctly. In case you want to arrange different plots together:
library(gridExtra)
grid.arrange(without_facet,
without_facet,
without_facet,
without_facet, nrow = 2)

Related

How to add labels to multiple ggplot graphs (A, B, C)

I am trying to add the labels A, B, and C to the top left hand corner of each of these graphs. I have tried cowplot::draw_plot_label(), but nothing seems to work. Can anyone help?
These A, B and C labels are not the main title of each plot.
# Packages
library(ggplot2)
library(gridExtra)
library(cowplot)
# 1st plot
p1 <- ggplot(data = new_data %>%
filter(Species =="Sharksucker_Remora")) +
scale_colour_manual(values=c(Sharksucker_Remora="black"), labels = c("Sharksucker Remora")) +
geom_line(mapping = aes(x = Date, y = Proportion, group = Species, colour = Species)) +
xlab("") +
ylab("Proportion") +
theme(legend.position="top") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1)) + labs(colour = ~italic(M.alfredi)~"Hitchhiker Species:") +
theme(legend.key=element_blank())
# 2nd plot
p2 <- ggplot(data = new_data %>%
filter(Species !="Sharksucker_Remora")) +
geom_line(mapping = aes(x = Date, y = Proportion, group = Species, colour = Species)) +
scale_colour_manual(values=c(Golden_Trevally="goldenrod2", Red_Snapper="firebrick2", Juvenile_Remora="darkolivegreen3"), labels = c("Juvenile Remora", "Golden Trevally", "Red Snapper")) +
xlab("") + ylab("Proportion") + labs(colour = "") + theme(legend.position="top") + theme(legend.key=element_blank()) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
# 3rd plot
p3 <- ggplot(data = new_data_counts) +
geom_bar(mapping = aes(x = Date, y = Count), stat =
'identity') +
xlab("Date (2015-2019)") + ylab("Total"~italic
(M.alfredi)~"Sightings") +
draw_plot_label(label =c("C") + theme(axis.text.x =
element_text(angle = 90, vjust = 0.5, hjust = 1))
# The grid
grid.arrange(p1,p2,p3)
I suggest you use labs(..., tag = ...) and theme(plot.tag = element_text()).
The code show how you can format the main title (here centered with hjust = 0.5) and the tag inside the theme() function. See the reproducible example, below:
# Packages
library(ggplot2)
library(gridExtra)
# library(cowplot) # not necessary here
# Plots
p1 <- ggplot() +
labs(title = "plot 1", tag = "A") +
theme(plot.title = element_text(hjust = 0.5),
plot.tag = element_text())
p2 <- ggplot() +
labs(title = "plot 2", tag = "B") +
theme(plot.title = element_text(hjust = 0.5),
plot.tag = element_text())
grid.arrange(p1, p2)
If you want the tag (A, B, C) to be inside the plotting area, this post suggest to use plot.tag.position = c(x, y). See for example:
p3 <- ggplot() +
labs(title = "plot 3", tag = "C") +
theme(plot.title = element_text(hjust = 0.5),
plot.tag = element_text(),
plot.tag.position = c(0.1, 0.8))
p3
Have you tried the package egg?
https://cran.r-project.org/web/packages/egg/vignettes/Overview.html
library(tidyverse)
library(magrittr)
data <- list()
for(i in 1:6) data[[i]] <- rnorm(100,0,1)
data %<>% bind_cols() %>% setNames(paste0("var",1:6))
p1 <- ggplot(data,aes(x = var1, y = var2)) + geom_point()
p2 <- ggplot(data,aes(x = var3, y = var4)) + geom_point()
p3 <- ggplot(data,aes(x = var5, y = var6)) + geom_point()
egg::ggarrange(p1,p2,p3,ncol = 1,
labels = c("A","B","C"))
Another option is using the patchwork package with plot_annotation which has the tag_levels argument which gives the possibility to add tags like letters or numbers. First a reproducible example with letters:
library(patchwork)
library(ggplot2)
p1 <- ggplot(mtcars) +
geom_point(aes(hp, disp)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, mpg, group = gear)) +
ggtitle('Plot 2')
p1 + p2 & plot_annotation(tag_levels = 'A')
Created on 2022-08-21 with reprex v2.0.2
Another option with numbers where you change the tag_levels to "1" like this:
p1 + p2 & plot_annotation(tag_levels = '1')
Created on 2022-08-21 with reprex v2.0.2
As you can see, the tags have letters or numbers. Check the links above for more information and options.

Adding a grob to patchwork plot R

I have a function that returns a patchwork plot and I can't make any changes to. I would like to add a rectGrob() on top of it. When I try to do this I remove two of the plots.
library(gridExtra)
library(patchwork)
library(ggplot2)
p1 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point()
p2 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) + geom_point()
p3 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width), col = 'blue') + geom_point()
p <- p1 + p2 + p3
grob_top <- grobTree(rectGrob(gp=gpar(fill='#F0F0F0',col= 'black')), textGrob('P1,P2, P3'))
grid.arrange(grob_top, p, heights = c(0.1, 0.9))
Using patchwork::wrap_elements() works better than gridExtra::grid.arrange()
patchwork::wrap_elements(grob_top) /
patchwork::wrap_elements(p) /
patchwork::wrap_elements(p) +
patchwork::plot_layout(ncol = 1, heights = c(0.1, 0.45, 0.45))
I think you want:
grid.arrange(grob_top,
p,
nrow = 2,
heights = c(0.1, 0.9))

Flushing the png device in plot_grid (using cowplot)

Here is my problem:
I do :
plot_grid(first_graph_by_mistake)
plot_grid(second_graph_on_purpuse)
ggsave("graph1.png")
vs ONLY
plot_grid(second_graph_on_purpose)
ggsave("graph2.png")
both the graphs look the same but when I do:
system("diff graph1.png graph2.png") it shows a difference.
Perhaps the png device is not flushed and some settings are different and that is why diff is showing a difference. How can I make the 2 graphs exactly the same ? That is my MAIN query.
I did the above in a very long piece of code. When I try to make a reprex example the diff does not show any difference between the 2 graphs. I am UNABLE to reproduce what I refer to in my query.
Here is the reprex:
library(cowplot)
library(grid)
plot.mpg.1 <- ggplot(mpg, aes(x = cty, y = hwy, colour = factor(cyl))) + geom_point(size=2.5)
plot.mpg.2 <- ggplot(mpg, aes(x = cty, y = displ, colour = factor(cyl))) + geom_point(size = 2.5)
plot.mpg.3 <- ggplot(mpg, aes(x = hwy, y = displ, colour = factor(cyl))) + geom_point(size = 2.5)
plot.mpg.4 <- ggplot(mpg, aes(x = drv, y = displ, colour = factor(cyl))) + geom_point(size = 2.5)
mygraphs <- list(plot.mpg.1,plot.mpg.2,plot.mpg.3,plot.mpg.4)
dummygraph <- mygraphs[[1]]
legend = get_legend(dummygraph + theme(legend.position = "bottom",legend.justification="center") + guides(fill = guide_legend(nrow = 1 )))
toplotlist <- lapply(mygraphs,function(x){x + theme(plot.margin = unit(c(0, 0, 0,0), "in"),legend.position="none")})
pmatrix <- do.call("plot_grid",toplotlist)
p<-plot_grid(pmatrix,legend,nrow=2,rel_heights = c(8,.2),rel_widths = c(10,1))
# Note : Please run first, the first section. Then run the second section.
######################################################################################################
# Without this line
# plot_grid(pmatrix,legend,nrow=2,rel_heights = c(64,1))
title <- ggdraw() + draw_label("My title", fontface='bold',size = 20)
semifinal <- plot_grid(title, p, ncol=1, rel_heights=c(0.1, 1))
blank <- grid.rect(gp=gpar(col="white"))
plot_grid(semifinal,blank,ncol=1,rel_heights=c(15,1))
ggsave(paste0("without_line.png"),height = 10,width = 10,dpi = 600)
########################################################################### ############################
# With this line
plot_grid(pmatrix,legend,nrow=2,rel_heights = c(64,1))
title <- ggdraw() + draw_label("My title", fontface='bold',size = 20)
semifinal <- plot_grid(title, p, ncol=1, rel_heights=c(0.1, 1))
blank <- grid.rect(gp=gpar(col="white"))
plot_grid(semifinal,blank,ncol=1,rel_heights=c(15,1))
ggsave(paste0("with_line.png"),height = 10,width = 10,dpi = 600)
###################################################################################################
# Now do
system("diff without_line.png with_line.png")

How to customize a boxplot legend indicating mean, outliers, median, etc?

I have a boxplot and by my supervisor's advice I have to indicate the mean, outliers and median in the legend, like this image:
How can I do this using ggplot2?
library(ggplot2)
A <- 1:20
DF <- data.frame(A)
ggplot(data = DF) +
geom_boxplot(aes(x = "", y = A))
There is no straightforward way. But you could make a custom legend using another plot:
p <- ggplot(mtcars) +
geom_boxplot(aes(x = factor(cyl), y = mpg))
d1 <- data.frame(x = 1, y = c(1:1000, 1502))
d2 <- data.frame(
y = c(boxplot.stats(d1$y)$stats, 1502),
x = 1,
label = c('min', '1st quartile', 'median', '3rd quartile', 'max', 'outlier')
)
leg <- ggplot(d1, aes(x, y)) + geom_boxplot(width = 0.2) +
geom_text(aes(x = 1.15, label = label), d2, hjust = 0) +
xlim(0.9, 1.5) +
theme_void() + theme(panel.background = element_rect(fill = 'white', color = 1))
p + annotation_custom(ggplotGrob(leg), xmin = 3, xmax = 3.5, ymin = 25, ymax = 35)

Showing median value in grouped boxplot in R

I have created boxplots using ggplot2 with this code.
plotgraph <- function(x, y, colour, min, max)
{
plot1 <- ggplot(dims, aes(x = x, y = y, fill = Region)) +
geom_boxplot()
#plot1 <- plot1 + scale_x_discrete(name = "Blog Type")
plot1 <- plot1 + labs(color='Region') + geom_hline(yintercept = 0, alpha = 0.4)
plot1 <- plot1 + scale_y_continuous(breaks=c(seq(min,max,5)), limits = c(min, max))
plot1 <- plot1 + labs(x="Blog Type", y="Dimension Score") + scale_fill_grey(start = 0.3, end = 0.7) + theme_grey()
plot1 <- plot1 + theme(legend.justification = c(1, 1), legend.position = c(1, 1))
return(plot1)
}
plot1 <- plotgraph (Blog, Dim1, Region, -30, 25)
A part of data I use is reproduced here.
Blog,Region,Dim1,Dim2,Dim3,Dim4
BlogsInd.,PK,-4.75,13.47,8.47,-1.29
BlogsInd.,PK,-5.69,6.08,1.51,-1.65
BlogsInd.,PK,-0.27,6.09,0.03,1.65
BlogsInd.,PK,-2.76,7.35,5.62,3.13
BlogsInd.,PK,-8.24,12.75,3.71,3.78
BlogsInd.,PK,-12.51,9.95,2.01,0.21
BlogsInd.,PK,-1.28,7.46,7.56,2.16
BlogsInd.,PK,0.95,13.63,3.01,3.35
BlogsNews,PK,-5.96,12.3,6.5,1.49
BlogsNews,PK,-8.81,7.47,4.76,1.98
BlogsNews,PK,-8.46,8.24,-1.07,5.09
BlogsNews,PK,-6.15,0.9,-3.09,4.94
BlogsNews,PK,-13.98,10.6,4.75,1.26
BlogsNews,PK,-16.43,14.49,4.08,9.91
BlogsNews,PK,-4.09,9.88,-2.79,5.58
BlogsNews,PK,-11.06,16.21,4.27,8.66
BlogsNews,PK,-9.04,6.63,-0.18,5.95
BlogsNews,PK,-8.56,7.7,0.71,4.69
BlogsNews,PK,-8.13,7.26,-1.13,0.26
BlogsNews,PK,-14.46,-1.34,-1.17,14.57
BlogsNews,PK,-4.21,2.18,3.79,1.26
BlogsNews,PK,-4.96,-2.99,3.39,2.47
BlogsNews,PK,-5.48,0.65,5.31,6.08
BlogsNews,PK,-4.53,-2.95,-7.79,-0.81
BlogsNews,PK,6.31,-9.89,-5.78,-5.13
BlogsTech,PK,-11.16,8.72,-5.53,8.86
BlogsTech,PK,-1.27,5.56,-3.92,-2.72
BlogsTech,PK,-11.49,0.26,-1.48,7.09
BlogsTech,PK,-0.9,-1.2,-2.03,-7.02
BlogsTech,PK,-12.27,-0.07,5.04,8.8
BlogsTech,PK,6.85,1.27,-11.95,-10.79
BlogsTech,PK,-5.21,-0.89,-6,-2.4
BlogsTech,PK,-1.06,-4.8,-8.62,-2.42
BlogsTech,PK,-2.6,-4.58,-2.07,-3.25
BlogsTech,PK,-0.95,2,-2.2,-3.46
BlogsTech,PK,-0.82,7.94,-4.95,-5.63
BlogsTech,PK,-7.65,-5.59,-3.28,-0.54
BlogsTech,PK,0.64,-1.65,-2.36,-2.68
BlogsTech,PK,-2.25,-3,-3.92,-4.87
BlogsTech,PK,-1.58,-1.42,-0.38,-5.15
Columns,PK,-5.73,3.26,0.81,-0.55
Columns,PK,0.37,-0.37,-0.28,-1.56
Columns,PK,-5.46,-4.28,2.61,1.29
Columns,PK,-3.48,2.38,12.87,3.73
Columns,PK,0.88,-2.24,-1.74,3.65
Columns,PK,-2.11,4.51,8.95,2.47
Columns,PK,-10.13,10.73,9.47,-0.47
Columns,PK,-2.08,1.04,0.11,0.6
Columns,PK,-4.33,5.65,2,-0.77
Columns,PK,1.09,-0.24,-0.92,-0.17
Columns,PK,-4.23,-4.01,-2.32,6.26
Columns,PK,-1.46,-1.53,9.83,5.73
Columns,PK,9.37,-1.32,1.27,-4.12
Columns,PK,5.84,-2.42,-5.21,1.07
Columns,PK,8.21,-9.36,-5.87,-3.21
Columns,PK,7.34,-7.3,-2.94,-5.86
Columns,PK,1.83,-2.77,1.47,-4.02
BlogsInd.,PK,14.39,-0.55,-5.42,-4.7
BlogsInd.,US,22.02,-1.39,2.5,-3.12
BlogsInd.,US,4.83,-3.58,5.34,9.22
BlogsInd.,US,-3.24,2.83,-5.3,-2.07
BlogsInd.,US,-5.69,15.17,-14.27,-1.62
BlogsInd.,US,-22.92,4.1,5.79,-3.88
BlogsNews,US,0.41,-2.03,-6.5,2.81
BlogsNews,US,-4.42,8.49,-8.04,2.04
BlogsNews,US,-10.72,-4.3,3.75,11.74
BlogsNews,US,-11.29,2.01,0.67,8.9
BlogsNews,US,-2.89,0.08,-1.59,7.06
BlogsNews,US,-7.59,8.51,3.02,12.33
BlogsNews,US,-7.45,23.51,2.79,0.48
BlogsNews,US,-12.49,15.79,-9.86,18.29
BlogsTech,US,-11.59,6.38,11.79,-7.28
BlogsTech,US,-4.6,4.12,7.46,3.36
BlogsTech,US,-22.83,2.54,10.7,5.09
BlogsTech,US,-4.83,3.37,-8.12,-0.9
BlogsTech,US,-14.76,29.21,6.23,9.33
Columns,US,-15.93,12.85,19.47,-0.88
Columns,US,-2.78,-1.52,8.16,0.24
Columns,US,-16.39,13.08,11.07,7.56
Even though I have tried to add detailed scale on y-axis, it is hard for me to pinpoint exact median score for each boxplot. So I need to print median value within each boxplot. There was another answer available (for faceted boxplot) which does not work for me as the printed values are not within the boxes but jammed together in the middle. It will be great to be able to print them within (middle and above the median line of) boxplots.
Thanks for your help.
Edit: I make a grouped graph as below.
Add
library(dplyr)
dims=dims%>%
group_by(Blog,Region)%>%
mutate(med=median(Dim1))
plotgraph <- function(x, y, colour, min, max)
{
plot1 <- ggplot(dims, aes(x = x, y = y, fill = Region)) +
geom_boxplot()+
labs(color='Region') +
geom_hline(yintercept = 0, alpha = 0.4)+
scale_y_continuous(breaks=c(seq(min,max,5)), limits = c(min, max))+
labs(x="Blog Type", y="Dimension Score") + scale_fill_grey(start = 0.3, end = 0.7) +
theme_grey()+
theme(legend.justification = c(1, 1), legend.position = c(1, 1))+
geom_text(aes(y = med,x=x, label = round(med,2)),position=position_dodge(width = 0.8),size = 3, vjust = -0.5,colour="blue")
return(plot1)
}
plot1 <- plotgraph (Blog, Dim1, Region, -30, 25)
Which gives (the text colour can be tweaked to something less tacky):
Note: You should consider using non-standard evaluation in your function rather than having it require the use of attach()
Edit:
One liner, not as clean I wanted it to be since I ran into problems with dplyr not properly aggregating the data even though it says the grouping was performed.
This function assume the dataframe is always called dims
library(ggplot2)
library(reshape2)
plotgraph <- function(x, y, colour, min, max)
{
plot1 <- ggplot(dims, aes_string(x = x, y = y, fill = colour)) +
geom_boxplot()+
labs(color=colour) +
geom_hline(yintercept = 0, alpha = 0.4)+
scale_y_continuous(breaks=c(seq(min,max,5)), limits = c(min, max))+
labs(x="Blog Type", y="Dimension Score") +
scale_fill_grey(start = 0.3, end = 0.7) +
theme_grey()+
theme(legend.justification = c(1, 1), legend.position = c(1, 1))+
geom_text(data= melt(with(dims, tapply(eval(parse(text=y)),list(eval(parse(text=x)),eval(parse(text=colour))), median)),varnames=c("Blog","Region"),value.name="med"),
aes_string(y = "med",x=x, label = "med"),position=position_dodge(width = 0.8),size = 3, vjust = -0.5,colour="blue")
return(plot1)
}
plot1 <- plotgraph ("Blog", "Dim1", "Region", -30, 25)
Assuming that Blog is your dataframe, the following should work:
min <- -30
max <- 25
meds <- aggregate(Dim1~Region, Blog, median)
plot1 <- ggplot(Blog, aes(x = Region, y = Dim1, fill = Region)) +
geom_boxplot()
plot1 <- plot1 + labs(color='Region') + geom_hline(yintercept = 0, alpha = 0.4)
plot1 <- plot1 + scale_y_continuous(breaks=c(seq(min,max,5)), limits = c(min, max))
plot1 <- plot1 + labs(x="Blog Type", y="Dimension Score") + scale_fill_grey(start = 0.3, end = 0.7) + theme_grey()
plot1 + theme(legend.justification = c(1, 1), legend.position = c(1, 1)) +
geom_text(data = meds, aes(y = Dim1, label = round(Dim1,2)),size = 5, vjust = -0.5, color='white')

Resources