I want to arrange N ggplot (each one is facetted) on a grid with grid.arrange.
library(tidyverse)
library(ggplot2)
library(gridExtra)
plots <- lapply(unique(mtcars$cyl), function(cyl) {
data <- mtcars %>% filter(cyl == cyl)
ggplot(data, aes(x=mpg, y=hp))+
geom_point(color = "blue")+
facet_wrap(.~carb)}) %>%
do.call(grid.arrange, .)
do.call(grid.arrange, plots )
The problem is that all the plots are based on the entire dataset and they render the same plot, while they shuold be different as I filter them in line
data <- mtcars %>% filter(cyl == cyl).
filter deals with cyl too letteral and treated as a string, therefore cyl==cyl is TRUE for the entire dataset. You can solve this by unquote cyl using !! or use another variable name in the function e.g. x.
#Option 1
data <- mtcars %>% filter(cyl == !!cyl)
#Option 2
... function(x) {
data <- mtcars %>% filter(cyl == x)
...
Here is a tidyverse approach
library(tidyverse)
group_plots <- mtcars %>%
group_split(cyl) %>%
map(~ggplot(., aes(x = mpg, y = hp))+
geom_point(color = "blue") +
facet_wrap(.~carb))
do.call(gridExtra::grid.arrange, group_plots)
Try use split() first:
library(tidyverse)
library(gridExtra)
l <- split(mtcars, mtcars$cyl) # divide based on cyl in a list
plots <- lapply(l, function(x) {
ggplot(x, aes(x=mpg, y=hp)) +
geom_point(color = "blue") +
facet_wrap(.~carb)
}) # call lapply() on each element
do.call(grid.arrange, plots)
Related
I have made a function:
mtcars_plot_fun <- function(cyl_in, gear_in) {
car_data <- mtcars %>%
filter(cyl == cyl_in,
gear == gear_in)
p <- car_data %>% ggplot(aes(x = disp, y = qsec, fill = gear)) +
geom_point()
print(p)
}
Then a loop that makes plots and stores them in the list object gg_obj
cyls <- c(4, 6)
gears <- c(3, 4)
for (cyl in cyls) {
for (gear in gears) {
gg_obj <- mtcars_plot_fun(cyl_in = cyl, gear_in = gear)
}
}
Now I want to extract the plots I need from gg_obj and use grid.arrange(), gridExtra or similar to plot them.
How can I pull out the plots I want, and store them as unique objects to plot in a grid?
You can use do.call to use your list of plots as arguments for grid.arrange.
For example:
do.call(grid.arrange, list.of.plots, ncol=2)
I created a function that splits a dataframe by group variable 'gear' and makes plots for each new df. How do I change the title for each plot then?
rank20 <- function(df){
sdf <- df
clusterName <- paste0("cluster",sdf$gear)
splitData <- split(sdf,clusterName)
plot <- lapply(splitData, function (x) {ggplot(x, aes(mpg, cyl)) + geom_point()+
labs(x="mpg", y="cyl",
title="This Needs To Be Changed") +
theme_minimal()})
do.call(grid.arrange,plot)
}
rank20(mtcars)
I want the following titles: gear3, gear4, etc. (corresponding to their gear value)
UPD: both results are right if using mtcars. But in my real case, I transform my initial df. So, I should have put my question in the different way. I need to take the titles from splitData df name itself rather than from the column.
I find it easier with lapply to use the indexes as the input. Provides more flexibility if you need to link the list to another element eg the name of the list:
rank20 <- function(df, col="gear"){
sdf <- df
clusterName <- paste0("cluster", sdf[[col]])
splitData <- split(sdf, clusterName)
# do the apply across the splitData indexes instead and pull out cluster
# name from the column
plot <- lapply(seq_along(splitData), function(i) {
X <- splitData[[i]]
i_title <- paste0(col, X[[col]][1])
## to use clusterName instead eg cluster3 instead of gear3:
#i_title <- paste0(col, names(splitData)[i])
ggplot(X, aes(mpg, cyl)) +
geom_point() +
labs(x="mpg", y="cyl", title=i_title) +
theme_minimal()
})
do.call(grid.arrange,plot)
}
rank20(mtcars)
As you say you wanted the name to be gear2, gear3 I've gone with this but hashed out the alternative i_title that uses the clusterName value instead.
In the input of the function you can change the col value to switch to a different column so gear isn't hard-coded
May I suggest a slightly different approach (yet also using the loop over indices as suggested in user Jonny Phelps answer). I am creating a list of plots and then using patchwork::wrap_plots for plotting. I find it smoother.
library(tidyverse)
library(patchwork)
len_ind <- length(unique(mtcars$cyl))
ls_plot <-
mtcars %>%
split(., .$cyl) %>%
map2(1:len_ind, ., function(x, y) {
ggplot(y, aes(mpg, cyl)) +
geom_point() +
labs(x = "mpg", y = "cyl",
title = names(.)[x]
) +
theme_minimal()
})
wrap_plots(ls_plot) + plot_layout(ncol = 1)
Just noticed this was the wrong column - using cyl instead of gear. Oops. It was kind of fun to wrap this into a function:
plot_col <- function(x, col, plotx, ploty){
len_ind <- length(unique(mtcars[[col]]))
x_name <- deparse(substitute(plotx))
y_name <- deparse(substitute(ploty))
ls_plot <-
mtcars %>%
split(., .[[col]]) %>%
map2(1:len_ind, ., function(x, y) {
ggplot(y, aes({{plotx}}, {{ploty}})) +
geom_point() +
labs(x = x_name, y = y_name,
title = names(.)[x]
) +
theme_minimal()
})
wrap_plots(ls_plot) + plot_layout(ncol = 1)
}
plot_col(mtcars, "gear", mpg, cyl)
I would like to generate a series of histograms for all variables in a dataset, but I am clearly not preparing the data correctly for use in the map function.
library(tidyverse)
mtcars %>%
select(wt, disp, hp) %>%
map(., function(x)
ggplot(aes(x = x)) + geom_histogram()
)
I can accomplish this task with a for loop (h/t but am trying to do the same thing within the tidyverse.
foo <- function(df) {
nm <- names(df)
for (i in seq_along(nm)) {
print(
ggplot(df, aes_string(x = nm[i])) +
geom_histogram())
}
}
mtcars %>%
select(wt, disp, hp) %>%
foo(.)
Any help is greatly appreciated.
Something like this would also work:
library(purrr)
library(dplyr)
mtcars %>%
select(wt, disp, hp) %>%
names() %>%
map(~ggplot(mtcars, aes_string(x = .)) + geom_histogram())
or:
mtcars %>%
select(wt, disp, hp) %>%
{map2(list(.), names(.), ~ ggplot(.x, aes_string(x = .y)) + geom_histogram())}
To use purrr::map, you could melt your data frame, and then split it based on the variable name into a list of data frames
library(reshape2)
library(dplyr)
library(ggplot2)
library(purrr)
melt(mtcars) %>%
split(.$variable) %>%
map(., ~ggplot(.x, aes(x=value)) +
geom_histogram())
You can also use ggplot2::facet_wrap to plot them all at once
library(reshape2)
library(dplyr)
library(ggplot2)
melt(mtcars) %>%
ggplot(., aes(x=value, label=variable)) +
geom_histogram() +
facet_wrap(~variable, nrow=ncol(mtcars))
I have the following workflow:
rm(list=ls())
data(mtcars)
attach(mtcars)
library(ggplot2)
library(plyr)
library(dplyr)
library(scales)
library(reshape2)
library(lazyeval)
my_func <- function(x, y) {
test<<-mtcars %>% group_by_(x, y) %>%
summarise(Freq = n()) %>%
mutate(Freq = Freq/sum(Freq))
test
}
my_func('gear', 'cyl')
ggplot(test, aes(x=gear, y=Freq))+
geom_bar(stat="identity", aes(fill=cyl), position=position_dodge(width=0.1))+
scale_y_continuous(labels=percent_format(), limits = c(0,1))
However, the resulting plot does not show the bars next to one another, but rather some on top of one another. What gives and how do I fix this?
You need to convert the cyl column to factor.
test$cyl <- as.factor(test$cyl)
ggplot(test, aes(x=gear, y=Freq))+
geom_bar(stat="identity", aes(fill=cyl), position=position_dodge(width=1))+
scale_y_continuous(labels=percent_format(), limits = c(0,1))
I would like to create one separate plot per group in a data frame and include the group in the title.
With the iris dataset I can in base R and ggplot do this
plots1 <- lapply(split(iris, iris$Species),
function(x)
ggplot(x, aes(x=Petal.Width, y=Petal.Length)) +
geom_point() +
ggtitle(x$Species[1]))
Is there an equivalent using dplyr?
Here's an attempt using facets instead of title.
p <- ggplot(data=iris, aes(x=Petal.Width, y=Petal.Length)) + geom_point()
plots2 = iris %>% group_by(Species) %>% do(plots = p %+% . + facet_wrap(~Species))
where I use %+% to replace the dataset in p with the subset for each call.
or (working but complex) with ggtitle
plots3 = iris %>%
group_by(Species) %>%
do(
plots = ggplot(data=.) +
geom_point(aes(x=Petal.Width, y=Petal.Length)) +
ggtitle(. %>% select(Species) %>% mutate(Species=as.character(Species)) %>% head(1) %>% as.character()))
The problem is that I can't seem to set the title per group with ggtitle in a very simple way.
Thanks!
Use .$Species to pull the species data into ggtitle:
iris %>% group_by(Species) %>% do(plots=ggplot(data=.) +
aes(x=Petal.Width, y=Petal.Length) + geom_point() + ggtitle(unique(.$Species)))
library(dplyr, warn.conflicts = FALSE)
library(ggplot2)
plots3 <- iris %>%
group_by(Species) %>%
group_map(~ ggplot(.) + aes(x=Petal.Width, y=Petal.Length) + geom_point() + ggtitle(.y[[1]]))
length(plots3)
#> [1] 3
# for example, the second plot :
plots3[[2]]
Created on 2021-11-19 by the reprex package (v2.0.1)
This is another option using rowwise:
plots2 = iris %>%
group_by(Species) %>%
do(plots = p %+% .) %>%
rowwise() %>%
do(x=.$plots + ggtitle(.$Species))