Skip to content

ggplot2: Waterfall Charts

May 10, 2010

Waterfall charts are often used for analytical purposes in the business setting to show the effect of sequentially introduced negative and/or positive values. Sometimes waterfall charts are also referred to as cascade charts.

In the next few paragraphs I will show how to plot a waterfall chart using ggplot2.


Data

A very small fictional dataset depicting the changes to a company cash position, found in a blogpost showing how to prepare a waterfall chart in Tableau.

> 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))
> balance
           desc amount
1 Starting Cash   2000
2         Sales   3400
3       Refunds  -1100
4       Payouts   -100
5  Court Losses  -6600
6    Court Wins   3800
7     Contracts   1400
8      End Cash   2800

In order to preserve the order of the lines in a dataframe I convert the desc variable to a factor; id and type variable are also added:

> balance$desc <- factor(balance$desc, levels = balance$desc)
> balance$id <- seq_along(balance$amount)
> balance$type <- ifelse(balance$amount > 0, "in",
+     "out")
> balance[balance$desc %in% c("Starting Cash", "End Cash"),
+     "type"] <- "net"

Next the data will be slightly reworked to specify the coordinates for drawing the waterfall bars.

> 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
  id          desc type start   end amount
1  1 Starting Cash  net     0  2000   2000
2  2         Sales   in  2000  5400   3400
3  3       Refunds  out  5400  4300  -1100
4  4       Payouts  out  4300  4200   -100
5  5  Court Losses  out  4200 -2400  -6600
6  6    Court Wins   in -2400  1400   3800
7  7     Contracts   in  1400  2800   1400
8  8      End Cash  net  2800     0   2800

Plotting

Now everything is set to plot the first waterfall chart. geom_rect is used to draw the rectangles using the coordinates calculated in the previous step.

> library(ggplot2)
> ggplot(balance, aes(desc, fill = type)) + geom_rect(aes(x = desc,
+     xmin = id - 0.45, xmax = id + 0.45, ymin = end,
+     ymax = start))
waterfall-007.png

The fill mapping could use some tweaking (my preference is to have outflows in red, inflows in green, and net position in blue), for that I change the order of the underlying factor levels.

> balance$type <- factor(balance$type, levels = c("out",
+     "in", "net"))

Almost ready, one more tweak to the x-axis labels: the helper function below replaces spaces with new lines, making the labels more readable.

> strwr <- function(str) gsub(" ", "\n", str)
> (p1 <- ggplot(balance, aes(fill = type)) + geom_rect(aes(x = desc,
+     xmin = id - 0.45, xmax = id + 0.45, ymin = end,
+     ymax = start)) + scale_y_continuous("", formatter = "comma") +
+     scale_x_discrete("", breaks = levels(balance$desc),
+         labels = strwr(levels(balance$desc))) +
+     opts(legend.position = "none"))
waterfall-011.png

Finally, the bar labels are also added (the conditional positioning of them is quite a lengthy process, as you can see).

> p1 + geom_text(subset = .(type == "in"), aes(id,
+     end, label = comma(amount)), vjust = 1, size = 3) +
+     geom_text(subset = .(type == "out"), aes(id,
+         end, label = comma(amount)), vjust = -0.3,
+         size = 3) + geom_text(data = subset(balance,
+     type == "net" & id == min(id)), aes(id, end,
+     colour = type, label = comma(end), vjust = ifelse(end <
+         start, 1, -0.3)), size = 3.5) + geom_text(data = subset(balance,
+     type == "net" & id == max(id)), aes(id, start,
+     colour = type, label = comma(start), vjust = ifelse(end <
+         start, -0.3, 1)), size = 3.5)
waterfall-013.png
About these ads
5 Comments leave one →
  1. Felipe Carrillo permalink
    March 8, 2011 4:40 am

    Hi:
    This is a great website, haven’t seen new posts, are you planning to post more examples in the future? just wondering…

    • learnr permalink*
      March 8, 2011 11:06 am

      Life has been quite busy for me over the past year, but I certainly aim to make new posts in the future.

  2. March 20, 2011 8:21 pm

    awesome example of business use case followed by R code. usually the order is reversed. i wonder how we could do this using Deducer?

    • learnr permalink*
      March 20, 2011 11:47 pm

      I haven’t used Deducer myself, but I guess with a little manual editing of the code Deducer produces this should be achievable.

  3. Juan Pablo Dolfini permalink
    May 15, 2012 7:18 pm

    Hint: Install the package “scales”.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 163 other followers

%d bloggers like this: