Related
Here is my data which produces a heat map. What I am hoping to do is produce multiple difference heatmaps with an outline around each of x categories.
data <- data.frame(id=c("john","john","john","kate","kate","kate","chris","chris","chris"),
group=c("geo","his","math","geo","his","math","geo","his","math"),
grade=c(65,76,87,67,89,98,99,97,96),
class=c("A","A","A","A","A","A","B","B","B"))
data
mine.heatmap <- ggplot(data = data, mapping = aes(x = id, y = group, fill = grade)) +
geom_tile() +
xlab(label = "id") +
ylab(label="group") +
labs(fill="grade")+
scale_fill_gradient2(low = "#800080",
high = "#FF8C00",mid = "white")
x <- mine.heatmap + facet_grid(
cols = vars(class), scales = "free", space = "free"
)
x + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1, size = 18, margin = margin(b=2)))+
theme(axis.text.y= element_text(angle = 0, vjust = 0.5, hjust=1, size = 18)) +
theme(legend.text = element_text(size=14))+
theme(legend.title = element_text(size=14))+
theme(strip.text = element_text(size=14))+
theme(axis.title.x = element_text(size=18)) +theme(axis.title.y = element_text(size=18))
Original Heat map:
What I am hoping to get are the following heatmaps:
One option to achieve your desired result would be to
put your plotting code in a function which takes as one argument the id for which you want to draw a outline.
Use some data wrangling to convert the categories to be plotted on the x and y aes to numerics per facet variable.
Add a geom_rect to your plotting code to draw the outline which uses the numerics computed in step 2.
library(ggplot2)
library(dplyr)
mine_heatmap <- function(x) {
p <- ggplot(data = data, mapping = aes(x = id, y = group, fill = grade)) +
geom_tile() +
# Add outline via a geom_rect
geom_rect(
data = subset(data, id == x),
aes(
xmin = id_num - .5, xmax = id_num + .5,
ymin = min(group_num) - .5, ymax = max(group_num) + .5
), fill = NA, color = "black", size = 1
) +
labs(x = "id", y = "group", fill = "grade") +
scale_fill_gradient2(
low = "#800080",
high = "#FF8C00", mid = "white"
)
p <- p + facet_grid(
cols = vars(class), scales = "free", space = "free"
)
p + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1, size = 18, margin = margin(b = 2))) +
theme(axis.text.y = element_text(angle = 0, vjust = 0.5, hjust = 1, size = 18)) +
theme(legend.text = element_text(size = 14)) +
theme(legend.title = element_text(size = 14)) +
theme(strip.text = element_text(size = 14)) +
theme(axis.title.x = element_text(size = 18)) + theme(axis.title.y = element_text(size = 18))
}
# Convert id and group to numerics per facet variable
data <- data |>
group_by(class) |>
mutate(
id_num = as.numeric(factor(id)),
group_num = as.numeric(factor(group))
) |>
ungroup()
mine_heatmap("john")
mine_heatmap("kate")
mine_heatmap("chris")
I am plotting my data and want to display totals on top of each bar but as soon as I add total count the bars disappear.
long<- data.frame(
Name = c("abc","abc","abc","gif","gif","gif","xyz","xyz","xyz"),
variable = c("a","b","c","a","b","c","c","b","a"),
value = c(4,6,NA,2,8,1,6,NA,NA))
Code
p<-long %>%
ggplot() + aes(Name, value, fill=variable) +
geom_bar(stat="summary", position = "fill") +
scale_y_continuous(labels = scales::percent_format()) +
ylab("Total_num") +
ggtitle("Totalnum") +
theme(plot.title = element_text(size = 20, hjust = 0.5)) +
theme(axis.text.x = element_text(angle = 75, vjust = 0.95, hjust=1))
p+ stat_summary(fun.y = sum, aes(label = ..y.., group = Name)+
geom_text(stat='value', aes(group=Name, label=.."value"..), position = position_stack(vjust = 0.5))
You can achieve that creating another df with the sum of value per Name and passing this to geom_text()
long<- data.frame(
Name = c("abc","abc","abc","gif","gif","gif","xyz","xyz","xyz"),
variable = c("a","b","c","a","b","c","c","b","a"),
value = c(4,6,NA,2,8,1,6,NA,NA))
long_totals <- long %>%
group_by(Name) %>%
summarise(Total = sum(value, na.rm = T))
p <- ggplot()+
geom_bar(data = long,
aes(x = Name,
y = value,
fill=variable),
stat="summary",
position = "fill") +
geom_text(data = long_totals,
aes(y = 100,
x = Name,
label = Total),
size = 7,
position = position_fill(vjust = 1.02)) +
scale_y_continuous(labels = scales::percent_format()) +
ylab("Total_num") +
ggtitle("Totalnum") +
theme(plot.title = element_text(size = 20, hjust = 0.5)) +
theme(axis.text.x = element_text(angle = 75, vjust = 0.95, hjust=1))
I have my labels roughly aligned to each side of my stacked bar chart. The problem is that they look like a mess because they aren't right and left justified on either side of the bar. How do I fix this so that they look professional?
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
)
# Sort order
level_order <- df3 %>%
arrange(desc(Amount))
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = -3.1,
size = 5) +
geom_text(aes(label = Label),
position = position_stack(vjust = 0.5),
hjust = 5,
size = 5) +
theme(legend.position = "none") +
theme(plot.title = element_text(size = 50, hjust = .5, vjust = 0)) +
ggtitle("Food Costs by Reindeer")
hjust determines the text alignment (with 0 being left-aligned, and 1 right-aligned). The x co-ordinate of your geom_text at the moment is defaulted to 1, so changing this will change the position of the text.
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(x=0.6, label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = 0.5,
size = 5) +
geom_text(aes(x=1.4, label = Label),
position = position_stack(vjust = 0.5),
hjust = 0.5,
size = 5) +
theme(legend.position = "none") +
theme(plot.title = element_text(size = 50, hjust = .5, vjust = 0)) +
ggtitle("Food Costs by Reindeer")
You can also pass hjust as an aesthetic. In order to do that, you will need to prepare the labelling as a separate data frame. Then, you only need to call geom_text once. I don't say this is necessarily better, but just pointing out that this is possible. A few more comments in the code, also regarding a few common pitfalls.
library(tidyverse)
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
) %>%
## arrange step here
arrange(desc(Amount))
## I like to prepare the data outside ggplot
label_df <- df3 %>%
mutate(Amount_lab = paste0("$", Amount)) %>%
pivot_longer(-Amount) %>%
## this adds a column for your adjustment, and the x position compared with the central column
mutate(hjust = rep(0:1, nrow(.)/2),
x = rep(c(1.21, .79), nrow(.)/2))
ggplot(mapping = aes(y = Amount)) +
## geom_col is geom_bar(stat = "identity"), stack is default, so you can omit it
## call data in the geom layers
## set x to 1
## width = .4 so it matches your selected x from above
geom_col(data = df3, aes(x = 1, fill=fct_inorder(Label)), width = .4) +
scale_fill_brewer(palette = "Blues", direction = -1) +
## need to reverse both y and value, weirdly
geom_text(data = label_df, aes(x, y = rev(Amount), label = rev(value),
## this is the main trick
hjust = hjust),
position = position_stack(vjust = 0.5) ) +
## sadly, need to turn clip off
coord_cartesian(clip = "off") +
theme_void() +
## call theme only once!!
theme(legend.position = "none",
plot.title = element_text(size = 20, hjust = .5, vjust = 0),
## you need to add a margin
plot.margin = margin(r = .6, l = .6, unit = "in")) +
ggtitle("Food Costs by Reindeer")
Created on 2021-12-20 by the reprex package (v2.0.1)
Try fixing the x co-ordinate in the call to geom_text and managing alignment with hjust...
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
)
library(ggplot2)
library(dplyr)
library(forcats)
level_order <- df3 %>%
arrange(desc(Amount))
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(x = 1.3, label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = 0,
size = 5) +
geom_text(aes(x = 0.6, label = Label),
position = position_stack(vjust = 0.5),
hjust = 0,
size = 5) +
theme(legend.position = "none") +
theme(plot.title = element_text(size = 50, hjust = .5, vjust = 0)) +
ggtitle("Food Costs by Reindeer")
Created on 2021-12-19 by the reprex package (v2.0.1)
Using Peter's answer above (reminding me of the "x" position argument I forgot existed), this was the final fix that got what I was looking for. hjust = 0 is left-justification and hjust = 1 is right justification.
library(tidyverse)
library(grid)
df3 <- data.frame(
Label = c("Dasher", "Dancer", "Comet", "Cupid", "Prancer", "Blitzen", "Rudolph"),
Amount = c(650.01, 601.01, 340.05, 330.20, 260.01, 250.80, 10.10)
)
# Sort order
level_order <- df3 %>%
arrange(desc(Amount))
ggplot(level_order, aes(fill=fct_inorder(Label), y=Amount, x="")) +
geom_bar(position="stack", stat="identity", width = 0.55) +
scale_fill_brewer(palette = "Blues", direction = -1) +
theme_void() +
geom_text(aes(x = 1.3, label = paste0("$", Amount)),
position = position_stack(vjust = 0.5),
hjust = 0,
size = 5) +
geom_text(aes(x = 0.7, label = Label),
position = position_stack(vjust = 0.5),
hjust = 1,
size = 5) +
theme(legend.position = "none",
plot.margin = unit(c(0,0,2,0), "cm"))
grid.text("Food Costs by Reindeer", x = unit(0.5, "npc"), y = unit(0, "npc"),
vjust = -0.5, gp = gpar(cex=4))
I am trying to create a plot like the one below. I'd like the order the points in each category in such a way that they form an s-shape. Is it possible to do this in ggplot?
Similar data available here
What I have so far:
somatic.variants <- read.delim("data/Lawrence.S2.txt", stringsAsFactors=T)
cancer_rates <- tapply(somatic.variants$logn_coding_mutations, somatic.variants$tumor_type, median)
cancer_rates <- cancer_rates[order(cancer_rates, decreasing=F)]
somatic.variants$tumor_type <- factor(somatic.variants$tumor_type, levels = names(cancer_rates))
library(ggplot2)
library(GGally)
ggplot(data = somatic.variants,
mapping = aes(x = tumor_type,
y = log10(n_coding_mutations))) +
geom_point(position = position_dodge2()) +
scale_x_discrete(position = "top") +
scale_y_continuous(labels = c(0,10,100,1000,10000), expand = c(0,0)) +
geom_stripped_cols() +
theme_bw() +
theme(axis.title.x = element_blank(),
axis.text.x = element_text(angle = 315, hjust = 1, size = 12),
panel.grid = element_blank()) +
labs(y = "Coding mutations count") +
stat_summary(fun = median,
geom="crossbar",
size = 0.25,
width = 0.9,
group = 1,
show.legend = FALSE,
color = "#FF0000")
This could be achieved by
grouping the data by x-axis categories
arranging by the y-axis value
which ensures that the points are plotted in ascending order of the values for each category.
somatic.variants <- read.delim("https://gist.githubusercontent.com/wudustan/57deecdaefa035c1ecabf930afde295a/raw/1594d51a1e3b52f674ff746caace3231fd31910a/Lawrence.S2.txt", stringsAsFactors=T)
cancer_rates <- tapply(somatic.variants$logn_coding_mutations, somatic.variants$tumor_type, median)
cancer_rates <- cancer_rates[order(cancer_rates, decreasing=F)]
somatic.variants$tumor_type <- factor(somatic.variants$tumor_type, levels = names(cancer_rates))
library(ggplot2)
library(GGally)
library(dplyr)
somatic.variants <- somatic.variants %>%
group_by(tumor_type) %>%
arrange(n_coding_mutations)
ggplot(data = somatic.variants,
mapping = aes(x = tumor_type,
y = log10(n_coding_mutations))) +
geom_point(position = position_dodge2(.9), size = .25) +
scale_x_discrete(position = "top") +
scale_y_continuous(labels = c(0,10,100,1000,10000), expand = c(0,0)) +
geom_stripped_cols() +
theme_bw() +
theme(axis.title.x = element_blank(),
axis.text.x = element_text(angle = 315, hjust = 1, size = 12),
panel.grid = element_blank()) +
labs(y = "Coding mutations count") +
stat_summary(fun = median,
geom="crossbar",
size = 0.25,
width = 0.9,
group = 1,
show.legend = FALSE,
color = "#FF0000")
#> Warning: Removed 29 rows containing non-finite values (stat_summary).
Background
I took the data from a Stephen Few Example and wanted to add labels to each of the bars to pull the legend from the side of the graphic.
The code in the "Hack Graphic" section got me there because I couldn't get the position_dodge() to work with the text labels.
Load Data
library(tidyverse)
library(forcats)
### Build data from his table
candidates <- tibble::tibble(`Rating Areas` = c("Experience",
"Communication", "Friendliness", "Subject matter knowledge", "Presentation",
"Education"), `Karen Fortou` = c(4,3.5, 4, 4, 3, 3.5), `Mike Rafun` = c(4.5,
2, 2, 5, 1.5, 4.5), `Jack Nymbul` = c(2.5, 5, 4.5, 2.5, 2.75, 2)) %>%
gather("Candidates", "Score", -`Rating Areas`)
# The totals for each candidate
totals <- candidates %>% group_by(Candidates) %>% summarise(Score =
sum(Score))
Hack Graphic
Notice how I used manually created x-axis values (x = c(seq(.6,1.35, by = .15), seq(1.6,2.35, by = .15), seq(2.6,3.35, by = .15))) to place the labels instead of using position = position_dodge() as described in this post.
candidates %>%
ggplot(aes(x = fct_reorder(Candidates, Score), y = Score)) +
geom_col(data = totals, alpha = .45) +
geom_col(aes(fill = `Rating Areas`), position = position_dodge(.9), color = "black",
show.legend = FALSE) +
geom_text(label = rep(c("Experience", "Communication", "Friendliness",
"Subject matter knowledge", "Presentation", "Education"),3),
x = c(seq(.6,1.35, by = .15), seq(1.6,2.35, by = .15),
seq(2.6,3.35, by = .15)), y = 5.1, angle = 90, color = "black",
hjust = "left", size = 4, fontface = "bold") +
scale_fill_brewer(type = "qual") +
scale_y_continuous(breaks = seq(0, 25, by = 2)) +
theme_bw() +
labs(x = "\nCandidates", y = "Rating Score") +
theme(axis.text.x = element_text(size = 14, color = "black"), legend.text = element_text(size = 14),
legend.title = element_text(size = 15), axis.title = element_text(size = 15))
Graphic Code that doesn't work
When I follow the example from the previous Stack answer using geom_text(aes(label =Rating Areas), position = position_dodge(width = 0.9), angle = 90, color = "black", hjust = "left", size = 4, fontface = "bold") it does not spread the labels out ever each bar.
I must be missing something obvious. Please help with how to get position_dodge() to work with this example?
candidates %>%
ggplot(aes(x = fct_reorder(Candidates, Score), y = Score)) +
geom_col(data = totals, alpha = .45) +
geom_col(aes(fill = `Rating Areas`), position = position_dodge(.9), color = "black", show.legend = FALSE) +
geom_text(aes(label = `Rating Areas`), position = position_dodge(width = 0.9), angle = 90, color = "black", hjust = "left", size = 4, fontface = "bold") +
scale_fill_brewer(type = "qual") +
scale_y_continuous(breaks = seq(0, 25, by = 2)) +
theme_bw() +
labs(x = "\nCandidates", y = "Rating Score") +
theme(axis.text.x = element_text(size = 14, color = "black"), legend.text = element_text(size = 14), legend.title = element_text(size = 15), axis.title = element_text(size = 15))
I think you need to have the same mapping for both geom_col and geom_text. You can add fill = Rating Areas to the aesthetics of geom_text. You will get a warning though.
candidates %>%
ggplot(aes(x = fct_reorder(Candidates, Score), y = Score)) +
geom_col(data = totals, alpha = .45) +
geom_col(aes(fill = `Rating Areas`), position = position_dodge(.9), color = "black", show.legend = FALSE) +
geom_text(aes(fill = `Rating Areas`, label = `Rating Areas`), position = position_dodge(width = 0.9), angle = 90, color = "black", hjust = "left", size = 4, fontface = "bold") +
scale_fill_brewer(type = "qual") +
scale_y_continuous(breaks = seq(0, 25, by = 2)) +
theme_bw() +
labs(x = "\nCandidates", y = "Rating Score") +
theme(axis.text.x = element_text(size = 14, color = "black"), legend.text = element_text(size = 14), legend.title = element_text(size = 15), axis.title = element_text(size = 15))
Edit: Here's a way to do it without the warning:
candidates %>%
ggplot(aes(x = fct_reorder(Candidates, Score), y = Score, fill = `Rating Areas`)) +
geom_col(data = totals, aes(x = fct_reorder(Candidates, Score), y = Score), alpha = .45, inherit.aes = FALSE) +
geom_col(position = position_dodge(.9), color = "black", show.legend = FALSE) +
geom_text(aes(label = `Rating Areas`), position = position_dodge(width = 0.9), angle = 90, color = "black", hjust = "left", size = 4, fontface = "bold") +
scale_fill_brewer(type = "qual") +
scale_y_continuous(breaks = seq(0, 25, by = 2)) +
theme_bw() +
labs(x = "\nCandidates", y = "Rating Score") +
theme(axis.text.x = element_text(size = 14, color = "black"), legend.text = element_text(size = 14), legend.title = element_text(size = 15), axis.title = element_text(size = 15))