- 🎯
geom_tilepositions items from their center. This can cause gaps when widths or durations change. - ✔️
geom_rectgives exact control. It lets you set the coordinate edges directly. - 🖼️ Fixed coordinate systems (
coord_fixed) stop plots from looking stretched and reduce parts that don't line up. - 🔍 Gaps happen because of tiny math errors and changes in
ggplot2's coordinate system. - 🛠️ Developers should use
geom_rectfor timeline and interval data to make sure plots look correct.
Introduction
When making data plots in R with the ggplot2 package, developers often use geom layers like geom_tile and geom_rect. They use these to make charts such as heatmaps, timelines, Gantt charts, or interval plots. But confusion and unexpected results often happen, especially when gaps appear between tiles or bars in a plot. This article looks closely at the differences between geom_tile() and geom_rect() in ggplot2. It checks how they work, their limits, and when to use them. We will also cover ways to fix drawing problems. And we will explain how to pick the right geom for your data.
Understanding geom_tile() and geom_rect()
How geom_tile() Works: A Center-Based Geometry
The geom_tile() function is made mainly for making tile-based plots, like heatmaps. This type of plot uses the center point and size (width and height) to draw each rectangle.
Each tile's size comes from:
xandy: the center spots of the tilewidthandheight: the size of each rectangle
If you don't give width or height directly, ggplot2 guesses good default sizes based on how far apart nearby data points are.
ggplot(data, aes(x, y, fill = value)) +
geom_tile()
This way of working works very well when your data is set up in a regular grid, like temperature readings over even bins in a heatmap. But, if your data is not regular, such as when it shows durations or different widths, problems with this center-based model show up. For example, gaps and overlaps can make your plot look wrong.
How geom_rect() Works: Boundary-Based Plotting
geom_rect() works differently. Instead of using the center spots, it asks you to give:
xminandxmax: the start and end spots on the x-axisyminandymax: the start and end spots on the y-axis
This gives you full control over your data rectangles. It helps you show durations, categories, and set-up plots like Gantt charts or calendar maps correctly.
ggplot(data, aes(xmin = start, xmax = end, ymin = y - 0.5, ymax = y + 0.5)) +
geom_rect(fill = "steelblue")
This exact control helps stop drawing errors. It does this because you are clearly telling ggplot2 where each tile's corners are. And it doesn't have to calculate those from the center.
Why Gaps Appear in geom_tile()
One of the most common questions among ggplot2 users is: “Why are there white lines or gaps between my tiles?”
These gaps rarely happen because your data is out of line. More often, they happen because of how geom_tile() does its math and draws things inside, especially when:
- You plot widths or time periods that change.
- The plot scales (like when you zoom in or change it).
- Small rounding errors in math make gaps between tiles.
Let's look at a common problem:
ggplot(data, aes(x = time, y = category)) +
geom_tile(aes(width = duration)) # Might show small gaps
Even with the correct width, tiny differences in where pixels are placed because of floating-point math and scaling can cause very small gaps. You can see these gaps especially when you save your plot as a vector file, like PDF or SVG.
These drawing errors are not just about how things look. They can wrongly make it seem like there are breaks when there are none. This can mislead people reading your reports or looking at client work.
Coordinate Systems and Panel Grids
One of the very important parts of how ggplot2 plots work is the coordinate system. ggplot2 lets you use both smooth and separate axes.
With smooth coordinates, even tiny math errors can mess up how things look. And this is worse with:
- Small tile sizes.
- Large, zoomed-in scales.
- Plot sizes that change.
The coord_fixed() Solution
To stop things from looking stretched, you can use coord_fixed():
ggplot(data, aes(x, y)) +
geom_tile() +
coord_fixed()
This makes sure that each unit on the x-axis has the same actual size as a unit on the y-axis. This fix for the plot's shape is very important. It sets right the stretching that makes tiles not line up.
Discrete Axes Alignment
When you can, use separate variables for your x or y plots. Separate variables don't have bad rounding math problems. This is because ggplot lines them up by groups, not by numbers.
ggplot(data, aes(x = factor(time), y = factor(category))) +
geom_tile()
This method often makes plots that look perfect, with fewer gaps.
Case Study: Variable Tile Widths
Imagine you are building a dashboard for system use. You record when users are busy, but the times they spend vary. A plot can quickly show activity bars that overlap or have different lengths.
df <- data.frame(
y = c(1, 2, 3),
x = c(1, 2.5, 4),
width = c(1, 1.5, 0.5),
fill = c("A", "B", "C")
)
Using geom_tile() (can result in gaps)
ggplot(df, aes(x = x, y = y, width = width, fill = fill)) +
geom_tile() +
coord_fixed()
This may show small slices between the tiles, called "hairline gaps."
Using geom_rect() (eliminates gaps)
Change the center-based data into clear edges:
df$xmin <- df$x - df$width / 2
df$xmax <- df$x + df$width / 2
df$ymin <- df$y - 0.5
df$ymax <- df$y + 0.5
ggplot(df, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax, fill = fill)) +
geom_rect() +
coord_fixed()
This plot looks perfect. Use geom_rect() when you work with widths or how long things take.
Fixing the Gaps in geom_tile()
Sometimes, changing to geom_rect() is not possible. This might be because of how your code is set up or what you like to use. If so, try these fixes to reduce gaps:
1. Slight Overdraw
Increase the tile width slightly:
ggplot(df, aes(x = x, y = y, width = width + 0.01, fill = fill)) +
geom_tile() +
coord_fixed()
Be careful: if the overlap is too big, it might cover tiles next to it.
2. Use coord_fixed() for Square Ratios
This keeps the x and y scales the same. And it reduces how stretching makes things look bad.
+ coord_fixed()
3. Remove Gridlines
Use theme_void() to hide lines that might make problems look worse.
+ theme_void()
Plotting Recommendation Table
| Use Case | Preferred Geom | Why to Use It |
|---|---|---|
| Regular heatmap / matrix | geom_tile() |
Simple to write, sets sizes by default. |
| Variable-width time periods | geom_rect() |
You fully control the start and end of each rectangle. |
| Calendar heatmaps | geom_rect() |
Lines up month/week tiles exactly. |
| Custom-position rectangles | geom_rect() |
Good for Gantt-style plots, GIS tiles, or showing data. |
| Faceted grid panels | geom_tile() |
Works better if sizes are all the same. |
Tips for Perfect Plots
- ✅ Always round numbers to a few decimal places (like 3). This helps avoid tiny pixel errors.
- ✅ Use
coord_fixed()for your plot system to make sure things line up. - ✅ Use
theme_void()or turn off borders. - ✅ If gaps stay, change to
geom_rect()and clearly set the edges. - ✅ Look at PNG (bitmap) and PDF (vector) versions of your plots. This helps you find drawing errors.
Performance Considerations
You might want to keep using geom_tile() because it's simpler to write and faster. Think about these points:
| Metric | geom_tile() |
geom_rect() |
|---|---|---|
| Simple to write | 👍 Simpler | 👎 Takes more writing |
| How fast it runs | 👍 Faster | 👎 A bit slower |
| Control of shape | 👎 Not as much | 👍 Exact |
| Best to use for | Heatmaps, matrices | Timelines, how long things take |
In short, if you want speed and all parts to look the same, use geom_tile(). But if you care more about being exact and having control, use geom_rect().
Visual Debugging Techniques
If you are not sure how plots are drawn differently, try these ways to check the plots by looking at them:
- Save as a high-quality PDF (vector format): easier to see tiny gaps.
- Add dashed vertical/horizontal grid lines:
+ geom_vline(xintercept = seq(0, 10, 1), linetype = "dotted") +
geom_hline(yintercept = seq(0, 10, 1), linetype = "dotted")
- Use
theme_minimal()ortheme_void()to make the plot look less busy. - Make two plots next to each other with
geom_tile()andgeom_rect()to see how they differ.
Beyond geom_tile() and geom_rect()
If rectangle-based geoms don’t fully work for what you want to show, here are some other options:
geom_polygon()– for making detailed, custom shapes. You get exact control over the corners.geom_segment()– great for timelines, Gantt charts.geom_path()orgeom_line()– for plotting data over time or paths of movement.geom_sf()– for working with map data or shapefile data.
Each has good and bad points. So, think carefully about what you need to do.
Developer Commentary: Why It Matters
Small drawing errors, like those from tiles that don't line up, can make your data science work seem less trustworthy. A clean, sharp plot is not just about how it looks. It is about being exact and trusted. Every shape on a chart means something to people looking at it. And an accidental gap might wrongly suggest a break, missing parts, or a mistake in how data was gathered.
Use ggplot2's flexibility by picking the right tools. Whether it’s geom_tile, geom_rect, or even custom geom_polygon, your choice should show good chart design, based on what the data means.
Bonus: Helper Function to Compute Rectangle Bounds
Doing the math for every row by hand can be tiring. Here's a function that does it for you:
expand_tile <- function(df, x = "x", y = "y", width = "width", height = "height") {
df$xmin <- df[[x]] - df[[width]] / 2
df$xmax <- df[[x]] + df[[width]] / 2
df$ymin <- df[[y]] - df[[height]] / 2
df$ymax <- df[[y]] + df[[height]] / 2
return(df)
}
# Example usage:
df <- expand_tile(df, x = "x", y = "y", width = "width", height = "height")
This helper function is very helpful when plotting how long things last or bin ranges. It changes geom_tile-style data into exact details for geom_rect().
Use this tool to get rid of guess-work errors. And it protects how exact your ggplot2 charts look.
Citations
Wickham, H. (2016). ggplot2: Elegant Graphics for Data Analysis. Springer. https://ggplot2-book.org
Chang, W. (2012). R Graphics Cookbook. O’Reilly Media.
R Core Team. (2023). The R Project for Statistical Computing. https://www.r-project.org