Explicitly set panel size (not just plot size) in ggplot2 - r

Is it possible to explicitly set the panel size (i.e., the gray grid panel) in a ggplot? I imagine (but can't find) that there is some ggplot extension that allows for arguments that resemble panel.width = unit(3, "in"), panel.height = unit(4, "in").
I have seen solutions for setting the size of the entire plot, or of getting multiple plots to align using the egg package. But nothing that would let me explicitly set the size of the panel.
library(dplyr)
library(ggplot2)
library(tibble)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
p_short <-
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip()
p_short

The ggh4x package has a similar function to the egg solution presented in the other answer. A slight convenience is that the plot is still a valid ggplot after using the function, so it would work with ggsave() and other layers can be added afterwards. (disclaimer: I wrote ggh4x)
library(dplyr)
library(ggplot2)
library(tibble)
library(ggh4x)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip() +
force_panelsizes(rows = unit(4, "in"),
cols = unit(3, "in"))
Created on 2021-04-21 by the reprex package (v1.0.0)

You can use set_panel_size() function from the egg package
library(tibble)
library(dplyr)
library(ggplot2)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
p_short <-
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip()
library(egg)
library(grid)
p_fixed <- set_panel_size(p_short,
width = unit(10, "cm"),
height = unit(4, "in"))
grid.newpage()
grid.draw(p_fixed)
Created on 2018-11-13 by the reprex package (v0.2.1.9000)

Related

Define axis dimensions manually in ggplot in R [duplicate]

Is it possible to explicitly set the panel size (i.e., the gray grid panel) in a ggplot? I imagine (but can't find) that there is some ggplot extension that allows for arguments that resemble panel.width = unit(3, "in"), panel.height = unit(4, "in").
I have seen solutions for setting the size of the entire plot, or of getting multiple plots to align using the egg package. But nothing that would let me explicitly set the size of the panel.
library(dplyr)
library(ggplot2)
library(tibble)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
p_short <-
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip()
p_short
The ggh4x package has a similar function to the egg solution presented in the other answer. A slight convenience is that the plot is still a valid ggplot after using the function, so it would work with ggsave() and other layers can be added afterwards. (disclaimer: I wrote ggh4x)
library(dplyr)
library(ggplot2)
library(tibble)
library(ggh4x)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip() +
force_panelsizes(rows = unit(4, "in"),
cols = unit(3, "in"))
Created on 2021-04-21 by the reprex package (v1.0.0)
You can use set_panel_size() function from the egg package
library(tibble)
library(dplyr)
library(ggplot2)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
p_short <-
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip()
library(egg)
library(grid)
p_fixed <- set_panel_size(p_short,
width = unit(10, "cm"),
height = unit(4, "in"))
grid.newpage()
grid.draw(p_fixed)
Created on 2018-11-13 by the reprex package (v0.2.1.9000)

ggplot with stacked bar chart ordered by a separate variable

I am trying to create a "order" stacked bar chart that each stack is colored by one variable and ordered by another variable, please find my example as below:
library(ggplot2)
library(dplyr)
data(iris)
chart.df.st00 <- iris %>%
as_tibble %>%
mutate(`Sepal.Length`=round(`Sepal.Length`)) %>%
count(Species,`Sepal.Length`) %>%
mutate(`Sepal.Length`=as.character(`Sepal.Length`)) %>%
group_by(Species) %>%
mutate(percent=n/sum(n)*100) %>%
arrange(desc(n)) %>%
mutate(rank=1:n()) %>%
ungroup %>%
mutate(rank=paste(Species,rank,sep='-'))
chart.df.st01 <- chart.df.st00 %>%
left_join(chart.df.st00 %>%
distinct(`Sepal.Length`) %>%
mutate(color=colorRampPalette(
RColorBrewer::brewer.pal(length(unique(chart.df.st00$`Sepal.Length`)),'Set1'))(length(unique(chart.df.st00$`Sepal.Length`)))))
chart.color1.st00 <- chart.df.st01 %>%
distinct(rank,color) %>%
arrange(rank)
chart.color1.st01 <- chart.color1.st00$color
names(chart.color1.st01) <- chart.color1.st00$rank
chart1 <- ggplot(data=chart.df.st01,
aes(x=1,y=percent)) +
geom_bar(aes(fill=rank),stat='identity') +
scale_fill_manual(values=chart.color1.st01) +
facet_wrap(.~Species,ncol = 1) +
scale_y_reverse(breaks=c(0,25,50,75,100),labels=c(100,75,50,25,0)) +
coord_flip()
chart.color2.st00 <- chart.df.st01 %>%
distinct(color,Sepal.Length) %>%
arrange(Sepal.Length)
chart.color2.st01 <- chart.color2.st00$color
names(chart.color2.st01) <- chart.color2.st00$`Sepal.Length`
chart2 <- ggplot(data=chart.df,
aes(x=1,y=percent)) +
geom_bar(aes(fill=`Sepal.Length`),stat='identity') +
scale_fill_manual(values=chart.color2.st01) +
facet_wrap(.~Species,ncol = 1) +
coord_flip()
In my example, each stack is filled by Sepal.Length, and order by rank, chart1 has the ordering of the stacks I want, but not the legend, while chart2 has the legend I want, but not the ordering of the stacks.
Is there a way to have a single chart with the stacked bar of chart1 and the legend of chart2?
Thanks!
Using the code for your second chart this could be achieved by additionally mapping rank on the group aes:
library(ggplot2)
library(dplyr)
data(iris)
chart.df.st00 <- iris %>%
as_tibble %>%
mutate(`Sepal.Length`=round(`Sepal.Length`)) %>%
count(Species,`Sepal.Length`) %>%
mutate(`Sepal.Length`=as.character(`Sepal.Length`)) %>%
group_by(Species) %>%
mutate(percent=n/sum(n)*100) %>%
arrange(desc(n)) %>%
mutate(rank=1:n()) %>%
ungroup %>%
mutate(rank=paste(Species,rank,sep='-'))
chart.df.st01 <- chart.df.st00 %>%
left_join(chart.df.st00 %>%
distinct(`Sepal.Length`) %>%
mutate(color=colorRampPalette(
RColorBrewer::brewer.pal(length(unique(chart.df.st00$`Sepal.Length`)),'Set1'))(length(unique(chart.df.st00$`Sepal.Length`)))))
#> Joining, by = "Sepal.Length"
chart.color2.st00 <- chart.df.st01 %>%
distinct(color,Sepal.Length) %>%
arrange(Sepal.Length)
chart.color2.st01 <- chart.color2.st00$color
names(chart.color2.st01) <- chart.color2.st00$`Sepal.Length`
ggplot(data=chart.df.st01,
aes(x=1,y=percent)) +
geom_bar(aes(fill=`Sepal.Length`, group = rank), stat='identity') +
scale_fill_manual(values = chart.color2.st01) +
facet_wrap(.~Species,ncol = 1) +
scale_y_reverse(breaks=c(0,25,50,75,100),labels=c(100,75,50,25,0)) +
coord_flip()

Keeping dodge position in boxplot passed to plotly

I have a regular boxplot in ggplot2:
# working example
library(ggplot2)
mtcars %>%
mutate(cyl=as.factor(cyl)) %>%
mutate(vs=as.factor(vs)) %>%
ggplot(aes(y=mpg, x=cyl)) +
geom_boxplot(aes(colour=vs))
It looks like this:
However, when I create an object and pass it to plotly, I lose the dodge position:
library(plotly)
mtcars_boxplot <-
mtcars %>%
mutate(cyl=as.factor(cyl)) %>%
mutate(vs=as.factor(vs)) %>%
ggplot(aes(y=mpg, x=cyl)) +
geom_boxplot(aes(colour=vs))
mtcars_boxplot %>%
ggplotly()
It looks like this:
I tried to add position=position_dodge() & position=position_dodge2() but none of them worked:
library(plotly)
mtcars_boxplot <-
mtcars %>%
mutate(cyl=as.factor(cyl)) %>%
mutate(vs=as.factor(vs)) %>%
ggplot(aes(y=mpg, x=cyl)) +
geom_boxplot(aes(colour=vs), position=position_dodge2())
mtcars_boxplot %>%
ggplotly()
What should I do to keep the dodge position like the first plot?
As suggested here, add layout(boxmode = "group")
library(plotly)
mtcars_boxplot %>%
ggplotly() %>%
layout(boxmode = "group")

Maintain ggplot panel size while axis labels change length

How can I make the panels of separate ggplots align when the y-axis labels change in length across plots? Below I've saved two subsets of mtcars with longer and shorter model names. Although the overall plots are the same size, the panels are smaller in the mt_long plot because the y-axis labels take up more of the plot.
library(dplyr)
library(ggplot2)
ds_mt <- mtcars %>% rownames_to_column("model")
mt_short <- ds_mt %>% arrange(nchar(model)) %>% slice(1:4)
mt_long <- ds_mt %>% arrange(-nchar(model)) %>% slice(1:4)
plot_short <-
mt_short %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip()
plot_long <-
mt_long %>%
ggplot(aes(x = model, y = mpg)) +
geom_col() +
coord_flip()
plot_short
plot_long
For this reprex, it is important that the plots be separate. Is there any way to set just the panel dimensions of the plot rather than the overall size of the plot?
We can use gridarrange from the egg package
library(egg)
ggarrange(plot_short, plot_long, ncol = 1)
To save, use
gg <- ggarrange(plot_short, plot_long, ncol = 1)
ggsave("file.png", gg)
try egg::set_panel_size(plot_short)

Apply a ggplot-function per group with dplyr and set title per group

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

Resources