Flushing the png device in plot_grid (using cowplot) - r

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")

Related

Add Label in Line chart in R

I am trying to add labels in line graph but am unable to do so.
I want to add lable such that blue line mentiones 'model_1'; red line mentioned 'model_2' and darkgreen line mentioned 'model_3'
Attaching the code below
p1 <- ggplot(data = Auto, aes(x = horsepower, y = mpg)) +
geom_point() +
geom_line(aes(y = fitted(lm_mpg_1)), color = "blue", size = 1) +
geom_line(aes(y = fitted(lm_mpg_2)), color = "red", size = 1) +
geom_line(aes(y = fitted(lm_mpg_3)), color = "darkgreen", size = 1)
I have tried to use geom_text, geom_label and annotate function however they give me error.
The code I tried was:
p1 + geom_text(label = c('model_1','model_2','model_3'))
You don't have any data. You can use dput to share your data. In the meanwhile I have used mtcars as an example below:
# library
library(ggplot2)
# Keep 30 first rows in the mtcars natively available dataset
data=head(mtcars, 30)
# 1/ add text with geom_text, use nudge to nudge the text
ggplot(data, aes(x=wt, y=mpg)) +
geom_point() + # Show dots
geom_text(
label=rownames(data),
nudge_x = 0.25, nudge_y = 0.25,
check_overlap = T
)
ggplot(data, aes(x=wt, y=mpg)) +
geom_point() + # Show dots
geom_label(
label=rownames(data),
nudge_x = 0.25, nudge_y = 0.25,
check_overlap = T
)
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_point()
p + annotate("text", x = 4, y = 25, label = "Some text")

How to set alpha parameter for background in gganimate

I try to set alpha parameter 0.1 for background in my animation:
library(tidyverse)
library(gganimate)
mtcars_ <- rename(mtcars, mpg_ = mpg, disp_ = disp)
mtcars_$mpg = min(mtcars$mpg)
gg <- ggplot(mtcars, aes(x = mpg, y = disp)) + geom_density_2d_filled(data = mtcars_, aes(x = mpg_, y = disp_), alpha = 0.1) + geom_line() + theme(legend.position = "none")
gg
anim <- gg + transition_reveal(mpg) + shadow_wake(1)
anim
but alpha is 1 in final movie. How to fix it?
I need movie with this image
One way to do this would be to replicate the data you need for each frame. geom_density should see everything in every frame, but geom_line should only "see" the values up to the currently displayed value. We could accomplish that using tidyr::uncount to make copies of our data, and then creating a variable for geom_line that is NA when the value is too high for the current frame.
library(tidyverse)
library(gganimate)
distinct_mpg <- mtcars %>% distinct(mpg) %>% arrange(mpg) %>% pull(mpg)
mtcars_frames <- mtcars %>%
uncount(length(distinct_mpg), .id = "frame") %>%
mutate(mpg_reveal = distinct_mpg[frame],
mpg_shown = if_else(mpg <= mpg_reveal, mpg, NA_real_))
animate(
ggplot(mtcars_frames, aes(y = disp)) +
geom_density_2d_filled(aes(x = mpg), alpha = 0.1) +
geom_line(aes(x = mpg_shown, group = frame)) +
transition_states(frame) +
scale_fill_viridis_d(guide = NULL),
fps = 20
)
You might just want either to remove the shadow_wake() or set its wake_length closer to 0. The visual results will be similar, but the computation time will be higher for the shadow_wake() option.
gg1 <- ggplot(mtcars, aes(x = mpg, y = disp)) +
geom_density_2d_filled(data = mtcars_, aes(x = mpg_, y = disp_), alpha = 0.2) + geom_line() +
theme(legend.position = "none",
panel.background = element_blank())
gg1 + transition_reveal(mpg)
shadow_wake() removed
Or set shadow_wake to a lower setting.
gg2 <-
ggplot(data = mtcars, aes(x = mpg, y = disp)) +
geom_density_2d_filled(data = mtcars_ , aes(x = mpg_, y = disp_), alpha = 0.2) +
geom_line() +
theme(legend.position = "none",
panel.background = element_blank())
gg2 + transition_reveal(mpg) + shadow_wake(wake_length = 0.05)
shadow_wake() lowered

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.

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

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)

It is possible to create inset graphs?

I know that when you use par( fig=c( ... ), new=T ), you can create inset graphs. However, I was wondering if it is possible to use ggplot2 library to create 'inset' graphs.
UPDATE 1: I tried using the par() with ggplot2, but it does not work.
UPDATE 2: I found a working solution at ggplot2 GoogleGroups using grid::viewport().
Section 8.4 of the book explains how to do this. The trick is to use the grid package's viewports.
#Any old plot
a_plot <- ggplot(cars, aes(speed, dist)) + geom_line()
#A viewport taking up a fraction of the plot area
vp <- viewport(width = 0.4, height = 0.4, x = 0.8, y = 0.2)
#Just draw the plot twice
png("test.png")
print(a_plot)
print(a_plot, vp = vp)
dev.off()
Much simpler solution utilizing ggplot2 and egg. Most importantly this solution works with ggsave.
library(ggplot2)
library(egg)
plotx <- ggplot(mpg, aes(displ, hwy)) + geom_point()
plotx +
annotation_custom(
ggplotGrob(plotx),
xmin = 5, xmax = 7, ymin = 30, ymax = 44
)
ggsave(filename = "inset-plot.png")
Alternatively, can use the cowplot R package by Claus O. Wilke (cowplot is a powerful extension of ggplot2). The author has an example about plotting an inset inside a larger graph in this intro vignette. Here is some adapted code:
library(cowplot)
main.plot <-
ggplot(data = mpg, aes(x = cty, y = hwy, colour = factor(cyl))) +
geom_point(size = 2.5)
inset.plot <- main.plot + theme(legend.position = "none")
plot.with.inset <-
ggdraw() +
draw_plot(main.plot) +
draw_plot(inset.plot, x = 0.07, y = .7, width = .3, height = .3)
# Can save the plot with ggsave()
ggsave(filename = "plot.with.inset.png",
plot = plot.with.inset,
width = 17,
height = 12,
units = "cm",
dpi = 300)
I prefer solutions that work with ggsave. After a lot of googling around I ended up with this (which is a general formula for positioning and sizing the plot that you insert.
library(tidyverse)
plot1 = qplot(1.00*mpg, 1.00*wt, data=mtcars) # Make sure x and y values are floating values in plot 1
plot2 = qplot(hp, cyl, data=mtcars)
plot(plot1)
# Specify position of plot2 (in percentages of plot1)
# This is in the top left and 25% width and 25% height
xleft = 0.05
xright = 0.30
ybottom = 0.70
ytop = 0.95
# Calculate position in plot1 coordinates
# Extract x and y values from plot1
l1 = ggplot_build(plot1)
x1 = l1$layout$panel_ranges[[1]]$x.range[1]
x2 = l1$layout$panel_ranges[[1]]$x.range[2]
y1 = l1$layout$panel_ranges[[1]]$y.range[1]
y2 = l1$layout$panel_ranges[[1]]$y.range[2]
xdif = x2-x1
ydif = y2-y1
xmin = x1 + (xleft*xdif)
xmax = x1 + (xright*xdif)
ymin = y1 + (ybottom*ydif)
ymax = y1 + (ytop*ydif)
# Get plot2 and make grob
g2 = ggplotGrob(plot2)
plot3 = plot1 + annotation_custom(grob = g2, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax)
plot(plot3)
ggsave(filename = "test.png", plot = plot3)
# Try and make a weird combination of plots
g1 <- ggplotGrob(plot1)
g2 <- ggplotGrob(plot2)
g3 <- ggplotGrob(plot3)
library(gridExtra)
library(grid)
t1 = arrangeGrob(g1,ncol=1, left = textGrob("A", y = 1, vjust=1, gp=gpar(fontsize=20)))
t2 = arrangeGrob(g2,ncol=1, left = textGrob("B", y = 1, vjust=1, gp=gpar(fontsize=20)))
t3 = arrangeGrob(g3,ncol=1, left = textGrob("C", y = 1, vjust=1, gp=gpar(fontsize=20)))
final = arrangeGrob(t1,t2,t3, layout_matrix = cbind(c(1,2), c(3,3)))
grid.arrange(final)
ggsave(filename = "test2.png", plot = final)
'ggplot2' >= 3.0.0 makes possible new approaches for adding insets, as now tibble objects containing lists as member columns can be passed as data. The objects in the list column can be even whole ggplots... The latest version of my package 'ggpmisc' provides geom_plot(), geom_table() and geom_grob(), and also versions that use npc units instead of native data units for locating the insets. These geoms can add multiple insets per call and obey faceting, which annotation_custom() does not. I copy the example from the help page, which adds an inset with a zoom-in detail of the main plot as an inset.
library(tibble)
library(ggpmisc)
p <-
ggplot(data = mtcars, mapping = aes(wt, mpg)) +
geom_point()
df <- tibble(x = 0.01, y = 0.01,
plot = list(p +
coord_cartesian(xlim = c(3, 4),
ylim = c(13, 16)) +
labs(x = NULL, y = NULL) +
theme_bw(10)))
p +
expand_limits(x = 0, y = 0) +
geom_plot_npc(data = df, aes(npcx = x, npcy = y, label = plot))
Or a barplot as inset, taken from the package vignette.
library(tibble)
library(ggpmisc)
p <- ggplot(mpg, aes(factor(cyl), hwy, fill = factor(cyl))) +
stat_summary(geom = "col", fun.y = mean, width = 2/3) +
labs(x = "Number of cylinders", y = NULL, title = "Means") +
scale_fill_discrete(guide = FALSE)
data.tb <- tibble(x = 7, y = 44,
plot = list(p +
theme_bw(8)))
ggplot(mpg, aes(displ, hwy, colour = factor(cyl))) +
geom_plot(data = data.tb, aes(x, y, label = plot)) +
geom_point() +
labs(x = "Engine displacement (l)", y = "Fuel use efficiency (MPG)",
colour = "Engine cylinders\n(number)") +
theme_bw()
The next example shows how to add different inset plots to different panels in a faceted plot. The next example uses the same example data after splitting it according to the century. This particular data set once split adds the problem of one missing level in one of the inset plots. As these plots are built on their own we need to use manual scales to make sure the colors and fill are consistent across the plots. With other data sets this may not be needed.
library(tibble)
library(ggpmisc)
my.mpg <- mpg
my.mpg$century <- factor(ifelse(my.mpg$year < 2000, "XX", "XXI"))
my.mpg$cyl.f <- factor(my.mpg$cyl)
my_scale_fill <- scale_fill_manual(guide = FALSE,
values = c("red", "orange", "darkgreen", "blue"),
breaks = levels(my.mpg$cyl.f))
p1 <- ggplot(subset(my.mpg, century == "XX"),
aes(factor(cyl), hwy, fill = cyl.f)) +
stat_summary(geom = "col", fun = mean, width = 2/3) +
labs(x = "Number of cylinders", y = NULL, title = "Means") +
my_scale_fill
p2 <- ggplot(subset(my.mpg, century == "XXI"),
aes(factor(cyl), hwy, fill = cyl.f)) +
stat_summary(geom = "col", fun = mean, width = 2/3) +
labs(x = "Number of cylinders", y = NULL, title = "Means") +
my_scale_fill
data.tb <- tibble(x = c(7, 7),
y = c(44, 44),
century = factor(c("XX", "XXI")),
plot = list(p1, p2))
ggplot() +
geom_plot(data = data.tb, aes(x, y, label = plot)) +
geom_point(data = my.mpg, aes(displ, hwy, colour = cyl.f)) +
labs(x = "Engine displacement (l)", y = "Fuel use efficiency (MPG)",
colour = "Engine cylinders\n(number)") +
scale_colour_manual(guide = FALSE,
values = c("red", "orange", "darkgreen", "blue"),
breaks = levels(my.mpg$cyl.f)) +
facet_wrap(~century, ncol = 1)
In 2019, the patchwork package entered the stage, with which you can create
insets
easily by using the inset_element() function:
require(ggplot2)
require(patchwork)
gg1 = ggplot(iris, aes(Sepal.Length, Sepal.Width)) +
geom_point()
gg2 = ggplot(iris, aes(Sepal.Length)) +
geom_density()
gg1 +
inset_element(gg2, left = 0.65, bottom = 0.75, right = 1, top = 1)

Resources