I would like to reverse the order of the legend for a horizontal bar chart. When adding guides(fill = guide_legend(reverse = TRUE)) to the ggplot it works fine (see second plot). However, after applying ggplotly() the legend is again in the default order.
How to reverse the order of the plotly legend without changing the order of the bars?
library(ggplot2)
library(dplyr)
data(mtcars)
p1 <- mtcars %>%
count(cyl, am) %>%
mutate(cyl = factor(cyl), am = factor(am)) %>%
ggplot(aes(cyl, n, fill = am)) +
geom_col(position = "dodge") +
coord_flip()
p1
p2 <- p1 + guides(fill = guide_legend(reverse = TRUE))
p2
plotly::ggplotly(p2)
Adding to the great answer of #Zac Garland here is a solution that works with legends of arbitrary length:
library(ggplot2)
library(dplyr)
reverse_legend_labels <- function(plotly_plot) {
n_labels <- length(plotly_plot$x$data)
plotly_plot$x$data[1:n_labels] <- plotly_plot$x$data[n_labels:1]
plotly_plot
}
p1 <- mtcars %>%
count(cyl, am) %>%
mutate(cyl = factor(cyl), am = factor(am)) %>%
ggplot(aes(cyl, n, fill = am)) +
geom_col(position = "dodge") +
coord_flip()
p2 <- mtcars %>%
count(am, cyl) %>%
mutate(cyl = factor(cyl), am = factor(am)) %>%
ggplot(aes(am, n, fill = cyl)) +
geom_col(position = "dodge") +
coord_flip()
p1 %>%
plotly::ggplotly() %>%
reverse_legend_labels()
p2 %>%
plotly::ggplotly() %>%
reverse_legend_labels()
When you call ggplotly, it's really just creating a list and a function call on that list.
So if you save that intermediate step, you can modify the list directly. and as such, modify the plot output.
library(ggplot2)
library(dplyr)
data(mtcars)
p1 <- mtcars %>%
count(cyl, am) %>%
mutate(cyl = factor(cyl), am = factor(am)) %>%
ggplot(aes(cyl, n, fill = am)) +
geom_col(position = "dodge") +
coord_flip()
html_plot <- ggplotly(p1)
replace_1 <- html_plot[["x"]][["data"]][[2]]
replace_2 <- html_plot[["x"]][["data"]][[1]]
html_plot[["x"]][["data"]][[1]] <- replace_1
html_plot[["x"]][["data"]][[2]] <- replace_2
html_plot
plot output
A simple solution is to define the order of the levels of the factor variable am:
library(ggplot2)
library(dplyr)
data(mtcars)
df <- mtcars %>%
count(cyl, am) %>%
mutate(cyl = factor(cyl), am = factor(as.character(am), levels = c("1", "0")))
head(df)
p1 <- df %>%
ggplot(aes(cyl, n, fill = am)) +
geom_col(position = "dodge") +
coord_flip()
p1
plotly::ggplotly(p1)
Related
I am having trouble placing the p-values in the correct position on the y axis of a ggplot using rstatix. I can get the example provided on the package author's blog to work fine, but when I change the values, the positions are incorrect. Here is the working version:
library(tidyverse)
library(rstatix)
##Example provided by the package author which works correctly
df <- ToothGrowth%>%
as_tibble()
#Check df
df
#Stats calculation
stat.test <- df %>%
group_by(dose) %>%
t_test(len ~ supp) %>%
adjust_pvalue(method = "bonferroni") %>%
add_significance()
# Make facet and add p-values
stat.test <- stat.test %>% add_xy_position(x = "supp", fun = "max")
#Check p value positions - y.position looks good
stat.test
#Plot
ggplot(df, aes(x = supp, y = len)) +
geom_boxplot() +
geom_jitter() +
facet_wrap( ~ dose, scales = "free") +
stat_pvalue_manual(stat.test, hide.ns = F,
label = "{p.adj}")
However, when I change the values, the position of the p values are too high.
## My example which plots incorrectly
##--- This is a very inelegant way to change the values!!
df <- ToothGrowth %>%
mutate(helper = paste0(supp, dose))
df$RecordingNo <- ave(seq.int(nrow(df)), df$helper, FUN = seq_along)
df <- df %>%
select(-helper) %>%
pivot_wider(names_from = c(dose), values_from = len) %>%
mutate(`0.5` = `0.5` * 0.1) %>%
mutate(`2` = `2` * 10) %>%
select(-RecordingNo) %>%
pivot_longer(-supp) %>%
rename(len = value, dose = name) %>%
mutate(dose = as_factor(dose)) %>%
as_tibble()
#Check df
df
##------
#This code is exactly the same as the working code above.
#Stats calculation
stat.test <- df %>%
group_by(dose) %>%
t_test(len ~ supp) %>%
adjust_pvalue(method = "bonferroni") %>%
add_significance()
# Make facet and add p-values
stat.test <- stat.test %>% add_xy_position(x = "supp", fun = "max")
#Check p value positions - y.position looks incorrect
stat.test
ggplot(df, aes(x = supp, y = len)) +
geom_boxplot() +
geom_jitter() +
facet_wrap( ~ dose, scales = "free") +
stat_pvalue_manual(stat.test, hide.ns = F,
label = "{p.adj}")
I guess there is a difference in the second dataframe which is causing the problems, but I can't figure it out. Thanks!
Like the scales option on facet_wrap, there is a scales option on add_xy_position that controls the p value position . As I am using "facet_wrap(...,scales = "free")" I should use add_xy_position(...,scales = "free") to make sure the positions match.
In my example:
stat.test <- stat.test %>% add_xy_position(x = "supp", fun = "max",scales = "free")
ggplot(df, aes(x = supp, y = len)) +
geom_boxplot() +
geom_jitter() +
facet_wrap( ~ dose, scales = "free") +
stat_pvalue_manual(stat.test, hide.ns = F,
label = "{p.adj}")
Answer from author's Github page.
df <- read.csv ('https://raw.githubusercontent.com/ulklc/covid19-
timeseries/master/countryReport/raw/rawReport.csv',
stringsAsFactors = FALSE)
How to create a pie chart of the death, confirmed and recovered fields in this data set by region.
perfect for a tidyverse
library(tidyverse)
df %>%
as_tibble() %>%
select(region, confirmed, recovered, death) %>%
gather(type, value, -region) %>%
group_by(region,type) %>%
summarise(value= sum(value)) %>%
ggplot(aes(x="", value, fill =region)) +
geom_col(position = position_fill(), color="white") +
ggrepel::geom_text_repel(aes(label = region), direction = "y",
position = position_fill(vjust = 0.5)) +
coord_polar(theta = "y") +
scale_fill_discrete("") +
facet_wrap(~type) +
theme_void() +
theme(legend.position = "bottom")
For labels I used function geom_text_repel from ggrepel package to easily avoid overplotting.
I would like to create a ggplot facet grid where the x axis of the grid (not the plot) are the labels, rather than squish into each chartlet.
Example:
mtcars %>%
group_by(mpg) %>%
mutate(cnt = n()) %>%
ggplot(aes(x = cyl, y = cnt)) +
geom_bar(stat = "identity") +
facet_grid(vs ~ cyl)
Looks like:
Rather than having 3 through 9 on each individual chart, I would like the horizontal part of the grid to be cyl as opposed to each individual chart.
In other words, each bar chart should be a single column bar chart only.
How can I do this?
Use a constant for the x value, while still faceting by cyl:
library(tidyverse)
mtcars %>%
group_by(mpg) %>%
mutate(cnt = n()) %>%
ggplot(aes(x = 1, y = cnt)) +
geom_bar(stat = "identity") +
facet_grid(vs ~ cyl) +
theme(axis.text.x = element_blank(),
axis.ticks.x = element_blank()) +
labs(x = "cyl")
Like this?
mtcars %>%
group_by(mpg) %>%
mutate(cnt = n()) %>%
ggplot(aes(x = cyl, y = cnt)) +
geom_bar(stat = "identity") +
facet_grid(vs ~ cyl)+
theme(axis.text.x = element_blank(),axis.ticks = element_blank())
Or like this?
mtcars %>%
group_by(mpg) %>%
mutate(cnt = n()) %>%
ggplot(aes(x = cyl, y = cnt)) +
geom_bar(stat = "identity") +
facet_grid(vs ~ cyl, scales="free")+
theme(axis.text.x = element_blank(),axis.ticks = element_blank())
How does one plot "filled" bars with counts labels using ggplot2?
I'm able to do this for "stacked" bars. But I'm very confused otherwise.
Here is a reproducible example using dplyr and the mpg dataset
library(ggplot)
library(dplyr)
mpg_summ <- mpg %>%
group_by(class, drv) %>%
summarise(freq = n()) %>%
ungroup() %>%
mutate(total = sum(freq),
prop = freq/total)
g <- ggplot(mpg_summ, aes(x = class, y = prop, group = drv))
g + geom_col(aes(fill = drv)) +
geom_text(aes(label = freq), position = position_stack(vjust = .5))
But if I try to plot counts for filled bars it does not work
g <- ggplot(mpg_summ, aes(x=class, fill=drv))
g + stat_count(aes(y = (..count..)/sum(..count..)), geom="bar", position="fill") +
scale_y_continuous(labels = percent_format())
Further, if I try:
g <- ggplot(mpg_summ, aes(x=class, fill=drv))
g + geom_bar(aes(y = freq), position="fill") +
geom_text(aes(label = freq), position = "fill") +
scale_y_continuous(labels = percent_format())
I get:
Error: stat_count() must not be used with a y aesthetic.
I missed the fill portion from the last question. This should get you there:
library(ggplot2)
library(dplyr)
mpg_summ <- mpg %>%
group_by(class, drv) %>%
summarise(freq = n()) %>%
ungroup() %>%
mutate(total = sum(freq),
prop = freq/total)
g <- ggplot(mpg_summ, aes(x = class, y = prop, group = drv))
g + geom_col(aes(fill = drv), position = 'fill') +
geom_text(aes(label = freq), position = position_fill(vjust = .5))
Consider the following code that makes a bar chart with a purple color palette
library(dplyr)
library(ggplot2)
dd <- mpg %>%
group_by(manufacturer, cyl) %>%
summarise(n = n()) %>%
ungroup()
mm <- dd %>%
group_by(manufacturer) %>%
summarise(mcyl = weighted.mean(cyl, n)) %>%
arrange(mcyl) %>%
ungroup()
dd %>% left_join(mm) %>%
ggplot(mapping = aes(x = reorder(manufacturer, mcyl), y = n, fill = factor(cyl))) +
geom_bar(stat = "identity", position = "fill") +
coord_flip() +
scale_fill_brewer(palette = "Purples")
Question: How can I make the palette for Audi red ("Reds") and for Ford blue ("Blues"), while keeping the others purple ("Purples")?
What is the most convenient (preferably tidyverse) way to put these red/blue/purple palettes in a variable and passing it to scale_fill_manual() (as explained in this related Q&A)?
Full working solution:
cyl <- sort(unique(mpg$cyl))
ncat <- length(cyl) # 4 types of cylinders
# create palettes
library(RColorBrewer)
purples <- tibble(cyl, colr = brewer.pal(ncat, "Purples"))
reds <- tibble(manufacturer = "audi", cyl, colr = brewer.pal(ncat, "Reds"))
blues <- tibble(manufacturer = "ford", cyl, colr = brewer.pal(ncat, "Blues"))
# merge them with the data
dd_p <- dd %>% filter(!(manufacturer %in% c("audi", "ford"))) %>% left_join(purples)
dd_r <- dd %>% filter(manufacturer == "audi") %>% left_join(reds)
dd_b <- dd %>% filter(manufacturer == "ford") %>% left_join(blues)
gg_dd <- rbind(dd_p, dd_r, dd_b) %>%
left_join(mm)
gg_dd %>%
ggplot(mapping = aes(x = reorder(manufacturer, mcyl), y = n, fill = colr)) +
geom_bar(stat = "identity", position = "fill") +
coord_flip() +
scale_fill_identity()