How to set y axis limits when using map2(~ggplot...? - r

How can I set different y axis limits in each plot when using purrr::map2?
I would like to set the y-axis lower limit to half the maximum y-axis value, something like: max(y-axis value/2).
data(mtcars)
library(tidyverse)
mtcars_split <-
mtcars %>%
split(mtcars$cyl)
plots <- map2(
mtcars_split,
names(mtcars_split),
~ggplot(data = .x, mapping = aes(y = mpg, x = wt)) +
geom_jitter() +
ggtitle(.y)+
scale_y_continuous(limits=c(max(.y)/2,NA))
)
plots
Error in max(.y)/2 : non-numeric argument to binary operator

.y is the name of the dataframe, which is why max(.y)/2 is giving you that error. This should give you what you want:
plots <- imap(
mtcars_split,
~ggplot(data = .x, mapping = aes(y = mpg, x = wt)) +
geom_jitter() +
ggtitle(.y) +
scale_y_continuous(limits=c(max(.x$mpg)/2,NA))
)
Note that imap(x, ...) is just shorthand for map2(x, names(x), ...).

This doesn't work based on the y-axis value, but it gets the job done if you don't mind specifying your y-column twice:
plots <- map2(
mtcars_split,
names(mtcars_split),
~ggplot(data = .x, mapping = aes(y = mpg, x = wt)) +
geom_jitter() +
ggtitle(.y)+
scale_y_continuous(limits=c(max(.x$mpg)/2,NA))
)
Or maybe a safer option:
plots <- map2(
mtcars_split,
names(mtcars_split),
~{
ploty <- 'mpg'
plotx <- 'wt'
ggplot(data = .x, mapping = aes_string(y = ploty, x = plotx)) +
geom_jitter() +
ggtitle(.y)+
scale_y_continuous(limits=c(max(.x[[ploty]])/2,NA))
}
)

Related

Aesthetics and bang-bang in ggplot2

I am trying to write a function that creates a barplot but I have trouble getting the fill aesthetic right.
If I use fill = !!x leads to Quosures can only be unquoted within a quasiquotation context.
and fill = x leads to Aesthetics must be either length 1 or the same as the data (4): fill
My Code:
genBar <- function(data, x, y) {
x <- enquo(x)
y <- enquo(y)
plot <- ggplot(data) +
geom_bar(aes(!!x, !!y),
stat = 'identity',
fill = <help>)
return(plot)
}
fill should be inside aes. Try :
library(ggplot2)
genBar <- function(data, x, y) {
plot <- ggplot(data) +
geom_bar(aes({{x}}, {{y}}, fill = {{x}}),
stat = 'identity')
return(plot)
}
genBar(mtcars, cyl, mpg)
If you want to pass column names as string use .data pronoun.
genBar <- function(data, x, y) {
plot <- ggplot(data) +
geom_bar(aes(.data[[x]], .data[[y]], fill = .data[[x]]),
stat = 'identity')
return(plot)
}
genBar(mtcars, "cyl", "mpg")
Are you looking for something like this?
library(dplyr)
library(ggplot2)
genBar <- function(data, x, y) {
x <- enquo(x)
y <- enquo(y)
plot <- ggplot(data) +
geom_bar(aes(!!x, !!y, fill = !!x),
stat = 'identity')
return(plot)
}
iris %>%
group_by(Species) %>%
summarize(Size = mean(Petal.Length)) %>%
genBar(Species, Size)
Created on 2020-12-04 by the reprex package (v0.3.0)

Same variables from 2 separate data.frames sided by side in ggplot2

I want to plot the exact same variable names (ses & math) from 2 separate data.frames (dat1 & dat2) but side by side so I can visually compare them.
I have tried the following but it places both data.frames on top of each other.
Is there a function within ggplot2 to plot ses vs. math from dat1 and the same from dat2 side by side and placed on the same axes scales?
library(ggplot2)
dat1 <- read.csv('https://raw.githubusercontent.com/rnorouzian/e/master/hsb.csv')
dat2 <- read.csv('https://raw.githubusercontent.com/rnorouzian/e/master/sm.csv')
ggplot(dat1, aes(x = ses, y = math, colour = factor(sector))) +
geom_point() +
geom_point(data = dat2, aes(x = ses, y = math, colour = factor(sector)))
You can try faceting combining the two datasets :
library(dplyr)
library(ggplot2)
list(dat1 = dat1 %>%
select(sector,ses, math) %>%
mutate(sector = as.character(sector)) ,
dat2 = dat2 %>% select(sector,ses, math)) %>%
bind_rows(.id = 'name') %>%
ggplot() +
aes(x = ses, y = math, colour = factor(sector)) +
geom_point() +
facet_wrap(.~name)
Another option is to create list of plots and arrange them with grid.arrange :
list_plots <- lapply(list(dat1, dat2), function(df) {
ggplot(df, aes(x = ses, y = math, colour = factor(sector))) + geom_point()
})
do.call(gridExtra::grid.arrange, c(list_plots, ncol = 2))

Save a list of plots dynamically

I am able to generate some plots based on a list of data frames:
df1 <- mtcars
df2 <- mtcars
combined_mtcars <- list(first_df = df1, second_df = df2)
# make the plots
imap(.x = combined_mtcars, ~ggplot(.x, aes(x = hp, y = mpg, group = cyl)) +
geom_line() +
ggtitle(.y))
I wanted to then save each plot to a directory called /plots. So I tried adding ggsave like so:
imap(.x = combined_mtcars, ~ggplot(.x, aes(x = hp, y = mpg, group = cyl)) +
geom_line() +
ggtitle(.y)) %>%
imap(~ggsave(plot = .y, file = paste0("/plots/", .y, ".png")))
This resulted in error "Saving 6.62 x 5.57 in image
Error in UseMethod("grid.draw") :
no applicable method for 'grid.draw' applied to an object of class "character"".
How can I save each iteration where the filename is the same as the title .y?
We need to make sure the ggplot object is being passed as the first argument, using the tag argument in the labs() function allows us to assign the plot to a "variable".
imap(.x = combined_mtcars, ~ggplot(.x, aes(x = hp, y = mpg, group = cyl)) +
geom_line() +
labs(title = .y, tag="Plot")%>%
imap(~ggsave(plot = Plot, file = paste0("/plots/", .y, ".png")))
If that does not work, try this since ggsave may default to the correct plot.
imap(.x = combined_mtcars, ~ggplot(.x, aes(x = hp, y = mpg, group = cyl)) +
geom_line() +
ggtitle(.y)) %>%
imap(~ggsave(file = paste0("/plots/", .y, ".png")))

Pass name of list iteration during map to ggplot

df1 <- mtcars
df2 <- mtcars
combined_mtcars <- list(first_df = df1, second_df = df2)
# make the plots
map(.x = combined_mtcars, .f = function(i) {
ggplot(i, aes(x = hp, y = mpg, group = cyl)) +
geom_line()
})
This generates two charts, one for each df in combined_mtcars.
I would like to add a title to each chart where the title is the name of the iteration, for "first_df" for the first plot and "second_df" for the second one.
Tried:
imap(.x = combined_mtcars, .f = function(i) {
ggplot(i, aes(x = hp, y = mpg, group = cyl)) +
geom_line() +
ggtitle(.y)
})
Which gave an error "Error in .f(.x[[i]], .y[[i]], ...) : unused argument (.y[[i]])
"
How can I pass the iteration name to ggplot?

More compact use of ggplot : grid spaghetti plot

The following code plot the predicted probability of several models against time. Having, all the plots on one graph was not readable so I divided the result in a grid.
I was wondering if it was possible to have only one ggplot with all the models then somehow specify which goes where with grid.arrange
Current :
p2.dat1 <- select(ppf, EXPOSURE, predp.glm.gen,predp.glm1, predp.glm2,predp.glm3,predp.glm4 )
mdf1 <- melt(p2.dat1 , id.vars="EXPOSURE")
plm.plot.all1 <- ggplot(data = mdf1,
aes(x = EXPOSURE, y = value, colour = variable)) +
geom_line()
p2.dat2 <- select(ppf, EXPOSURE, predp.glm.gen, predp.glm5,predp.glm.step )
mdf2 <- melt(p2.dat2 , id.vars="EXPOSURE")
plm.plot.all2 <- ggplot(data = mdf2,
aes(x = EXPOSURE, y = value, colour = variable)) +
geom_line()
grid.arrange(plm.plot.all1, plm.plot.all2, nrow=2)
Expected:
p2.dat <- select(ppf, EXPOSURE, predp.glm.gen,predp.glm1, predp.glm2,predp.glm3,predp.glm4,predp.glm5,predp.glm.step)
mdf <- melt(p2.dat , id.vars="EXPOSURE")
plm.plot.all <- ggplot(data = mdf1,
aes(x = EXPOSURE, y = value, colour = variable)) +
geom_line()
grid.arrange(plm.plot.all[some_selection_somehow], plm.plot.all[same], nrow=2)
Thanks,
You can do this with grid.arrange by writing some helper functions. It can be done more succinctly, but I prefer small focused functions that can be used with pipes.
library(tidyverse)
library(gridExtra)
# Helper Functions ----
plot_function <- function(x) {
ggplot(x, aes(x = EXPOSURE, y = value, colour = variable)) +
geom_line() +
labs(title = unique(x$variable)) +
theme(legend.position = "none")
}
grid_plot <- function(x, selection) {
order <- c(names(x)[grepl(selection,names(x))], names(x)[!grepl(selection,names(x))])
grid.arrange(grobs = x[order], nrow = 2)
}
# Actually make the plot ----
ppf %>%
select(EXPOSURE, predp.glm.gen,predp.glm1, predp.glm2,predp.glm3,predp.glm4,predp.glm5,predp.glm.step) %>%
gather(variable, value, -EXPOSURE) %>%
split(.$variable) %>%
map(plot_function) %>%
grid_plot("predp.glm3")
or you could do this with ggplot, a facet_wrap and factoring the variable column to the proper order. This has the benefits of shared axes across the plots, which facilitates easy comparison. You can alter the helper functions in the first approach to set the axes explicitly to achieve the same effect, but its just easier keeping it in ggplot.
library(tidyverse)
selection <- "predp.glm3"
plot_data <- ppf %>%
select(EXPOSURE, predp.glm.gen,predp.glm1, predp.glm2,predp.glm3,predp.glm4,predp.glm5,predp.glm.step) %>%
gather(variable, value, -EXPOSURE) %>%
mutate(variable = fct_relevel(variable, c(selection, levels(variable)[-grepl(selection, levels(variable))])))
ggplot(plot_data, aes(x = EXPOSURE, y = value, colour = variable)) +
geom_line() +
facet_wrap( ~variable, nrow = 2) +
theme(legend.position = "none")

Resources