Animint Tutorial

This tutorial is designed to demonstrate animint, a package that converts ggplot2 plots into d3 javascript graphics. Animint allows you to make interactive web-based graphics using familiar R methods. In addition, animint allows graphics to be animated and respond to user clicks.

Contents

Lines

Simple lines

This should work exactly as in ggplot2.

#' Generate data
data <- data.frame(x=rep(1:10, times=5), group = rep(1:5, each=10))
data$lt <- c("even", "odd")[(data$group%%2+1)] # linetype
data$group <- as.factor(data$group)
data$y <- rnorm(length(data$x), data$x, .5) + rep(rnorm(5, 0, 2), each=10)

#' Simple line plot
p1 <- ggplot() + geom_line(data=data, aes(x=x, y=y, group=group)) + 
  ggtitle("geom_line")
p1

plot of chunk linessimple



#' Simple line plot with colours...
p2 <- ggplot() + geom_line(data=data, aes(x=x, y=y, colour=group, group=group)) +
  ggtitle("geom_line + scale_colour_discrete")
p2

plot of chunk linessimple


gg2animint(list(p1=p1, p2=p2), out.dir="geoms/linessimple")

Animint plot

Linetypes

ggplot2 has several methods of linetype specification, all of which are supported in animint.

#' Simple line plot with colours and linetype
p3 <- ggplot() + geom_line(data=data, aes(x=x, y=y, colour=group, group=group, linetype=lt)) +
  ggtitle("geom_line + scale_linetype_manual")
p3

plot of chunk linetypes


#' Use automatic linetypes from ggplot with coerced factors
p4 <- ggplot() + geom_line(data=data, aes(x=x, y=y, colour=group, group=group, linetype=group)) +
  ggtitle("geom_line + scale_linetype automatic")
p4

plot of chunk linetypes


#' Manually specify linetypes using <length, space, length, space...> notation
data$lt <- rep(c("2423", "2415", "331323", "F2F4", "solid"), each=10)
p5 <- ggplot() + geom_line(data=data, aes(x=x, y=y, colour=group, group=group, linetype=lt)) + 
  scale_linetype_identity("group", guide="legend", labels = c("1", "2", "3", "4", "5")) + 
  scale_colour_discrete("group") + 
  ggtitle("Manual Linetypes: dash-space length")
p5

plot of chunk linetypes


#' All possible linetypes
lts <- scales::linetype_pal()(13)
lt1 <- data.frame(x=0, xend=.25, y=1:13, yend=1:13, lt=lts, lx = -.125)
p6 <- ggplot()+geom_segment(data=lt1, aes(x=x, xend=xend, y=y, yend=yend, linetype=lt)) + 
  scale_linetype_identity() + geom_text(data=lt1, aes(x=lx, y=y, label=lt), hjust=0) + 
  ggtitle("Scales package: all linetypes")

lts2 <- c("solid", "dashed", "dotted", "dotdash", "longdash", "twodash")
lt2 <- data.frame(x=0, xend=.25, y=1:6, yend=1:6, lt=lts2, lx=-.125)
p7 <- ggplot() + geom_segment(data=lt2, aes(x=x, xend=xend, y=y, yend=yend, linetype=lt)) + 
  scale_linetype_identity() + geom_text(data=lt2, aes(x=lx, y=y, label=lt), hjust=0) +
  ggtitle("Named linetypes")

gg2animint(list(p3=p3, p4=p4, p5=p5, p6=p6, p7=p7), out.dir="geoms/linetypes")

Animint Plot

Alpha scales and lines

#' Spaghetti Plot Data
n <- 500
pts <- 10
data2 <- data.frame(x=rep(1:pts, times=n), group = rep(1:n, each=pts))
data2$group <- as.factor(data2$group)
data2$y <- rnorm(length(data2$x), data2$x*rep(rnorm(n, 1, .25), each=pts), .25) + rep(rnorm(n, 0, 1), each=pts)
data2$lty <- "solid"
data2$lty[which(data2$group%in%subset(data2, x==10)$group[order(subset(data2, x==10)$y)][1:floor(n/10)])] <- "3133"
data2 <- ddply(data2, .(group), transform, maxy = max(y), miny=min(y))
data2$below0 <- factor(sign(data2$miny)<0)

qplot(data=data2, x=x, y=y, group=group, geom="line", alpha=I(.2))

plot of chunk alphalines


#' scale_alpha
p8 <- ggplot() + geom_line(data=data2, aes(x=x, y=y, group=group), alpha=.1) +
  ggtitle("Constant alpha")
p8

plot of chunk alphalines


p9 <- ggplot() + geom_line(data=subset(data2, as.numeric(group) < 50), 
                           aes(x=x, y=y, group=group, linetype=lty), alpha=.2) +
  scale_linetype_identity() + 
  ggtitle("Constant alpha, I(linetype)")
p9

plot of chunk alphalines


p10 <- ggplot() + geom_line(data=subset(data2, as.numeric(group) < 50), 
                            aes(x=x, y=y, group=group, linetype=below0, alpha=maxy)) + 
  scale_alpha_continuous(range=c(.1, .5)) + 
  ggtitle("Continuous alpha")
p10

plot of chunk alphalines


#' Size Scaling
p11 <- ggplot() + geom_line(data=subset(data2, as.numeric(group)%%50 ==1), 
                            aes(x=x, y=y, group=group, size=(floor(miny)+3)/3)) + 
  scale_size_continuous("Line Size", range=c(1,3)) +
  ggtitle("Continuous size")
p11

plot of chunk alphalines


p12 <- ggplot() + geom_line(data=data2, aes(x=x, y=y, group=group, alpha=miny, colour=maxy)) + 
  scale_alpha_continuous(range=c(.1, .3)) + 
  ggtitle("Continuous Alpha and Colour")
p12

plot of chunk alphalines

gg2animint(list(p8=p8, p9=p9, p10=p10, p11=p11, p12=p12), out.dir="geoms/alphalines")

Animint plot

Points

In general, points are exactly the same in animint and ggplot2, with two exceptions:

# Randomly generate some data
scatterdata <- data.frame(x=rnorm(100, 50, 15))
scatterdata$y <- with(scatterdata, runif(100, x-5, x+5))
scatterdata$xnew <- round(scatterdata$x/20)*20
scatterdata$xnew <- as.factor(scatterdata$xnew)
scatterdata$class <- factor(round(scatterdata$x/10)%%2, labels=c("high", "low"))
scatterdata$class4 <- factor(rowSums(sapply(quantile(scatterdata$x)+c(0,0,0,0,.1), function(i) scatterdata$x<i)), levels=1:4, labels=c("high", "medhigh", "medlow", "low"), ordered=TRUE)

s1 <- ggplot() + geom_point(data=scatterdata, aes(x=x, y=y)) +
  xlab("very long x axis label") + 
  ylab("very long y axis label") +
  ggtitle("Titles are awesome")
s1

plot of chunk pointssimple


#' Colours, Demonstrates axis -- works with factor data
#' Specify colours using R colour names
s2 <- ggplot() + geom_point(data=scatterdata, aes(x=xnew, y=y), colour="blue") +
  ggtitle("Colours are cool")
s2

plot of chunk pointssimple


#' Specify colours manually using hex values
s3 <- ggplot() + 
  geom_point(data=scatterdata, aes(x=xnew, y=y, colour=class, fill=class)) + 
  scale_colour_manual(values=c("#FF0000", "#0000FF")) + 
  scale_fill_manual(values=c("#FF0000", "#0000FF")) +
  ggtitle("Manual colour/fill scales")
s3

plot of chunk pointssimple


#' Categorical colour scales 
s4 <- ggplot() + geom_point(data=scatterdata, aes(x=xnew, y=y, colour=xnew, fill=xnew)) +
  ggtitle("Categorical colour/fill scales")
s4

plot of chunk pointssimple


#' Color by x*y axis (no binning)
s6 <- ggplot() + geom_point(data=scatterdata, aes(x=x, y=y, color=x*y, fill=x*y)) +
  ggtitle("Continuous color scales")
s6

plot of chunk pointssimple


gg2animint(list(s1=s1, s2=s2, s3=s3, s4=s4, s6=s6), out.dir="geoms/simplepoints")

Animint plot

geom_jitter

s5 <- ggplot() + geom_jitter(data=scatterdata, aes(x=xnew, y=y, colour=class4, fill=class4)) +
  ggtitle("geom_jitter")
s5

plot of chunk jitterplots

gg2animint(list(s5=s5), out.dir="geoms/jitterpoints")

Animint plot

Interactive plots and scale_size

With showSelected, it is sometimes useful to have two copies of a geom - one copy with low alpha that has no showSelected or clickSelects attributes, and another copy that is interactive. This allows the data to be visible all the time while still utilizing the interactivity of d3.

library(plyr)
scatterdata2 <- data.frame(x=rnorm(1000, 0, .5), y=rnorm(1000, 0, .5))
scatterdata2$quad <- c(3, 4, 2, 1)[with(scatterdata2, (3+sign(x)+2*sign(y))/2+1)]
scatterdata2$quad <- factor(scatterdata2$quad, labels=c("Q1", "Q2", "Q3", "Q4"), ordered=TRUE)
scatterdata2 <- ddply(scatterdata2, .(quad), transform, str=sqrt(x^2+y^2)/4)
scatterdata2.summary <- ddply(scatterdata2, .(quad), summarise, xmin=min(x), xmax=max(x), ymin=min(y), ymax=max(y), xmean=mean(x), ymean=mean(y))
qplot(data=scatterdata2, x=x, y=y, geom="point", colour=quad)

plot of chunk sizepoints


#' Interactive plots...
s7 <- ggplot() + 
  geom_rect(data=scatterdata2.summary, aes(xmax=xmax, xmin=xmin, ymax=ymax, ymin=ymin, 
                                           colour=quad, fill=quad,
                                           clickSelects = quad), alpha=.3) +
  geom_point(data=scatterdata2.summary, aes(x=xmean, y=ymean, colour=quad, fill=quad, showSelected = quad), size=5) +
  geom_point(data=scatterdata2, aes(x=x, y=y), alpha=.15) + 
  scale_colour_discrete(guide="legend") + scale_fill_discrete(guide="legend") +
  scale_alpha_discrete(guide="none") +
  ggtitle("Selects & Means")
s7

plot of chunk sizepoints


#' Single alpha value
s8 <- ggplot() + 
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad),alpha=.2)+
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, 
                                    clickSelects=quad, showSelected=quad), alpha=.6) +
  guides(colour = guide_legend(override.aes = list(alpha = 1)), 
         fill = guide_legend(override.aes = list(alpha = 1))) +
  ggtitle("Constant alpha")
s8
## Warning: Duplicated override.aes is ignored.

plot of chunk sizepoints


#' Continuous alpha
s9 <- ggplot() +
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad),alpha=.2)+
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, 
                                    alpha=str, clickSelects=quad, showSelected=quad)) +
  guides(colour = guide_legend(override.aes = list(alpha = 1)), 
         fill = guide_legend(override.aes = list(alpha = 1))) +
  scale_alpha(range=c(.6, 1), guide="none") +
  ggtitle("Continuous alpha")
s9
## Warning: Duplicated override.aes is ignored.

plot of chunk sizepoints


#' Categorical alpha and scale_alpha_discrete()
#' Note, to get unselected points to show up, need to have two copies of geom_point: One for anything that isn't selected, one for only the selected points.
s10 <- ggplot() + 
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, alpha=quad))+
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, 
                                    alpha=quad, clickSelects=quad, showSelected=quad)) +
  guides(colour = guide_legend(override.aes = list(alpha = 1)), 
         fill = guide_legend(override.aes = list(alpha = 1))) +
  scale_alpha_discrete(guide="none")+
  ggtitle("Discrete alpha")
s10
## Warning: Duplicated override.aes is ignored.

plot of chunk sizepoints



#' Point Size Scaling
#' Scale defaults to radius, but area is more easily interpreted by the brain (Tufte).
s11 <- ggplot() + 
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, size=str), alpha=.5) +
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, 
                                    size=str, clickSelects=quad, showSelected=quad), alpha=.3) +
  ggtitle("Scale Size")
s11

plot of chunk sizepoints



s12 <- ggplot() + 
  geom_point(data=scatterdata2, aes(x=x, y=y, colour=quad, fill=quad, size=str), alpha=.5) + 
  scale_size_area() +
  ggtitle("Scale Area")
s12

plot of chunk sizepoints


gg2animint(list(s7=s7, s8=s8, s9=s9, s10=s10, s11=s11, s12=s12), out.dir="geoms/interactivepoints")
## Warning: Duplicated override.aes is ignored. Warning: Duplicated
## override.aes is ignored. Warning: Duplicated override.aes is ignored.

Animint plots

Other geoms

library(ggplot2)
library(plyr)
library(animint)

geom_abline

xydata <- data.frame(x=sort(runif(50, 0, 10)))
xydata$y <- 3+2*xydata$x + rnorm(50, 0, 1)
g1 <- ggplot() + geom_point(data=xydata, aes(x=x, y=y)) + 
  geom_abline(data=data.frame(intercept=c(3, 0), slope=c(2,1)), aes(intercept=intercept, slope=slope)) +
  ggtitle("geom_abline")
g1

plot of chunk abline

gg2animint(list(g1=g1), out.dir="geoms/abline")

Animint plot

geom_ribbon

ribbondata <- data.frame(x=seq(0, 1, .1), ymin=runif(11, 0, 1), ymax=runif(11, 1, 2))
ribbondata <- rbind(cbind(ribbondata, group="low"), cbind(ribbondata, group="high"))
ribbondata[12:22,2:3] <- ribbondata[12:22,2:3]+1
g2 <- ggplot() + 
  geom_ribbon(data=ribbondata, aes(x=x, ymin=ymin, ymax=ymax, group=group, fill=group), alpha=.5) + 
  ggtitle("geom_ribbon")
g2

plot of chunk ribbon

gg2animint(list(g2=g2), out.dir="geoms/ribbon")

Animint plot

geom_tile

tiledata <- data.frame(x=rnorm(1000, 0, 3))
tiledata$y <- rnorm(1000, tiledata$x, 3)
tiledata$rx <- round(tiledata$x)
tiledata$ry <- round(tiledata$y)
tiledata <- ddply(tiledata, .(rx,ry), summarise, n=length(rx))

g3 <- ggplot() + geom_tile(data=tiledata, aes(x=rx, y=ry, fill=n)) +
  scale_fill_gradient(low="#56B1F7", high="#132B43") + 
  xlab("x") + ylab("y") + ggtitle("geom_tile")
g3

plot of chunk tile

gg2animint(list(g3=g3), out.dir="geoms/tile")

Animint plot

geom_path

pathdata <- data.frame(x=rnorm(30, 0, .5), y=rnorm(30, 0, .5), z=1:30)
g4 <- ggplot() + geom_path(data=pathdata, aes(x=x, y=y), alpha=.5) +
  geom_text(data=pathdata, aes(x=x, y=y, label=z)) + 
  ggtitle("geom_path")
g4

plot of chunk path

gg2animint(list(g4=g4), out.dir="geoms/path")

Animint plot

geom_polygon

polydata <- rbind(
  data.frame(x=c(0, .5, 1, .5, 0), y=c(0, 0, 1, 1, 0), group="parallelogram", fill="blue", xc=.5, yc=.5),
  data.frame(x=c(.5, .75, 1, .5), y=c(.5, 0, .5, .5), group="triangle", fill="red", xc=.75, yc=.33)
  )
g5 <- ggplot() + 
  geom_polygon(data=polydata, aes(x=x, y=y, group=group, fill=fill, colour=fill), alpha=.5)+
  scale_colour_identity() + scale_fill_identity()+
  geom_text(data=polydata, aes(x=xc, y=yc, label=group)) +
  ggtitle("geom_polygon")
g5

plot of chunk polygons

gg2animint(list(g5=g5), out.dir="geoms/polygon")

Animint plot

geom_linerange

boxplotdata <- rbind(data.frame(x=1:50, y=sort(rnorm(50, 3, 1)), group="N(3,1)"),
                     data.frame(x=1:50, y=sort(rnorm(50, 0, 1)), group="N(0,1)"), 
                     data.frame(x=1:50, y=sort(rgamma(50, 2, 1/3)), group="Gamma(2,1/3)"))
boxplotdata <- ddply(boxplotdata, .(group), transform, ymax=max(y), ymin=min(y), med=median(y))

g6 <- ggplot() + 
  geom_linerange(data=boxplotdata, aes(x=factor(group), ymax=ymax, ymin=ymin, colour=factor(group))) +
  ggtitle("geom_linerange") + scale_colour_discrete("Distribution") + xlab("Distribution")
g6

plot of chunk linerange

gg2animint(list(g6=g6), out.dir="geoms/linerange")

Animint plot

geom_histogram

g7 <- ggplot() + 
  geom_histogram(data=subset(boxplotdata, group=="Gamma(2,1/3)"), aes(x=y, fill=..count..), binwidth=1) + 
  ggtitle("geom_histogram")
g7

plot of chunk histogram

gg2animint(list(g7=g7), out.dir="geoms/histogram")

Animint plot

geom_violin

g8 <- ggplot() + 
  geom_violin(data=boxplotdata, aes(x=group, y=y, fill=group, group=group)) +
  ggtitle("geom_violin")+ scale_fill_discrete("Distribution") + xlab("Distribution")
g8

plot of chunk violin

gg2animint(list(g8=g8), out.dir="geoms/violin")

Animint plot

geom_step

g9 <- ggplot() + geom_step(data=boxplotdata, aes(x=x, y=y, colour=factor(group), group=group)) +
  scale_colour_discrete("Distribution") +
  ggtitle("geom_step")
g9

plot of chunk step

gg2animint(list(g9=g9), out.dir="geoms/step")

Animint plot

geom_contour

library(reshape2) # for melt
contourdata <- melt(volcano)
names(contourdata) <- c("x", "y", "z")
g11 <- ggplot() + geom_contour(data=contourdata, aes(x=x, y=y, z=z), binwidth=4, size=0.5) + 
  geom_contour(data=contourdata, aes(x=x, y=y, z=z), binwidth=10, size=1) +
  ggtitle("geom_contour")
g11

plot of chunk contour


gg2animint(list(g11=g11), out.dir="geoms/contour")

Animint plot

contourdata2 <- floor(contourdata/3)*3 # to make fewer tiles

g12 <- ggplot() + 
  geom_tile(data=contourdata2, aes(x=x, y=y, fill=z, colour=z)) + 
  geom_contour(data=contourdata, aes(x=x, y=y, z=z), colour="black", size=.5) +
  scale_fill_continuous("height", low="#56B1F7", high="#132B43", guide="legend") +
  scale_colour_continuous("height", low="#56B1F7", high="#132B43", guide="legend") +
  ggtitle("geom_tile + geom_contour") 
g12

plot of chunk contourtile


gg2animint(list(g12=g12), out.dir="geoms/contour2")

Animint plot. You may have to wait a moment to see the tiles - as each tile is a separate d3 object, it can take a few seconds to load all of the tiles.

scale_y_log10 and geom_contour with stat_density2d

While stat_density2d does not always work reliably, we can still use the statistic within another geom, such as geom_contour.

library("MASS")
data(geyser,package="MASS")
g13 <- ggplot() +  
  geom_point(data=geyser, aes(x = duration, y = waiting)) + 
  geom_contour(data=geyser, aes(x = duration, y = waiting), colour="blue", size=.5, stat="density2d") + 
  xlim(0.5, 6) + scale_y_log10(limits=c(40,110)) +
  ggtitle("geom_contour 2d density")
g13