Reputation: 281
I am trying to do some waterfall plots using the following code I found on the internet and adapted:
waterfall <- function(balance){
balance$desc <- factor(balance$desc, levels = balance$desc)
balance$id <- seq_along(balance$amount)
balance$type <- ifelse(balance$amount > 0, "increase","decrease")
balance[balance$id %in% c(1,dim(balance)[1]),"type"] <- "net"
balance$end <- cumsum(balance$amount)
balance$end <- c(head(balance$end, -1), 0)
balance$start <- c(0, head(balance$end, -1))
balance <- balance[, c(3, 1, 4, 6, 5, 2)]
balance
balance$type <- factor(balance$type, levels = c("decrease","increase", "net"))
p1 <- ggplot(balance, aes(desc, fill = type)) +
geom_rect(aes(x = desc,xmin = id - 0.45, xmax = id + 0.45, ymin = end,ymax = start))+
xlab("") +
ylab("") +
geom_text(subset = .(type == "increase"), aes(id,end, label = comma(amount)), vjust = 1, size = 4,fontface="bold") +
geom_text(subset = .(type == "decrease"), aes(id,end, label = comma(amount)), vjust = -0.3,size = 4,fontface="bold")+
geom_text(data = subset(balance,type == "net" & id == min(id)), aes(id, end, label = comma(end), vjust = ifelse(end <start, 1, -0.3)), size = 4,fontface="bold") +
geom_text(data = subset(balance,type == "net" & id == max(id)), aes(id, start, label = comma(start), vjust = ifelse(end < start, -0.3, 1)), size = 4,fontface="bold")+
theme_bw()+
theme(legend.position = "none")
return(p1)
}
I am quite happy with the result if I use it on the following example
balance <- data.frame(desc = c("Starting Cash","Sales", "Refunds", "Payouts", "Court Losses","Court Wins", "Contracts", "End Cash"),
amount = c(2000, 3400, -1100, -100, -6600, 3800, 1400, 2800))
but if I want to plot a waterfall with only increases (resp. decreases) such as on the following example
balance <- data.frame(desc = c("Starting Cash", "Court Losses","Court Wins", "Contracts", "End Cash"),
amount = c(2000, -1100, -100, -6600, 2800))
then I have an error
Error in if (nrow(layer_data) == 0) return() : argument is of length zero
I guess it has something to do with the subset argument in the geom_text, but I have no idea how to treat the case where the subset is empty.
I have a second question. I am happy with the three colors that I have in the first case. However, if I run the code on the second example, by removing the faulty portion of the code, I get a graph with only two colors that are different than on the previous case. Is it possible to fix the colors such as to have blue/red/green as in the first example ?
Thanks.
Upvotes: 1
Views: 1424
Reputation: 1711
It is indeed because of the empty subset for "increase". An easy way to avoid such empty subsets is with if statements:
waterfall <- function(balance){
balance$desc <- factor(balance$desc, levels = balance$desc)
balance$id <- seq_along(balance$amount)
balance$type <- ifelse(balance$amount > 0, "increase","decrease")
balance[balance$id %in% c(1,dim(balance)[1]),"type"] <- "net"
balance$end <- cumsum(balance$amount)
balance$end <- c(head(balance$end, -1), 0)
balance$start <- c(0, head(balance$end, -1))
balance$type <- factor(balance$type, levels = c("decrease","increase", "net"))
p1 <- ggplot(balance, aes(desc, fill = type)) +
geom_rect(aes(x = desc,xmin = id - 0.45, xmax = id + 0.45, ymin = end,ymax = start))+
xlab("") +
ylab("") +
geom_text(data = subset(balance,type == "net" & id == min(id)), aes(id, end, label = comma(end), vjust = ifelse(end <start, 1, -0.3)), size = 4,fontface="bold") +
geom_text(data = subset(balance,type == "net" & id == max(id)), aes(id, start, label = comma(start), vjust = ifelse(end < start, -0.3, 1)), size = 4,fontface="bold")+
theme_bw()+
theme(legend.position = "none")
if ("increase" %in% balance$type){
p1 <- p1 + geom_text(subset = .(type == "increase"), aes(id,end, label = comma(amount)), vjust = 1, size = 4,fontface="bold")
}
if ("decrease" %in% balance$type){
p1 <- p1 + geom_text(subset = .(type == "decrease"), aes(id,end, label = comma(amount)), vjust = -0.3,size = 4,fontface="bold")
}
p1
}
You can fix the colors for your different levels by adding
scale_fill_manual(values = c(decrease = "indianred",increase ="forestgreen", net = "dodgerblue2"))
to your ggplot.
Upvotes: 1