I try to get rid of the nesting (upper) facet label. So far I can only apply changes to all facet labels together and not just the grouping ones.
library(tidyverse)
library(ggh4x)
df <- as_tibble(iris) %>%
mutate(
Nester = if_else(Species == "setosa", "Short Leaves", "Long Leaves"),
Nester = factor(Nester)
) %>%
pivot_longer(!c(Species, Nester), names_to = "Measure", values_to = "Value")
ylim <- df %>%
split(.$Nester) %>%
map(., ~ range(.$Value))
df %>%
ggplot(aes(Measure, Value)) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 90, , vjust = .5)) +
facet_nested(~ Nester + Species, scales = "free_y", independent = "y") +
facetted_pos_scales(
y = list(
Species == "versicolor" ~ scale_y_continuous(limits = ylim[[1]]),
Species == "virginica" ~ scale_y_continuous(limits = ylim[[1]], guide = "none"),
Species == "setosa" ~ scale_y_continuous(limits = ylim[[2]])
)
)
Created on 2022-08-23 with reprex v2.0.2
I manually edited the plot above to show the desired plot:
I need a method that works with nested facets, as my actual data is far bigger and more complex.
EDIT: The solution needs to use nested facets.
Update on OP request: This is a solution using the color trick white on white is not visible:
From https://teunbrand.github.io/ggh4x/articles/Facets.html
my_strips <- strip_themed(
# Horizontal strips
background_x = elem_list_rect(fill = c("white", "dodgerblue")),
text_x = elem_list_text(colour = c("white", "white"),
face = c("bold", "bold")),
by_layer_x = TRUE,
text_y = elem_list_text(angle = c(0, 90)),
by_layer_y = FALSE
)
df %>%
pivot_longer(!c(Species, Nester), names_to = "Measure", values_to = "Value") %>%
ggplot(aes(Measure, Value)) +
geom_boxplot() +
facet_nested(~ Nester + Species , scales = "free_y", independent = "y",
strip = my_strips)+
theme(
axis.text.x=element_text(angle=90, hjust=0, vjust=0))
First answer:
We could use just facet_wrap for this. No need for facet_nested:
library(tidyverse)
p <- df %>%
pivot_longer(!c(Species, Nester), names_to = "Measure", values_to = "Value") %>%
ggplot(aes(Measure, Value)) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 90, , vjust = .5)) +
facet_wrap(.~Species)
p
This answer resembles #TarJae's answer, but uses element_blank()s instead. The result is pretty similar, but may be relevant if exporting graphics with a transparent background.
library(tidyverse)
library(ggh4x)
df <- as_tibble(iris) %>%
mutate(
Nester = if_else(Species == "setosa", "Short Leaves", "Long Leaves"),
Nester = factor(Nester)
) %>%
pivot_longer(!c(Species, Nester), names_to = "Measure", values_to = "Value")
ylim <- df %>%
split(.$Nester) %>%
map(., ~ range(.$Value))
strips <- strip_nested(
text_x = list(element_blank(), element_text()),
background_x = list(element_blank(), element_rect()),
by_layer_x = TRUE
)
df %>%
ggplot(aes(Measure, Value)) +
geom_boxplot() +
theme(axis.text.x = element_text(angle = 90, , vjust = .5)) +
facet_nested(
~ Nester + Species, scales = "free_y", independent = "y",
strip = strips
) +
facetted_pos_scales(
y = list(
Species == "versicolor" ~ scale_y_continuous(limits = ylim[[1]]),
Species == "virginica" ~ scale_y_continuous(limits = ylim[[1]], guide = "none"),
Species == "setosa" ~ scale_y_continuous(limits = ylim[[2]])
)
)
Created on 2022-08-23 by the reprex package (v2.0.1)
Related
Is it possible to have a second "free" y-axis specifically for the nester "Short leaves"? I do not want to have an independent y-axis for all 3, just 2 for the respective nesters. How can I do that?
library(tidyverse)
library(ggh4x)
df <- as_tibble(iris) %>%
select(3, 5) %>%
mutate(Nester = if_else(Species == "setosa", "Short Leaves", "Long Leaves"),
Nester = factor(Nester))
df %>%
pivot_longer(!c(Species, Nester), names_to = "Measure", values_to = "Value") %>%
ggplot(aes(Measure, Value)) +
geom_boxplot() +
facet_nested(~ Nester + Species)
EDIT:
So far, I only found those two options that do free y-axis for all 3:
facet_nested(~ Nester + Species, scales = "free_y", independent = "y")
and
facet_nested_wrap(~ Nester + Species, scales = "free_y", nrow = 1)
which do not give the desired result.
There isn't really a good amount of control at the facet levels about which panel uses which y-scale. However, facetted_pos_scales() gives you exactly that control.
Let's suppose we have this plot from your example.
library(tidyverse)
library(ggh4x)
df <- as_tibble(iris) %>%
select(3, 5) %>%
mutate(Nester = if_else(Species == "setosa", "Short Leaves", "Long Leaves"),
Nester = factor(Nester))
p <- df %>%
pivot_longer(!c(Species, Nester), names_to = "Measure", values_to = "Value") %>%
ggplot(aes(Measure, Value)) +
geom_boxplot() +
facet_nested(~ Nester + Species, scales = "free_y", independent = "y")
We can 'fix' a scale by giving it a constant limit, which we'd have to pre-calculate. You can then set that scale to some panels.
ylim <- range(df$Petal.Length[df$Nester == "Long Leaves"])
p + facetted_pos_scales(
y = list(Nester == "Long Leaves" ~ scale_y_continuous(limits = ylim))
)
If you also wish to omit the axis in between the panels with fixed scales, you'd need to set the scales separately for each panel. In the middle panel, you'd have to set guide = "none" to hide the axis.
p + facetted_pos_scales(
y = list(
Species == "versicolor" ~ scale_y_continuous(limits = ylim),
Species == "virginica" ~ scale_y_continuous(limits = ylim, guide = "none")
)
)
Created on 2022-08-19 by the reprex package (v2.0.0)
it is a question with two missing solutions :D
Connect the points with a geom_line per variable
Change the x-axis, so that the 3 years are visible above the model
library(tidyverse)
data <- mtcars%>%
as_tibble(rownames = "model")%>%
mutate(jahr = 2019)%>%
bind_rows(mtcars%>%
as_tibble(rownames = "model")%>%
mutate_if(is.numeric, ~.*0.9)%>%
mutate(jahr = 2020))%>%
bind_rows(mtcars%>%
as_tibble(rownames = "model")%>%
mutate_if(is.numeric, ~.*0.7)%>%
mutate(jahr = 2021))
data%>%
filter(model %in% c("Datsun 710", "Honda Civic", "Valiant"))%>%
gather(variable, wert, -c(model, jahr))%>%
ggplot()+
geom_point(data = ~filter(., jahr == 2019),
aes(x = model, y = wert, col = model),
position = position_nudge(x = -.2))+
geom_point(data = ~filter(., jahr == 2020),
aes(x = model, y = wert, col = model),
position = position_nudge(x = 0))+
geom_point(data = ~filter(., jahr == 2021),
aes(x = model, y = wert, col = model),
position = position_nudge(x = .2))+
facet_wrap(~variable, scales = "free_y")
Is this what you'd like to achieve?
(Also included a possible alternative facet_grid approach.)
library(tidyverse)
# Sample data
data <- mtcars %>%
as_tibble(rownames = "model") %>%
mutate(jahr = 2019) %>%
bind_rows(mtcars %>%
as_tibble(rownames = "model") %>%
mutate_if(is.numeric, ~ . * 0.9) %>%
mutate(jahr = 2020)) %>%
bind_rows(mtcars %>%
as_tibble(rownames = "model") %>%
mutate_if(is.numeric, ~ . * 0.7) %>%
mutate(jahr = 2021))
# Combining model & jahr
data %>%
filter(model %in% c("Datsun 710", "Honda Civic", "Valiant")) %>%
gather(variable, wert, -c(model, jahr)) %>%
mutate(yr_mod = str_c(model, " - ", jahr)) %>%
ggplot(aes(yr_mod, wert, col = model)) +
geom_point() +
geom_line(aes(group = model)) +
facet_wrap(~ variable, scales = "free_y") +
theme(axis.text.x = element_text(angle = 70, hjust = 1),
legend.position = "none") +
labs(x = NULL)
# Alternative presentation with facet_grid
data %>%
filter(model %in% c("Datsun 710", "Honda Civic", "Valiant")) %>%
gather(variable, wert, -c(model, jahr)) %>%
ggplot(aes(jahr, wert, col = model)) +
geom_point() +
geom_line(aes(group = model)) +
facet_grid(variable ~ model, scales = "free_y") +
scale_x_continuous(breaks = c(2019:2021),
expand = expansion(mult = c(0.5, 0.5))) +
theme(legend.position = "none") +
labs(x = NULL)
Created on 2022-06-12 by the reprex package (v2.0.1)
Something like this?
library(tidyverse)
data%>%
filter(model %in% c("Datsun 710", "Honda Civic", "Valiant"))%>%
gather(variable, wert, -c(model, jahr))%>%
ggplot(aes(x=model, y=wert, color = model, group = jahr))+
geom_point(position = position_nudge(), size=5)+
geom_line(color="grey", size=1)+
scale_color_manual(values = c("orange3", "green4", "purple4"), name = NULL) +
facet_wrap(~variable, scales = "free_y")+
theme_minimal(base_size = 20) +
theme(legend.position = "top",
axis.text.x = element_text(angle = 45, hjust=1))
I have an example code below. I have built a figure with ggplot and it is almost there, but I would like to add an additional curve across all facets from y. The final output should look like the image attached. I'm not sure how I would do this.
x <- iris[-1:-3]
bw <- 1
nbin <- 100
y <- head(iris, 50)[2]
ggplot(x, aes(x = Petal.Width)) +
geom_density(aes(y = bw *..count.., fill = Species), size = 1, alpha = 0.4) +
facet_wrap(~Species)+
scale_x_continuous(labels = scales::math_format(10^.x), limits = c(0, 5), expand = c(0,0)) +
scale_y_continuous(expand = c(0,0), limits = c(0, NA)) +
annotation_logticks(sides = "b", short=unit(-1,"mm"), mid=unit(-2,"mm"), long=unit(-3,"mm")) +
coord_cartesian(clip='off') + theme(panel.background = element_blank(),
panel.border = element_rect(colour = "black", fill=NA))
Does this do what you are looking to achieve?
I'm sure there are better ways; basically I've generated three versions of the y data and shuffled the grouping variables to allow ggplot's facet_wrap and fill to manage the appearance.
It would be great if there is way to make one set of data appear in all facets without this repetition.
library(ggplot2)
library(dplyr)
library(tidyr)
x1 <-
x %>%
mutate(var = "Petal width") %>%
rename(val = Petal.Width)
df <-
y %>%
mutate(var = "Sepal width",
spp1 = "setosa",
spp2 = "versicolor",
spp3 = "virginica") %>%
pivot_longer(cols = starts_with("spp"), names_to = "temp", values_to = "Species") %>%
select(-temp) %>%
rename(val = Sepal.Width )%>%
bind_rows(x1) %>%
mutate(g1 = case_when(var == "Sepal width" ~ "all species: sepal width",
TRUE ~ paste0(Species, ": petal width")))
ggplot(df, aes(x = val)) +
geom_density(aes(y = bw *..count.., fill = g1), size = 1, alpha = 0.4) +
facet_wrap(~Species)+
scale_x_continuous(labels = scales::math_format(10^.x), limits = c(0, 5), expand = c(0,0)) +
scale_y_continuous(expand = c(0,0), limits = c(0, NA)) +
annotation_logticks(sides = "b", short=unit(-1,"mm"), mid=unit(-2,"mm"), long=unit(-3,"mm")) +
coord_cartesian(clip='off') + theme(panel.background = element_blank(),
panel.border = element_rect(colour = "black", fill=NA))
Created on 2020-07-01 by the reprex package (v0.3.0)
I am working a making som nice plots that I can copy-paste when needed (that is why I have included so many options). So I have this plot:
library(tidyverse)
mtcars %>%
group_by(cyl) %>%
summarise(n=n()) %>%
ungroup() %>%
mutate(cars = "cars") %>%
ggplot(aes(x = as.factor(cars), y = n, fill=as.factor(cyl))) +
geom_bar(stat="identity", width = .3) +
geom_text(aes(label = paste0(round(n, digits = 0), "stk.")),
position = position_stack(vjust = 0.5)) +
labs(title = "Number of cars with cylinders in the data set",
subtitle= "If needed",
caption= "Fodnote",
x= "", y="Antal",
fill="# of cylinders") +
theme(#legend.position="none",
plot.caption = element_text(hjust = 0))
How can I reorder the stacks so e.g. the blue is at the bottom, then the red stack and the green stack on top.
Thanks. I think the solution involes forcats...
Is this what you're looking for? To change the fill color, use scale_fill_manual() or scale_fill_brewer()
library(tidyverse)
library(forcats)
mtcars %>%
group_by(cyl) %>%
summarise(n=n()) %>%
ungroup() %>%
mutate(cars = "cars",
cars = factor(cars),
cyl = factor(cyl)) %>%
# use fct_reorder here
mutate(cyl = fct_reorder(cyl, n)) %>%
ggplot(aes(x = cars, y = n, fill = cyl)) +
geom_col(width = .3) +
geom_text(aes(label = paste0(round(n, digits = 0), "stk.")),
position = position_stack(vjust = 0.5)) +
labs(title = "Number of cars with cylinders in the data set",
subtitle = "If needed",
caption = "Footnote",
x = "", y = "Antal",
fill = "# of cylinders") +
theme(#legend.position="none",
plot.caption = element_text(hjust = 0))
To define order, convert cyl to factor with desired levels.
df1 = mtcars
df1$cyl = factor(df1$cyl, levels = c(6, 4, 8))
df1 %>%
group_by(cyl) %>%
summarise(n=n()) %>%
ungroup() %>%
mutate(cars = "cars") %>%
ggplot(aes(x = as.factor(cars), y = n, fill=as.factor(cyl))) +
#scale_fill_manual(values=c("green", "red", "blue")) +
geom_bar(stat="identity", width = .3) +
geom_text(aes(label = paste0(round(n, digits = 0), "stk.")),
position = position_stack(vjust = 0.5)) +
labs(title = "Number of cars with cylinders in the data set",
subtitle= "If needed",
caption= "Fodnote",
x= "", y="Antal",
fill="# of cylinders") +
theme(#legend.position="none",
plot.caption = element_text(hjust = 0))
I have a plot i wish to add another layer to
Th plot is below. I want to overlay another polar plot on it to see that the numbers "match up"
In the example below I have create the plot for one species of the iris dataset. I would like to overlay another plot of a different species
Thank you for your time
library(ggplot2)
library(dplyr)
mydf <- iris
plot.data <- tidyr::gather(mydf,key = attribute ,value = avg_score, Sepal.Length:Petal.Width)
plot.data <- plot.data %>%
filter(Species == 'setosa') %>%
group_by(attribute) %>%
summarise(attr_mean = mean(avg_score))
ggplot(plot.data, aes(x=attribute, y = attr_mean, col = attribute)) +
geom_bar(stat = "identity", fill = 'white') +
coord_polar(theta = "x") +
theme_bw()
This is quite the pedestrian way of doing things.
plot.setosa <- plot.data %>%
filter(Species == 'setosa') %>%
group_by(attribute) %>%
summarise(attr_mean = mean(avg_score))
plot.virginica <- plot.data %>%
filter(Species == 'virginica') %>%
group_by(attribute) %>%
summarise(attr_mean = mean(avg_score))
ggplot(plot.setosa, aes(x=attribute, y = attr_mean, col = attribute)) +
geom_bar(stat = "identity", fill = 'blue', alpha = 0.25) +
geom_bar(data = plot.virginica, stat = "identity", fill= "green", alpha = 0.25,
aes(x = attribute, y = attr_mean, col = attribute)) +
coord_polar(theta = "x") +
theme_bw()
And a slightly less pedestrian.
xy <- plot.data %>%
group_by(Species, attribute) %>%
summarise(attr_mean = mean(avg_score))
ggplot(xy, aes(x = attribute, y = attr_mean, color = attribute, fill = Species)) +
theme_bw() +
geom_bar(stat = "identity", alpha = 0.25) +
coord_polar(theta = "x")