質問

This is basically the same question this one, but with one important difference: I want a ggplot2-based tile plot with horizontal panels, and where all of the tiles are of equal height. The other question was about vertical panels.

Here’s some example code, based on the one in the other question:

d = data.frame(sites=rep(paste("S", 1:31),each=12),
               month=factor(rep(1:12,31)),
               value=runif(31*12),
               panel=c(rep("Group 1",16*12), rep("Group 2", 12*12),
                       rep("Group 3", 3*12)))

Plotting this using

ggplot(d, aes(x=month, y=sites, fill=value)) + 
  geom_tile(colour="white") + facet_wrap(~panel, nrow=1)

results in enter image description here

Basically, I want each block of blue tiles moved up, so that there is no space above them. I can achieve this using

ggplot(d, aes(x=month, y=sites, fill=value, colour="white")) + 
  geom_tile(colour="white") + facet_wrap(~panel, scales="free_y", nrow=1)

but this results in tiles of unequal height:

enter image description here

The other question had a nice solution for vertical panels, but applying this to the above code has no effect. Is there a similar solution for horizontal panels?

役に立ちましたか?

解決

Here is a hack using gridExtra and setting up levels:

d.splt <- split(d, d$panel)
max.unique <- max(sapply(d.splt, function(x) length(unique(x$sites))))
d.gg <- lapply(d.splt, function(d.sub){
  lvls <- unique(as.character(d.sub$sites))
  length(lvls) <- max.unique
  lvls <- replace(lvls, is.na(lvls), "")
  d.sub$sites <- factor(as.character(d.sub$sites), levels=lvls)

  ggplot(d.sub, aes(x=month, y=sites, fill=value, colour="white")) + 
    geom_tile(colour="white", stat="identity") +
    scale_y_discrete(drop=F) + scale_fill_continuous(guide=F)
} )
library(gridExtra)
do.call(grid.arrange, c(d.gg, list(nrow=1)))

enter image description here

This will throw some warnings, but you can ignore them. Also, you'll need to add titles and also a legend (you can mess with the logic so the last one produces a legend).

The main issue with this is that the color scale will be fit for each graph independently, but there you can force that to be fixed.

他のヒント

I’ll accept @BrodieG’s solution, since that is the most general solution to my question, but I’ll add my own, for the special case where the actual row/site names don’t matter, only the (within-panel) order of the tile rows. Here we can use a hack where we change the level names in all the subgroups to the ones used in the group with the largest number of rows. Example data and original plot:

# Example data
set.seed(7)
d = data.frame(sites=rep(paste("S", 1:31),each=12),
               month=factor(rep(1:12,31)),
               value=runif(31*12),
               panel=c(rep("Group 1",16*12),
                       rep("Group 2", 12*12),
                       rep("Group 3", 3*12)))

# Reorder rows / site names (just to show that the code works properly)
d$sites = reorder(d$sites, d$value, mean)

# Original plot
library(ggplot2)
ggplot(d, aes(x=month, y=sites, fill=value)) + 
  geom_tile(colour="white") + facet_wrap(~panel, nrow=1)

enter image description here

Now we only need to change the site names of each subgroup:

# Fetch the name of the group with the most rows
library(plyr)
d.stat = ddply(d, .(panel), summarise, nrows=length(unique(sites)))
maxpanel = with(d.stat, panel[which.max(nrows)])

# Fetch the levels / rownames of the group with the most rows
levels.maxpanel = levels(droplevels(subset(d, panel==maxpanel))$sites)

# Substitute the levels / row names of the
# biggest group for each subgroup
subs.levels = function(d.sub) {
  levels.subpanel = rev(levels(droplevels(d.sub$sites)))
  d.sub$sites[] = rev(levels.maxpanel)[match(d.sub$sites, levels.subpanel)]
  d.sub
}
d.recoded = ddply(d, .(panel), subs.levels)

# New plot
ggplot(d.recoded, aes(x=month, y=sites, fill=value)) + 
  geom_tile(colour="white") + facet_wrap(~panel, nrow=1) +
  theme(axis.text.y=element_blank()) 

enter image description here

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top