R: Ggplot2 Make Two Geom_Tile Plots Have Equal Height

R ggplot2 geom_tile Several graphs in the same plot

The trick is to gather your data from its wide format to a longer version where instead of a column for each variable, you have one column that indicates which variable is referred to, and another the value for that variable. Then the grammar of graphics looks after it for you.

library(tidyr)

data_long <- gather(data, variable, value, -Year, -Player)

ggplot(data_long, aes(y = factor(Year), x = variable, fill = value)) +
geom_tile(color = "white", width = 0.9,height = 0.4)

You will need more colours of course for the different levels than you currently have.

I don't know what you intend to do with more players but you will probably want to use `+ facet_wrap(~Player) when you get their values - keeping everything in a long thing data frame of the same type as here.

Setting specific tile size for geom_tile between two graphs

If the two matrices have at least one dimension that is equal, then this is accomplished directly by egg::ggarrange(), which lines up the plot panels:

## Save plots into variables
gg1 <- melt(mat_lg) %>% ggplot(...)
gg2 <- melt(mat_sm) %>% ggplot(...)

## Let ggarrange line up the panels
egg::ggarrange( gg1, gg2 )

Sample Image

If dimensions of the two matrices differ entirely, use egg::set_panel_size to adjust the panel sizes explicitly:

p1 <- egg::set_panel_size( gg1, height=unit(ncol(mat_lg), "cm"),
width=unit(nrow(mat_lg), "cm") )
p2 <- egg::set_panel_size( gg2, height=unit(ncol(mat_sm), "cm"),
width=unit(nrow(mat_sm), "cm") )
cowplot::plot_grid( p1, p2, nrow=1 )

Sample Image

geom_tile and facet_grid/facet_wrap for same height of tiles

You can look at what ggplot contains before plotting, and rescale the panels accordingly.

g <- ggplot_build(p) 
## find out how many y-breaks are in each panel
## to infer the number of tiles
vtiles <- sapply(lapply(g$panel$ranges, "[[", "y.major"), length)

## convert the plot to a gtable object
gt <- ggplot_gtable(g)
## find out which items in the layout correspond to the panels
## we refer to the "t" (top) index of the layout
panels <- gt$layout$t[grepl("panel", gt$layout$name)]
## replace the default panel heights (1null) with relative sizes
## null units scale relative to each other, so we scale with the number of tiles
gt$heights[panels] <-lapply(vtiles, unit, "null")
## draw on a clean slate
library(grid)
grid.newpage()
grid.draw(gt)

Sample Image

ggplot2 heatmap with tile height and width as aes()

The issue is that your tiles are overlapping. The reason is that while you could pass the width and the heights as aesthetics, geom_tile will not adjust the x and y positions of the tiles for you. As your are mapping a discrete variable on x and y your tiles are positioned on a equidistant grid. In your case the tiles are positioned at .5, 1.5 and 2.5. The tiles are then drawn on these positions with the specified width and height.

This could be easily seen by adding some transparency to your plot:

library(ggplot2)
library(dplyr)

ggplot(plot_df,
aes(x = destination,
y = origin)) +
geom_tile(
aes(
width = w_destination,
height = w_origin,
fill = value), color = "black", alpha = .2)

Sample Image

To achieve your desired result you have to manually compute the x and y positions according to the desired widths and heights to prevent the overlapping of the boxes. To this end you could switch to a continuous scale and set the desired breaks and labels via scale_x/y_ continuous:

breaks <- wdf %>% 
mutate(cumw = cumsum(w),
pos = .5 * (cumw + lag(cumw, default = 0))) %>%
select(region, pos)

plot_df <- plot_df %>%
left_join(breaks, by = c("origin" = "region")) %>%
rename(y = pos) %>%
left_join(breaks, by = c("destination" = "region")) %>%
rename(x = pos)

ggplot(plot_df,
aes(x = x,
y = y)) +
geom_tile(
aes(
width = w_destination,
height = w_origin,
fill = value), color = "black") +
scale_x_continuous(breaks = breaks$pos, labels = breaks$region, expand = c(0, 0.1)) +
scale_y_continuous(breaks = breaks$pos, labels = breaks$region, expand = c(0, 0.1))

Sample Image

How to adjust the tile height in geom tile?

geom_tile takes the aesthetic height=h and generates ymin and ymax from -h/2 to h/2. This is why you aren't getting plot you intend. While I fully support @Didzis' solution as it is extremely straightforward and gets the job done, I'll show the solution using geom_tile for two reasons. It's fun and always nice to know :).

The objective is to "generate" your y positions accordingly depending on the height so that the plot is as you expect. Taking test2 data.frame,

require(plyr)
# calculate y coordinate accounting for shift in y due to h
test2 <- ddply(test2, .(x), transform, y2 = c(0, head(h,-1)) + h/2)
p <- ggplot(test2, aes(x=x, y=y2, fill = z)) +
geom_tile(aes(width = w, height=h))
p

Sample Image

Now, you see that the heights are neatly generated (ymin and ymax)

ggplot_build(p)$data

# fill x y PANEL group xmin xmax ymin ymax
# 1 #00BFC4 1 0.50 1 4 0.75 1.25 0.0 1.0
# 2 #619CFF 1 2.50 1 5 0.75 1.25 1.0 4.0
# 3 #00BA38 3 1.50 1 3 2.00 4.00 0.0 3.0
# 4 #F8766D 3 3.50 1 1 2.00 4.00 3.0 4.0
# 5 #B79F00 6 0.75 1 2 5.50 6.50 0.0 1.5
# 6 #F564E3 6 2.75 1 6 5.50 6.50 1.5 4.0


Related Topics



Leave a reply



Submit