Colouring brain surface with heat map - r

library(rgl)
library(brainR)
template <- readNIfTI(system.file("MNI152_T1_2mm_brain.nii.gz",
package = "brainR"), reorient = FALSE)
misc3d::contour3d(template, level = 4500, alpha = .7, draw = T)
With the above code one can can generate a 3D model of the brain.
The argument draw = FALSE asks contour3d to compute and return the contour surface as a triangle mesh object without drawing it.
a <- misc3d::contour3d(template, level = 4500, alpha = .7, draw = F)
str(a)
List of 10
$ v1 : num [1:110433, 1:3] 45 45 46 46 47 47 43 43 44 44 ...
$ v2 : num [1:110433, 1:3] 44.1 46 46 47 47 ...
$ v3 : num [1:110433, 1:3] 45 45 45 46 46 ...
$ color : chr "white"
$ color2 : logi NA
$ fill : logi TRUE
$ material: chr "default"
$ col.mesh: logi NA
$ alpha : num 0.7
$ smooth : num 0
- attr(*, "class")= chr "Triangles3D"
I would like to use the external surface of the above object to project heat maps, or say, colouring the surface... I also wonder how to determine certain positions in this model, e.g. EEG channel positions. Is it possible to generate only the surface with a$v1, a$v2 ... using function rgl:::surface3d? Thank you in advance,

I was able to draw the triangle mesh in colors with rgl. Some considerations:
I'm using rgl::triangles3d With this function, points are taken in consecutive triplets, each point v1 v2 v3 a triangle vertex (see
?triangles3d) so i had to extract the points and reorder them.
colors are mapped to vertex, taken in groups of three also. I created a simple color_map based on the x position of each vertex. Of course you must create your desired color map.
Hope this will help you.
data<-misc3d::contour3d(template, level = 4500, alpha = .7, draw = F)
points <- rbind(data$v1, data$v2, data$v3)
points <- points[order(rep(1:(nrow(points)/3),3), rep(1:3, nrow(points)/3)),]
color_map <- c("red","blue","green")[cut(points[,1], 3, labels=F)]
rgl::open3d()
rgl::triangles3d(points, alpha=.7, color = color_map)
Edit
Coordinates x, y, z are in the scale of the positions of the array template i.e. point c(5.4, 10.8, 30.1) is related to the level of the array around [5, 10, 30], that is, that point should be around level=4500

Related

Why the red line in histogram is too short?

test <- NULL
for(i in 1:1000){
p <- rgamma(1,239,10)
yrep <- rpois (10,p)
test <- c(test,yrep)}
hist (test, xlab="T (yrep)", yaxt="n", cex=1,col = "yellow")
lines(rep(22,2), col="red", c(0,100))
print(mean(test<=22))
I got
But why the red line cannot exceed the histogram? How to edit my code to let the red line be normal?
You can try abline instead:
test <- NULL
for(i in 1:1000){
p <- rgamma(1,239,10)
yrep <- rpois (10,p)
test <- c(test,yrep)}
hist (test, xlab="T (yrep)", yaxt="n", cex=1,col = "yellow")
abline(v=22, col="red")
#Vincent's answer fixes the problem using abline. But if you need to know how high to go (perhaps you don't want a full-vertical line), then here's "why":
First, hist(.) returns a list that includes some meta about the histogram.
set.seed(42)
# test <- ...
h <- hist (test, xlab="T (yrep)", yaxt="n", cex=1,col = "yellow")
str(h)
# List of 6
# $ breaks : int [1:20] 8 10 12 14 16 18 20 22 24 26 ...
# $ counts : int [1:19] 18 70 191 405 812 1154 1554 1545 1358 1084 ...
# $ density : num [1:19] 0.0009 0.0035 0.00955 0.02025 0.0406 ...
# $ mids : num [1:19] 9 11 13 15 17 19 21 23 25 27 ...
# $ xname : chr "test"
# $ equidist: logi TRUE
# - attr(*, "class")= chr "histogram"
The y-axis is defined off of the $counts variable, so we can see that it goes up to at least 1554.
Another way to see what the axis is doing is with
par("usr")
# [1] 6.48 47.52 -62.16 1616.16
This tells us that the x-axis ranges from 6.48 to 47.52, and the y-axis ranges from -62.16 to 1616.16. (The reason y includes negative values is that by default, R expands the plot by 4% in both directions.) From this, you could know that your line would need to span from 0 (or -62.16 if you wanted to start at the true bottom) to 1616.16 (or around up). This says that our look at h$counts would have ended near the top of the hist bars but not at the top of the plotted region.

Drawing Dendogram using R with Agglomerative hierarchical clustering (AHC) techniques with Complete link method

I have calculated the Distance matrix with the complete link method as shown in the image below:
The pairwise distance betwwen the clusters are
{0.5,1.12,1.5,3.61}
But While implementing with the same matrix in R with the code below:
Matrix
x1,x2,x3,x4,x5
0,0.5,2.24,3.35,3
0.5,0,2.5,3.61,3.04
2.24,2.5,0,1.12,1.41
3.35,3.61,1.12,0,1.5
3,3.04,1.41,1.5,0
Implementation:
library(cluster)
dt<-read.csv("cluster.csv")
df<-scale(dt[-1])
dc<-dist(df,method = "euclidean")
hc1 <- hclust(dc, method = "complete" )
plot(hc1, labels = c("x1", "x2","x3","x4","x5"),
hang = 0.1,
main = "Cluster dendrogram", sub = NULL,
xlab = NULL, ylab = "Height")
abline(h = hc1$height, lty = 2, col = "lightgrey")
str(hc1)
str(hc1)
List of 7
$ merge : int [1:4, 1:2] -1 -3 -5 1 -2 -4 2 3
$ height : num [1:4] 0.444 1.516 1.851 3.753
$ order : int [1:5] 1 2 5 3 4
$ labels : NULL
$ method : chr "complete"
$ call : language hclust(d = dc, method = "complete")
$ dist.method: chr "euclidean"
- attr(*, "class")= chr "hclust"
I have got the height as: 0.444 1.516 1.851 3.753
Which means the dendogram will be different in both cases, why is that different in both cases? May be i have done something wrong on the implementing on both ways?
Since the provided matrix is the euclidean distance matrix, so i don't need to calculate the distance matrix: rather i should convert the data.frame to dist.matrix. and to as.dist(m).
The below code will give me the exact result which was obtained from the paper calculation:
library(reshape)
dt<-read.csv("C:/Users/Aakash/Desktop/cluster.csv")
m <- as.matrix(dt)
hc1 <- hclust(as.dist(m), method = "complete" )
plot(hc1, labels = c("x1", "x2","x3","x4","x5"),
hang = 0.1,
main = "Complete Method Dendogram", sub = NULL,
xlab = "Items", ylab = "Height")
abline(h = hc1$height, lty = 2, col = "lightgrey")
str(hc1)
height : num [1:4] 0.5 1.12 1.5 3.61
Obtained Dendogram:

Adding point locations to a 3D DEM plot in R

I have some point locations which include UTMs and Elevation as a data frame
I also have a DEM layer.
I have figured out how to plot the DEM in 3D using plot3D in rgl.
I can also plot the points in 3D using points3d.
I have been able to put them in the same plot using points3d with add=TRUE
however the points and DEM are radically far away from each other.
In the code below I also tried to change this to a spatial data frame but rgl doesn't seem to like that.
Is it possible to plot them together with the points laying over the DEM?
I have searched and searched for a solution to this.
Here is the R code I have used so far:
> library(raster)
> library(rgdal)
> library(maptools)
> library(rgeos)
> library(lattice)
> library(latticeExtra)
> library(sp)
> library(rasterVis)
> library(rgl)
>
> # taking data read from a .csv of UTM and elevation values
>
> Points.Sp <- data.frame(Points=Rawdata$PointName, UTM.N=Rawdata$UTM.N, UTM.W=Rawdata$UTM.W, Elevation=Rawdata$Elevation)
> Points.Sp <- unique(Points.Sp) #weeding out duplicates
> Points.Sp <- Points.Sp[,c(3,2,4)] #getting rid of point names # I realize this looks messy but it gets what I want
> head(Points.Sp)
UTM.W UTM.N Elevation
1 275815 3879223 1340
8 274813 3879727 1325
29 275312 3879727 1258
45 275812 3879724 1169
66 276313 3879727 1067
75 276813 3879727 1208
>
> dem.in <- raster("D:/Thesis/SouthernApps/Coweeta/Coweeta/DEM_30m_wgs84.img") # reading in DEM
> plot(dem.in) # check in 2D # takes a long time very large, need to crop
>
> dem.crop <- crop(dem.in, c(272000, 282000, 3878000, 3884000))
> plot(dem.crop) # check in 2D, looks good.
>
> plot3D(dem.crop) # plot in 3D looks like exactly what I want
>
> points3d(Points.Sp, pch=19, cex=2, col="black", add=TRUE) # adds the points to plot but in wrong place
>
> #attempting to set a CRS in case this is the problem.
> coordinates(Points.Sp)=c(1,2)
> proj4string(Points.Sp)=CRS("++proj=utm +zone=17") # set CRS
> str(Points.Sp)
Formal class 'SpatialPointsDataFrame' [package "sp"] with 5 slots
..# data :'data.frame': 71 obs. of 1 variable:
.. ..$ Elevation: int [1:71] 1340 1325 1258 1169 1067 1208 1256 1089 1031 959 ...
..# coords.nrs : num [1:2] 1 2
..# coords : num [1:71, 1:2] 275815 274813 275312 275812 276313 ...
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : chr [1:71] "1" "8" "29" "45" ...
.. .. ..$ : chr [1:2] "UTM.W" "UTM.N"
..# bbox : num [1:2, 1:2] 274309 3878440 279876 3883732
.. ..- attr(*, "dimnames")=List of 2
.. .. ..$ : chr [1:2] "UTM.W" "UTM.N"
.. .. ..$ : chr [1:2] "min" "max"
..# proj4string:Formal class 'CRS' [package "sp"] with 1 slot
.. .. ..# projargs: chr "+proj=utm +zone=17 +ellps=WGS84"
>
> # trying this a different way after setting CRS
> x <- Points.Sp#coords[1:71,1]
> y <- Points.Sp#coords[1:71,2]
> z <- Points.Sp#data$Elevation
> m <- data.frame(x=x,y=y,z=z)
>
> plot3D(dem.crop) #again, plot in 3D looks like exactly what I want
> points3d(m, pch=19, cex=2, col="black", add=TRUE) # still adds the points to plot but in wrong place
This code reproduces the problem.
## define a Raster object
data(volcano)
r <- raster(volcano)
extent(r) <- c(0, 610, 0, 870)
## extract sample points
xy <- sampleRandom(r1, 100, xy = TRUE)
r1<-data.frame(x=seq(0, 500, length=(71)), y=seq(0, 500, length=(71)), z=seq(0,500, length=(71)))
## display them
plot3D(r, adjust = FALSE)
points3d(r1, add=TRUE)
As documented in the help page, both the x-axis and y-axis are adjusted with the z values. You can disable this default setting with adjust = FALSE:
library(rgl)
library(rasterVis)
## define a Raster object
data(volcano)
r <- raster(volcano)
extent(r) <- c(0, 610, 0, 870)
## extract sample points
xy <- sampleRandom(r, 100, xy = TRUE)
## display them
plot3D(r, adjust = FALSE)
points3d(xy)
## define a Raster object
data(volcano)
r <- raster(volcano)
extent(r) <- c(0, 610, 0, 870)
## extract sample points
xy <- sampleRandom(r1, 100, xy = TRUE)
#must extract the data from the raster and recombine with the xy data.
#I don't know why this is different than simply using the raw values but it
#provides the desired effect.
r1<-data.frame(x=seq(0, 500, length=(71)), y=seq(0, 500, length=(71)))
z<-extract(r, r1)
r1$z<-z
## display them
plot3D(r, adjust = FALSE)
points3d(r1, add=TRUE)
#points now lie flat on 3d image.
Points flush to 3d Image
Image for original problem

Draw 3D plot of two classes according to 3 variables with R

I have an R data.frame:
> str(trainTotal)
'data.frame': 1000 obs. of 41 variables:
$ V1 : num 0.299 -1.174 1.192 1.573 -0.613 ...
$ V2 : num -1.227 0.332 -0.414 -0.58 -0.644 ...
etc.
$ V40 : num 0.101 -1.818 2.987 1.883 0.408 ...
$ Class: int 1 0 0 1 0 1 0 1 1 0 ...
and I would like to draw a 3D scatter plot of Class "0" in blue and Class "1" in red according to V13, V5, and V24.
V13, V5, V24 are the top variables when sorted by scaled variance, so my intuition tells me the 3D visualization could be interesting. Not sure if that makes sense.
How can I plot this with R ?
Edit:
I have tried the following:
install.packages("Rcmdr")
library(Rcmdr)
scatter3d(x=trainTotal[[13]], y= trainTotal[[5]], z= trainTotal[[24]], point.col = as.numeric(as.factor(trainTotal[,41])), size = 10)
which gives me this plot:
I am not sure how to read this plot.
I would prefer to see only dots of two colors, for a start.
Maybe something like this? Using scatterplot3d.
library(scatterplot3d)
#random data
DF <- data.frame(V13 = sample(1:100, 10, T), V5 = sample(1:100, 10, T), V24 = sample(1:100, 10, T), class = sample(0:1, 10, T))
#plot
scatterplot3d(x = DF$V13, y = DF$V5, z = DF$V24, color = c("blue", "red")[as.factor(DF$class)], pch = 19)
This gives:
In scatterplot3d there is also an angle argument for different views.
Perspective issues mean that static 3d plots are mostly horrible and misleading. If you really want a 3d scatterplot, it's best to draw one where you can view it from different angles. The rgl package allows this.
EDIT: I've updated the plot to use colours, in this case picked using the colorspace package, though you can define them however you like. Specifying attributes for points is described on the ?rgl.material help page.
library(rgl)
library(colorspace)
n_points <- 50
n_groups <- 5
some_data <- data.frame(
x = seq(0, 1, length.out = n_points),
y = runif(n_points),
z = rnorm(n_points),
group = gl(n_groups, n_points / n_groups)
)
colors <- rainbow_hcl(n_groups)
with(some_data, points3d(x, y, z, color = colors[group], size = 7))
axes3d()

Setting hex bins in ggplot2 to same size

I'm trying to make a hexbin representation of data in several categories. The problem is, facetting these bins seems to make all of them different sizes.
set.seed(1) #Create data
bindata <- data.frame(x=rnorm(100), y=rnorm(100))
fac_probs <- dnorm(seq(-3, 3, length.out=26))
fac_probs <- fac_probs/sum(fac_probs)
bindata$factor <- sample(letters, 100, replace=TRUE, prob=fac_probs)
library(ggplot2) #Actual plotting
library(hexbin)
ggplot(bindata, aes(x=x, y=y)) +
geom_hex() +
facet_wrap(~factor)
Is it possible to set something to make all these bins physically the same size?
As Julius says, the problem is that hexGrob doesn't get the information about the bin sizes, and guesses it from the differences it finds within the facet.
Obviously, it would make sense to hand dx and dy to a hexGrob -- not having the width and height of a hexagon is like specifying a circle by center without giving the radius.
Workaround:
The resolution strategy works, if the facet contains two adjacent haxagons that differ in both x and y. So, as a workaround, I'll construct manually a data.frame containing the x and y center coordinates of the cells, and the factor for facetting and the counts:
In addition to the libraries specified in the question, I'll need
library (reshape2)
and also bindata$factor actually needs to be a factor:
bindata$factor <- as.factor (bindata$factor)
Now, calculate the basic hexagon grid
h <- hexbin (bindata, xbins = 5, IDs = TRUE,
xbnds = range (bindata$x),
ybnds = range (bindata$y))
Next, we need to calculate the counts depending on bindata$factor
counts <- hexTapply (h, bindata$factor, table)
counts <- t (simplify2array (counts))
counts <- melt (counts)
colnames (counts) <- c ("ID", "factor", "counts")
As we have the cell IDs, we can merge this data.frame with the proper coordinates:
hexdf <- data.frame (hcell2xy (h), ID = h#cell)
hexdf <- merge (counts, hexdf)
Here's what the data.frame looks like:
> head (hexdf)
ID factor counts x y
1 3 e 0 -0.3681728 -1.914359
2 3 s 0 -0.3681728 -1.914359
3 3 y 0 -0.3681728 -1.914359
4 3 r 0 -0.3681728 -1.914359
5 3 p 0 -0.3681728 -1.914359
6 3 o 0 -0.3681728 -1.914359
ggplotting (use the command below) this yields the correct bin sizes, but the figure has a bit weird appearance: 0 count hexagons are drawn, but only where some other facet has this bin populated. To suppres the drawing, we can set the counts there to NA and make the na.value completely transparent (it defaults to grey50):
hexdf$counts [hexdf$counts == 0] <- NA
ggplot(hexdf, aes(x=x, y=y, fill = counts)) +
geom_hex(stat="identity") +
facet_wrap(~factor) +
coord_equal () +
scale_fill_continuous (low = "grey80", high = "#000040", na.value = "#00000000")
yields the figure at the top of the post.
This strategy works as long as the binwidths are correct without facetting. If the binwidths are set very small, the resolution may still yield too large dx and dy. In that case, we can supply hexGrob with two adjacent bins (but differing in both x and y) with NA counts for each facet.
dummy <- hgridcent (xbins = 5,
xbnds = range (bindata$x),
ybnds = range (bindata$y),
shape = 1)
dummy <- data.frame (ID = 0,
factor = rep (levels (bindata$factor), each = 2),
counts = NA,
x = rep (dummy$x [1] + c (0, dummy$dx/2),
nlevels (bindata$factor)),
y = rep (dummy$y [1] + c (0, dummy$dy ),
nlevels (bindata$factor)))
An additional advantage of this approach is that we can delete all the rows with 0 counts already in counts, in this case reducing the size of hexdf by roughly 3/4 (122 rows instead of 520):
counts <- counts [counts$counts > 0 ,]
hexdf <- data.frame (hcell2xy (h), ID = h#cell)
hexdf <- merge (counts, hexdf)
hexdf <- rbind (hexdf, dummy)
The plot looks exactly the same as above, but you can visualize the difference with na.value not being fully transparent.
more about the problem
The problem is not unique to facetting but occurs always if too few bins are occupied, so that no "diagonally" adjacent bins are populated.
Here's a series of more minimal data that shows the problem:
First, I trace hexBin so I get all center coordinates of the same hexagonal grid that ggplot2:::hexBin and the object returned by hexbin:
trace (ggplot2:::hexBin, exit = quote ({trace.grid <<- as.data.frame (hgridcent (xbins = xbins, xbnds = xbnds, ybnds = ybnds, shape = ybins/xbins) [1:2]); trace.h <<- hb}))
Set up a very small data set:
df <- data.frame (x = 3 : 1, y = 1 : 3)
And plot:
p <- ggplot(df, aes(x=x, y=y)) + geom_hex(binwidth=c(1, 1)) +
coord_fixed (xlim = c (0, 4), ylim = c (0,4))
p # needed for the tracing to occur
p + geom_point (data = trace.grid, size = 4) +
geom_point (data = df, col = "red") # data pts
str (trace.h)
Formal class 'hexbin' [package "hexbin"] with 16 slots
..# cell : int [1:3] 3 5 7
..# count : int [1:3] 1 1 1
..# xcm : num [1:3] 3 2 1
..# ycm : num [1:3] 1 2 3
..# xbins : num 2
..# shape : num 1
..# xbnds : num [1:2] 1 3
..# ybnds : num [1:2] 1 3
..# dimen : num [1:2] 4 3
..# n : int 3
..# ncells: int 3
..# call : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds)
..# xlab : chr "x"
..# ylab : chr "y"
..# cID : NULL
..# cAtt : int(0)
I repeat the plot, leaving out data point 2:
p <- ggplot(df [-2,], aes(x=x, y=y)) + geom_hex(binwidth=c(1, 1)) + coord_fixed (xlim = c (0, 4), ylim = c (0,4))
p
p + geom_point (data = trace.grid, size = 4) + geom_point (data = df, col = "red")
str (trace.h)
Formal class 'hexbin' [package "hexbin"] with 16 slots
..# cell : int [1:2] 3 7
..# count : int [1:2] 1 1
..# xcm : num [1:2] 3 1
..# ycm : num [1:2] 1 3
..# xbins : num 2
..# shape : num 1
..# xbnds : num [1:2] 1 3
..# ybnds : num [1:2] 1 3
..# dimen : num [1:2] 4 3
..# n : int 2
..# ncells: int 2
..# call : language hexbin(x = x, y = y, xbins = xbins, shape = ybins/xbins, xbnds = xbnds, ybnds = ybnds)
..# xlab : chr "x"
..# ylab : chr "y"
..# cID : NULL
..# cAtt : int(0)
note that the results from hexbin are on the same grid (cell numbers did not change, just cell 5 is not populated any more and thus not listed), grid dimensions and ranges did not change. But the plotted hexagons did change dramatically.
Also notice that hgridcent forgets to return the center coordinates of the first cell (lower left).
Though it gets populated:
df <- data.frame (x = 1 : 3, y = 1 : 3)
p <- ggplot(df, aes(x=x, y=y)) + geom_hex(binwidth=c(0.5, 0.8)) +
coord_fixed (xlim = c (0, 4), ylim = c (0,4))
p # needed for the tracing to occur
p + geom_point (data = trace.grid, size = 4) +
geom_point (data = df, col = "red") + # data pts
geom_point (data = as.data.frame (hcell2xy (trace.h)), shape = 1, size = 6)
Here, the rendering of the hexagons cannot possibly be correct - they do not belong to one hexagonal grid.
I tried to replicate your solution with the same data set using lattice hexbinplot. Initially, it gave me an error xbnds[1] < xbnds[2] is not fulfilled. This error was due to wrong numeric vectors specifying range of values that should be covered by the binning. I changed those arguments in hexbinplot, and it somehow worked. Not sure if it helps you to solve it with ggplot, but it's probably some starting point.
library(lattice)
library(hexbin)
hexbinplot(y ~ x | factor, bindata, xbnds = "panel", ybnds = "panel", xbins=5,
layout=c(7,3))
EDIT
Although rectangular bins with stat_bin2d() work just fine:
ggplot(bindata, aes(x=x, y=y, group=factor)) +
facet_wrap(~factor) +
stat_bin2d(binwidth=c(0.6, 0.6))
There are two source files that we are interested in: stat-binhex.r and geom-hex.r, mainly hexBin and hexGrob functions.
As #Dinre mentioned, this issue is not really related to faceting. What we can see is that binwidth is not ignored and is used in a special way in hexBin, this function is applied for every facet separately. After that, hexGrob is applied for every facet. To be sure you can inspect them with e.g.
trace(ggplot2:::hexGrob, quote(browser()))
trace(ggplot2:::hexBin, quote(browser()))
Hence this explains why sizes differ - they depend on both binwidth and the data of each facet itself.
It is difficult to keep track of the process because of various coordinates transforms, but notice that the output of hexBin
data.frame(
hcell2xy(hb),
count = hb#count,
density = hb#count / sum(hb#count, na.rm=TRUE)
)
always seems to look quite ordinary and that hexGrob is responsible for drawing hex bins, distortion, i.e. it has polygonGrob. In case when there is only one hex bin in a facet there is a more serious anomaly.
dx <- resolution(x, FALSE)
dy <- resolution(y, FALSE) / sqrt(3) / 2 * 1.15
in ?resolution we can see
Description
The resolution is is the smallest non-zero distance between adjacent
values. If there is only one unique value, then the resolution is
defined to be one.
for this reason (resolution(x, FALSE) == 1 and resolution(y, FALSE) == 1) the x coordinates of polygonGrob of the first facet in your example are
[1] 1.5native 1.5native 0.5native -0.5native -0.5native 0.5native
and if I am not wrong, in this case native units are like npc, so they should be between 0 and 1. That is, in case of single hex bin it goes out of range because of resolution(). This function also is the reason of distortion that #Dinre mentioned even when having up to several hex bins.
So for now there does not seem to be an option to have hex bins of equal size. A temporal (and very inconvenient for a large number of factors) solution could begin with something like this:
library(gridExtra)
set.seed(2)
bindata <- data.frame(x = rnorm(100), y = rnorm(100))
fac_probs <- c(10, 40, 40, 10)
bindata$factor <- sample(letters[1:4], 100,
replace = TRUE, prob = fac_probs)
binwidths <- list(c(0.4, 0.4), c(0.5, 0.5),
c(0.5, 0.5), c(0.4, 0.4))
plots <- mapply(function(w,z){
ggplot(bindata[bindata$factor == w, ], aes(x = x, y = y)) +
geom_hex(binwidth = z) + theme(legend.position = 'none')
}, letters[1:4], binwidths, SIMPLIFY = FALSE)
do.call(grid.arrange, plots)
I also did some fiddling around with the hex plots in 'ggplot2', and I was able to consistently produce significant bin distortion when a factor's population was reduced to 8 or below. I can't explain why this is happening without digging down into the package source (which I am reluctant to do), but I can tell you that sparse factors seem to consistently wreck the hex bin plotting in 'ggplot2'.
This suggests to me that the size and shape of a particular hex bin in 'ggplot2' is related to a calculation that is unique to each facet, instead of doing a single calculation for the group and plotting the data afterwards. This is somewhat reinforced by the fact that I can reproduce the distortion in any given facet by plotting only that single factor, like so:
ggplot(bindata[bindata$factor=="e",], aes(x=x, y=y)) +
geom_hex()
This feels like something that should be elevated to the package maintainer, Hadley Wickham (h.wickham at gmail.com). This info is publicly available from CRAN.
Update: I sent an email to the Hadley Wickham asking if he would take a look at this question, and he confirmed that this behavior is indeed a bug.

Resources