Heatmaps in ggplot2

How to make Heatmaps plots in ggplot2 with Plotly.


Plotly Studio: Transform any dataset into an interactive data application in minutes with AI. Sign up for early access now.

Basic 2d Heatmap

See also geom_hex for a similar geom with hexagonal bins. Note: facetting is supported in geom_bin2d but not geom_hex.

Source: Department of Canadian Heritage

library(plotly)

english_french <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/english_french.csv",stringsAsFactors = FALSE)

p <- ggplot(english_french, aes(x=engperc,y=frenperc)) + 
  geom_bin2d() +
  labs(title = "Distribution of Canadian areas by English and French fluency",
       x = "% fluent in English",
       y = "% fluent in French",
       fill = "# of census \nsubdivisions")
ggplotly(p)

geom_raster creates a coloured heatmap, with two variables acting as the x- and y-coordinates and a third variable mapping onto a colour. (It is coded similarly to geom_tile and is generated more quickly.) This uses the volcano dataset that comes pre-loaded with R.

library(reshape2)
library(plotly)

df <- melt(volcano)

p <- ggplot(df, aes(Var1, Var2)) +
  geom_raster(aes(fill=value)) +
  labs(x="West to East",
       y="North to South",
       title = "Elevation map of Maunga Whau")

ggplotly(p)

Customized Colours

This uses the Spectral palette from ColorBrewer; a full list of palettes is here.

library(reshape2)
library(plotly)

df <- melt(volcano)

p <- ggplot(df, aes(Var1, Var2)) +
  geom_raster(aes(fill=value)) +
  scale_fill_distiller(palette = "Spectral", direction = -1) +
  labs(x="West to East",
       y="North to South",
       title = "Elevation map of Maunga Whau",
       fill = "Elevation") +
  theme(text = element_text(family = 'Fira Sans'),
        plot.title = element_text(hjust = 0.5))

ggplotly(p)

Let's flip the colour scheme so that lighter colours denote larger numbers than darker colours. We should also move to a logarithmic scale, since as it is, the very large value in the bottom right overshadows all other values.

library(plotly)

p <- ggplot(english_french, aes(x=engperc,y=frenperc)) + 
  geom_bin2d() +
  scale_fill_gradient(low="lightblue1",high="darkblue",trans="log10") +
  labs(title = "Distribution of Canadian towns by English and French fluency",
       x = "% fluent in English",
       y = "% fluent in French",
       fill = "# of census \nsubdivisions")
ggplotly(p)

Weighted Data

In the previous graphs, each observation represented a single census subdivision - this counted small towns of 500 people equally with cities like Montreal and Toronto. We can weight the data by the "total" column (i.e. total population) to make this a graph of population.

library(plotly)

p <- ggplot(english_french, aes(x=engperc, y=frenperc, weight=total)) + 
  geom_bin2d() +
  scale_fill_gradient(low="lightblue1",high="darkblue",trans="log10") +
  labs(title = "Distribution of the Canadian population by English and French fluency",
       x = "% fluent in English",
       y = "% fluent in French",
       fill = "population")
fig <- ggplotly(p)


fig

With Facets

We can facet the graphic with the "region" column, and set "bins" to 20, so that the graph is 20 x 20 sides.

library(plotly)

p <- ggplot(english_french, aes(x=engperc,y=frenperc, weight=total)) + 
  geom_bin2d(bins = 20) +
  facet_wrap(~factor(region, levels = c("Atlantic","Québec","Ontario","Prairies","British Columbia"))) +
  scale_fill_gradient(low="lightblue1",high="darkblue",trans="log10") +
  labs(title = "Distribution of Canadian towns by English and French fluency",
       x = "% fluent in English",
       y = "% fluent in French",
       fill = "population")
fig <- ggplotly(p)


fig

Customized Appearance

We can modify the graph's appearance - for example, if the grey background makes it difficult to make out the paler shades of blue, we can change the theme to one with a white background. Included also is a way to change the font.

library(plotly)

p <- ggplot(english_french, aes(x=engperc,y=frenperc, weight=total)) + 
  geom_bin2d(bins = 20) +
  facet_wrap(~factor(region, levels = c("Atlantic","Québec","Ontario","Prairies","British Columbia"))) +
  scale_fill_gradient(low="lightblue1",high="darkblue",trans="log10") +
  labs(title = "Distribution of Canadian towns by English and French fluency",
       x = "% fluent in English",
       y = "% fluent in French",
       fill = "population") +
  theme_bw() +
  theme(text = element_text(family = 'Fira Sans'))
fig <- ggplotly(p)


fig

Basic geom_tile graph

This graph, compiled by Jeff Zimmerman, shows how often hitters swing and miss at fastballs, based on their velocity and spin rate. Colour schemes are from ColorBrewer; a complete list of palettes is available here.

library(plotly)
spinrates <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/spinrates.csv",
                      stringsAsFactors = FALSE)

p <- ggplot(spinrates, aes(x=velocity, y=spinrate)) +
  geom_tile(aes(fill = swing_miss)) +
  scale_fill_distiller(palette = "YlGnBu") +
  labs(title = "Likelihood of swinging and missing on a fastball",
       y = "spin rate (rpm)")

ggplotly(p)

Adjusting appearance

The direction option sets which side of the colour scheme maps onto the low values and which side maps onto the high; it defaults to -1 but could be adjusted to 1.

Also adjusted the theme.

library(plotly)
spinrates <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/spinrates.csv",
                      stringsAsFactors = FALSE)

p <- ggplot(spinrates, aes(x=velocity, y=spinrate)) +
  geom_tile(aes(fill = swing_miss)) +
  scale_fill_distiller(palette = "YlGnBu", direction = 1) +
  theme_light() +
  labs(title = "Likelihood of swinging and missing on a fastball",
       y = "spin rate (rpm)")

ggplotly(p)

geom_tile with viridis colour scheme

Viridis colour schemes are uniform in both colour and black-and-white, as well as for those with colour-blindness. There are five colour schemes: "magma" (or "A"), "inferno" (or "B"), "plasma" (or "C"), "viridis" (or "D", the default option) and "cividis" (or "E").

library(plotly)
spinrates <- read.csv("https://raw.githubusercontent.com/plotly/datasets/master/spinrates.csv",
                      stringsAsFactors = FALSE)

p <- ggplot(spinrates, aes(x=velocity, y=spinrate)) +
  geom_tile(aes(fill = swing_miss)) +
  scale_fill_viridis_c(option = "B", direction = -1) +
  labs(title = "Likelihood of swinging and missing on a fastball",
       y = "spin rate (rpm)") +
  theme_light()

ggplotly(p)

What About Dash?

Dash for R is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.

Learn about how to install Dash for R at https://dashr.plot.ly/installation.

Everywhere in this page that you see fig, you can display the same figure in a Dash for R application by passing it to the figure argument of the Graph component from the built-in dashCoreComponents package like this:

library(plotly)

fig <- plot_ly() 
# fig <- fig %>% add_trace( ... )
# fig <- fig %>% layout( ... ) 

library(dash)
library(dashCoreComponents)
library(dashHtmlComponents)

app <- Dash$new()
app$layout(
    htmlDiv(
        list(
            dccGraph(figure=fig) 
        )
     )
)

app$run_server(debug=TRUE, dev_tools_hot_reload=FALSE)