I have been playing around with tracking software and the ways to visualize the position of an animal in an arena over time. Even with normal cameras (30 Hz) and relatively small periods of time (~5 min) we get massive datasets and the only way to make sense of them is to aggregate data over time. I have been interested in neat visualizations (using R), thus here are some ways/comments. I will explore different packages and doing comparisons of the results.




I have an example dataset with XY position of the animal. I also have other morphological data that is irrelevant for this case. See example below:

##          X        Y Orientation MinorAxis MajorAxis
## 1 325.5522 158.9700   -1.550285  50.47925  130.5679
## 2 323.3055 156.6896   -1.569706  51.29840  130.3467
## 3 321.7411 154.8107    1.545683  52.13492  130.3881
## 4 320.8373 153.3294    1.512549  52.67011  130.6403
## 5 320.0381 152.5523    1.477299  53.07783  130.1023
## 6 319.3961 152.2019    1.439171  53.75891  129.6706

I will do some setup that is common for all the graphs (aka, color palette and the dimensions of the arena).

# Get color palette 

refCol <- colorRampPalette(rev(brewer.pal(6,'Spectral')))
mycol <- refCol(6)

# define bin sizes
bin_size <- 40

# Camera resolution is 640x480. Hence...
xbins <- 640/bin_size 
ybins <- 480/bin_size


The reason I always go to ggplot2 first is because it’s awesome, I buy into the grammar and find it intuitive to accumulate layers over layers. The underlying thought is that ggplot2 handles all problems. In this case the result has some pros (layers, layers and more layer), and some cons (basically, it doesn’t look amazing).

p <- ggplot(rat_pos, aes(X,Y)) +
  stat_density2d(geom = 'tile', aes(fill = ..density..), contour = FALSE,
                 n = c(xbins, ybins)) +
  #geom_point() + 
  #geom_path(alpha=0.1) +
  coord_equal() +
  theme_minimal() +
  scale_fill_gradientn(colors =  mycol) +
  geom_vline(xintercept = c(0,640))+
  geom_hline(yintercept = c(0,480))


This is interesting because we can overlay things into the plot. For example the trace:

p + geom_path(alpha=0.1)

We can further remove the axis (or any other modifications we feel like doing).

p + geom_path(alpha=0.5) + theme_void()

I found that, if we calculate the density externally, it looks smoother. This is a mixed, bkde2D mediated, ggplot2 approach (aka the best of 2 worlds).

bins <- bkde2D(as.matrix(rat_pos[,1:2]), bandwidth = c(xbins, ybins),
               gridsize = c(640L, 480L))

bins_to_plot <- melt(bins$fhat)

ggplot(bins_to_plot, aes(Var1, Var2, fill = value)) +
  coord_equal() +
  theme_minimal() +
  scale_fill_gradientn(colors =  mycol) +
  geom_vline(xintercept = c(0,640))+
  geom_hline(yintercept = c(0,480))+

Using raster package

Raster package is maybe an older solution, which is surprisingly low demand. In 3 code lines we get a perfectly functional plot. On the other hand, it’s not the best looking graph and we get the caveats (yes, base graphics).

r <- raster(xmn=0, ymn=0, xmx=640, ymx=480, res=20)
x <- rasterize(rat_pos[,1:2], r, fun='count')
plot(x, xlim=c(0,640), ylim=c(0,480), xaxt='n', ann=FALSE, yaxt='n')

Using gplots package

This package produces a nice plot. I must confess I did not look to much into it because it’s really similar to what ggplot2 can do with tiles. I was biased towards a smoother version of the world.

# create plot
q <- hist2d(rat_pos[,1:2], same.scale=TRUE,
       nbins=c(xbins, ybins),
       col = mycol, xaxt='n', ann=FALSE, yaxt='n')


I have found a good amount of advice and inspiration in the links below.