I want to show covered ranges (including overlaps) and (after some failures with stacked bar plots) I chose geom_rect. The following code works well for one type.
library(tidyverse)
# create dummy data
foo <- tibble(start = c(1, 150, 140, 75, 300),
end = c(150, 180, 170, 160, 400))
ggplot() +
geom_rect(data = foo, aes(xmin = start, xmax = end, ymin = 0, ymax = 1), fill = "green", linetype = "blank", alpha = 0.3) +
geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1), fill = NA, colour = "black") +
scale_y_continuous(name = "", breaks = NULL, limits = c(0, 1)) +
scale_x_continuous(name = "", breaks = NULL) +
theme_minimal() +
theme(panel.grid = element_blank())
If I add more data (only one more type, but in the original data I do have some more) like below, I can add the data "by hand", i.e. add two lines of code for each type, but I'm looking for a way to do this by grouping, but didn't succeed.
foo <- foo %>%
mutate(type = "A", .before = 1)
bar <- tibble(type = "B",
start = c(1, 30, 40, 100, 150, 200, 310),
end = c(20, 50, 100, 120, 200, 300, 380))
foo <- bind_rows(foo, bar)
ggplot() +
geom_rect(data = foo %>% filter(type == "A"), aes(xmin = start, xmax = end, ymin = 0, ymax = 1), fill = "green", linetype = "blank", alpha = 0.3) +
geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 0, ymax = 1), fill = NA, colour = "black") +
geom_rect(data = foo %>% filter(type == "B"), aes(xmin = start, xmax = end, ymin = 2, ymax = 3), fill = "green", linetype = "blank", alpha = 0.3) +
geom_rect(data = foo, aes(xmin = 1, xmax = max(end), ymin = 2, ymax = 3), fill = NA, colour = "black") +
scale_y_continuous(name = "", breaks = NULL, limits = c(0, 3)) +
scale_x_continuous(name = "", breaks = NULL) +
geom_text(aes(x = c(0, 0), y = c(0.5, 2.5), label = c("A", "B")), size = 4, hjust = 2) +
theme_minimal() +
theme(panel.grid = element_blank())
So, the graph already looks the way I want, but I'd prefer to get here by using grouping (or any other non-manual way).
Maybe there's also a different geom or method to get this kind of graph?
You can write a small helper function that positions a categorical value in continuous space. Example below.
helper <- function(x) {(match(x, sort(unique(x))) - 1) * 2}
ggplot(foo) +
geom_rect(
aes(xmin = start, xmax = end,
ymin = helper(type),
ymax = helper(type) + 1),
fill = "green", linetype = "blank", alpha = 0.3
) +
geom_rect(
aes(xmin = min(start), xmax = max(end),
ymin = helper(type),
ymax = helper(type) + 1),
fill = NA, colour = "black"
) +
scale_y_continuous(name = "", breaks = NULL, limits = c(0, 3)) +
scale_x_continuous(name = "", breaks = NULL) +
annotate(
"text", x = c(0, 0), y = c(0.5, 2.5), label = c("A", "B"),
size = 4, hjust = 2
) +
theme_minimal() +
theme(panel.grid = element_blank())
Related
Trying to use ggpattern for this plot but can't get it to work right. Legend looks okay doesn't translate to what's on plot itself. Not stripes or dots on actual plot?
test <- tibble(names = c("fred", "harry", "tom"),
start = c(1, 3, 5),
end = c(10, 5, 7),
stripe = c("yes", "no", "yes"))
ggplot() +
geom_rect_pattern(data = test,
aes(xmin = names,
xmax = names,
ymin = start,
ymax = end,
color = names,
fill = names,
pattern = stripe), size = 4)
Your issue is that you do not have xmin, xmax, ymin, ymax values. Since you use rectangles (you need to specify 4 corners): e.g:
plot_df <- data.frame(
xmin = c(0, 10, 3),
xmax = c(8, 18, 4),
ymin = c(0, 10, 8),
ymax = c(5, 19, 15),
type = c('a', 'b', 'c'),
colour1 = c('red', 'black', 'blue')
)
After that
ggplot(plot_df) +
geom_rect_pattern(
aes(
xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax,
pattern_fill = I(colour)
),
pattern = 'stripe',
colour = 'black',
pattern_density = 0.3,
fill = NA
) +
theme_bw(18)
To produce the plot without altering your data, you could try:
ggplot() +
geom_rect_pattern(data = test,
aes(xmin = as.numeric(factor(names)) - 0.25,
xmax = as.numeric(factor(names)) + 0.25,
ymin = start,
ymax = end,
fill = names,
pattern = stripe), pattern_fill = 'black', size = 0) +
scale_x_continuous(breaks = seq(length(levels(factor(test$names)))),
labels = levels(factor(test$names))) +
scale_pattern_manual(values = c('none', 'stripe'))
I have data and I need to reproduce a graph like this. I'm not sure what this type of plot is called, thus I also don't know how to plot it in R. Any help is appreciated.
I don't know what it's called, but here's a function to produce one:
library(ggplot2)
quadrant_plot <- function(component, values,
fills = c("#919191", "#686868", "#434343", "#ededed"))
{
df <- data.frame(component = factor(component, levels = component),
xvalue = c(values[1:2], -values[3:4]),
yvalue = c(values[1], -values[2:3], values[4]),
xmin = c(0, 0, -Inf, -Inf),
xmax = c(Inf, Inf, 0, 0),
ymin = c(0, -Inf, -Inf, 0),
ymax = c(Inf, 0, 0, Inf),
textx = c(50, 50, -50, -50),
texty = c(50, -50, -50, 50),
labelx = c(95, 95, -95, -95),
labely = c(95, -95, -95, 95),
hjust = c(1, 1, 0, 0))
ggplot(df, aes(xvalue, yvalue)) +
geom_rect(aes(xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax, fill = component)) +
geom_point() +
geom_polygon(fill = NA, color = "black", size = 1) +
geom_text(aes(x = textx, y = texty, label = abs(xvalue))) +
geom_text(aes(x = labelx, y = labely, label = component, hjust = hjust),
size = 6) +
geom_hline(aes(yintercept = 0), size = 1) +
geom_vline(aes(xintercept = 0), size = 1) +
scale_fill_manual(values = fills, guide = guide_none()) +
lims(x = c(-100, 100), y = c(-100, 100)) +
coord_equal() +
theme_void()
}
So you can do:
quadrant_plot(component = c("Adhocracy", "Hierarchy", "Market", "Clan"),
values = c(18.4, 22.9, 32.6, 26.5))
Or if you want to change the input and the colours you can do
quadrant_plot(component = c("Optimism", "Insight", "Skill", "Luck"),
values = c(35, 15, 12, 31),
fills = c("gold", "deepskyblue4", "orange", "lightblue"))
Reproducible data:
df <- data.frame(cbind("Thriving" = c(2, 2, NA, runif(9, 2.0, 5.0)), "Performance" = c(2, 3.5, 2.3, 4.2, NA, runif(7, 1.9, 6.9)), "Mastery_Climate" = c(runif(10, 2.2, 6.5), NA, 2.3), "Competitive_Climate" = c(NA, runif(4, 1.0, 3.6), NA, NA, runif(5, 1.5, 2.8)), "Collaboration" = c(runif(8, 2.2, 7.0), NA, NA, 5.5, 2.1)))
With this data I want to create bloxplots using the following command with the packages ggplot2 and tidyr:
df %>%
gather(key = "variable", value = "value") -> n
n$variable <- factor(n$variable, levels = c("Thriving", "Performance", "Mastery_Climate", "Competitive_Climate", "Collaboration"))
ggplot(data = n, aes(y = value, x = variable)) + stat_summary(fun.data = min.mean.sd.max, geom = "boxplot", col = "#323232", fill = "#EFC76C") +
coord_flip() + scale_y_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) +
expand_limits(y = c(1, 7)) +
labs(x = "", y = "") +
theme(text = element_text(size = 12), panel.background = element_rect(fill = "#EAEDED")) +
theme(plot.margin=unit(c(0, 2, 0, 1.8),"cm"))
The function used in stat_summary is as follows:
min.mean.sd.max <- function(x) {
r <- c(min(x), mean(x) - sd(x), mean(x), mean(x) + sd(x), max(x))
names(r) <- c("ymin", "lower", "middle", "upper", "ymax")
r
}
Now, HERE IT COMES: everything works beautifully, however, now I would like to colour the background in three different colours, green, yellow and red. I know that I have to use geom_rect for that. However, in order to have the boxplots in the foreground, I need to pass the geom_rect argument first - but this breaks my code:
df %>%
gather(key = "variable", value = "value") -> n
n$variable <- factor(n$variable, levels = c("Thriving", "Performance", "Mastery_Climate", "Competitive_Climate", "Collaboration"))
ggplot(data = n, aes(y = value, x = variable)) +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = 3, fill = "green"), alpha = .01) +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 3, ymax = 5, fill = "yellow"), alpha = .01) +
geom_rect(aes(xmin = -Inf, xmax = Inf, ymin = 5, ymax = Inf, fill = "red"), alpha = .01) +
stat_summary(fun.data = min.mean.sd.max, geom = "boxplot", col = "#323232", fill = "#EFC76C") +
coord_flip() + scale_y_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) +
expand_limits(y = c(1, 7)) +
labs(x = "", y = "") +
theme(text = element_text(size = 12), panel.background = element_rect(fill = "#EAEDED")) +
theme(plot.margin=unit(c(0, 2, 0, 1.8),"cm"))
As you can see, I get the error "Error: Discrete value supplied to continuous scale". From research I understand that this is because I needed to change the sequence and it now is a problem that the x-variable is a factor. However, I have been unable to solve this.
It would be great if all the other code could stay the same, it took me ages to put it together. Also, once the boxplots are in the foreground, it would be great if the grid behind would still be visible. Furthermore, I was so confused by the fill in geom_rect, I put in "green" and I get pink, or I put "yellow" and get blue - no clue why.
In any case, any help is very much appreciated. Many greetings!
Edit: Updated answer with better shading control
I think this approach is more idiomatic to ggplot: this puts the shading into a separate table with numeric y values. In a modified ggplot call, all the y values are mapped as numeric values, but the labels for those values are swapped out in the scale_y_continuous line.
rects <- data.frame(xmin = -Inf,
xmax = Inf,
ymin = c(0,3,5),
ymax = c(3,5,Inf),
fill = c("green", "yellow", "red"))
ggplot(data = n, aes(y = value, x = as.numeric(variable))) +
geom_rect(data = rects, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill),
# Control the shading opacity here.
inherit.aes = FALSE, alpha = 0.15) +
stat_summary(fun.data = min.mean.sd.max, geom = "boxplot", col = "#323232", fill = "#EFC76C") +
scale_fill_identity() +
scale_x_continuous(breaks = as.numeric(unique(n$variable)), minor_breaks = NULL,
labels = unique(n$variable)) +
scale_y_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) +
expand_limits(y = c(1, 7)) +
coord_flip() +
labs(x = "", y = "") +
theme(text = element_text(size = 12), panel.background = element_rect(fill = "#EAEDED")) +
theme(plot.margin=unit(c(0, 2, 0, 1.8),"cm"))
Original answer
geom_rect's coordinates should be pulled outside of the aes() call, and then I get a working solution. However, one problem with this approach is that the background rectangles are actually getting drawn once for each element in the source data, which is why the colors are so bright even with alpha = 0.01.
ggplot(data = n, aes(y = value, x = variable)) +
geom_rect(xmin = -Inf, xmax = Inf, ymin = 0, ymax = 3, fill = "green", alpha = .005) +
geom_rect(xmin = -Inf, xmax = Inf, ymin = 3, ymax = 5, fill = "yellow", alpha = .005) +
geom_rect(xmin = -Inf, xmax = Inf, ymin = 5, ymax = 7, fill = "red", alpha = .005) +
stat_summary(fun.data = min.mean.sd.max, geom = "boxplot", col = "#323232", fill = "#EFC76C") +
coord_flip() + scale_y_continuous(breaks = c(1, 2, 3, 4, 5, 6, 7)) +
expand_limits(y = c(1, 7)) +
labs(x = "", y = "") +
theme(text = element_text(size = 12), panel.background = element_rect(fill = "#EAEDED")) +
theme(plot.margin=unit(c(0, 2, 0, 1.8),"cm"))
I am trying to recreate the waterfall chart as shown in
https://vita.had.co.nz/papers/ggplot2-wires.pdf
I am reproducing the code from the link
balance <- data.frame(event = c("Starting\nCash", "Sales", "Refunds",
"Payouts", "Court\nLosses", "Court\nWins", "Contracts", "End\nCash"),
change = c(2000, 3400, -1100, -100, -6600, 3800, 1400, -2800))
balance$balance <- cumsum(c(0, balance$change[-nrow(balance)]))
balance$time <- 1:nrow(balance)
balance$flow <- factor(sign(balance$change))
ggplot(balance) +
geom_hline(yintercept = 0, colour = "white", size = 2) +
geom_rect(aes(fill= 'red'),xmin = time - 0.45, xmax = time + 0.45, ymin = balance, ymax = balance)
geom_text(aes(x = time, y = pmin(balance, balance + change) - 50, label = dollar(change)),
hjust = 0.5, vjust = 1, size = 3)
scale_x_continuous( breaks = balance$time, labels = balance$event) +
scale_y_continuous("Balance") +
scale_fill_manual(values = c("-1" = "red", "1" = "black"))
it throws an error :Error in scale_x_continuous(breaks = balance$time, labels = balance$event) + : non-numeric argument to binary operator
THe final output, per the pdf should look like the image below
ggplot(balance %>%
mutate(flow = factor(flow, labels = c("Negative", "Positive")))) +
geom_hline(yintercept = 0, colour = "white", size = 2) +
geom_rect(aes(fill= flow, xmin = time - 0.45, xmax = time+0.45, ymin = change, ymax = balance),
color = "black") +
geom_text(aes(x = time, y = pmin(balance, balance + change) - 50, label = change),
hjust = 0.5, vjust = 1, size = 3) +
scale_x_continuous(breaks = balance$time, labels = balance$event) +
scale_y_continuous("Balance") +
scale_fill_manual(values = c("Negative" = "red", "Positive" = "green"))
I am trying to plot some data in a ggplotly plot.
The x-axis contains dates. Ggplotly doesn't work well with dates as when I hover over a point, the date is displayed as a number.
I solved this by setting a tooltip like below.
Some sample data:
x <- data.frame(Date = as.Date(seq(Sys.Date(), Sys.Date() + 29, by = "days")), Amount = seq(-10000, 19000, by = 1000),
stringsAsFactors = FALSE)
The plot:
ggplotly(ggplot(x, aes(x = Date, y = Amount, group = 1, text = paste("Date: ", Date, "<br>Amount: ", Amount))) + geom_line() + geom_point()
, tooltip = "text")
Now I want to use geom_rect() to get some background colors depending on the value of the y-axis. This gives me problems as the rectangles seem to be placed on top of the geom_line(). Also, the rectangles are labeled by ggplotly too, which I don't want either.
Here is the code I tried (the background coloring works fine when I am not using a custom tooltip, but then the problem with the dates in the labels occurs):
ggplotly(ggplot(x, aes(x = Date, y = Amount, group = 1, text = paste("Date: ", Date, "<br>Amount: ", Amount))) + geom_line() + geom_point()
+
geom_rect(aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = 10000, ymax = max(max(x$Amount) + 1000, 11000), fill = "1")) +
geom_rect(aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = 0, ymax = 10000, fill = "2")) +
geom_rect(aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = min(min(x$Amount) - 1000, 0), ymax = 0, fill = "3"))
+
scale_fill_manual(values = alpha(c("green", "orange", "red"), 0.2))
, tooltip = "text")
Result
Any help would be appreciated, thanks!
EDIT:
The following code results in working geom_rect():
ggplotly(ggplot(x, aes(x = Date, y = Amount)) + geom_line() + geom_point()
+
geom_rect(aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = 10000, ymax = max(max(x$Amount) + 1000, 11000), fill = "1")) +
geom_rect(aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = 0, ymax = 10000, fill = "2")) +
geom_rect(aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = min(min(x$Amount) - 1000, 0), ymax = 0, fill = "3"))
+
scale_fill_manual(values = alpha(c("green", "orange", "red"), 0.2)))
Result
You could try this:
ggplotly(ggplot() +
geom_rect(data = x, aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = 10000, ymax = max(max(x$Amount) + 1000, 11000), fill = "1")) +
geom_rect(data = x, aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = 0, ymax = 10000, fill = "2")) +
geom_rect(data = x, aes(xmin = as.Date(Sys.Date()),
xmax = as.Date(Sys.Date() + 30),
ymin = min(min(x$Amount) - 1000, 0), ymax = 0, fill = "3")) +
geom_line(data = x, aes(x = Date, y = Amount, group = 1, text = paste("Date: ", Date, "<br>Amount: ", Amount))) +
geom_point(data = x, aes(x = Date, y = Amount, text = paste("Date: ", Date, "<br>Amount: ", Amount))) +
scale_fill_manual(values = alpha(c("green", "orange", "red"), 0.2))
, tooltip = "text")