I have a ggplot
issue where I'm trying to make line plots with a very minimal look. I've gotten rid of the legend in favor of text labels at the right side of each line. It might not be so noticeable if the labels weren't so long, but I'd like it better if the gridlines stopped at the maximum x-value (in this case, at the year 2015).
library(tidyverse)
df <- structure(list(industry = c("Accommodation & Food Services", "Construction", "Health Care & Social Asst.", "Retail Trade",
"Accommodation & Food Services", "Construction", "Health Care & Social Asst.", "Retail Trade"),
year = c(2002L, 2002L, 2002L, 2002L, 2015L, 2015L, 2015L, 2015L),
value = c(6.977, 5.264, 17.065, 14.528, 8.032, 4.648, 20.547, 12.568)),
class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -8L),
.Names = c("industry", "year", "value"))
ggplot(df, aes(x = year, y = value, color = industry)) +
geom_line() +
geom_text(aes(label = industry), data = . %>% filter(year == max(year)), size = 3, hjust = 0, nudge_x = 0.1) +
scale_x_continuous(expand = expand_scale(mult = c(0.05, 0.85)), breaks = c(min(df$year), max(df$year)), name = NULL) +
scale_y_continuous(limits = c(0, NA), name = "Value (thousands)") +
theme_minimal() +
theme(legend.position = "none",
panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank())
I'd like to avoid the grob-hacking done in this answer because I have several plots and want to put them together quickly.
The solution I've come up with so far is to create pseudo-gridlines by making a geom_segment
with dummy data at regular y-values that starts at the earliest year and ends at the latest one. This works okay, because I have an idea of what y-breaks I want and can set scale_y_continuous
to match geom_segment
.
break_df <- tibble(x1 = 2002, x2 = 2015, y = seq(0, 20, by = 5))
ggplot(df, aes(x = year, y = value, color = industry)) +
geom_segment(aes(x = x1, xend = x2, y = y, yend = y), data = break_df, inherit.aes = F, size = 0.4, color = "gray85") +
geom_line() +
geom_text(aes(label = industry), data = . %>% filter(year == max(year)), size = 3, hjust = 0, nudge_x = 0.1) +
scale_x_continuous(expand = expand_scale(mult = c(0.05, 0.85)), breaks = c(min(df$year), max(df$year)), name = NULL) +
scale_y_continuous(limits = c(0, NA), name = "Value (thousands)", breaks = break_df$y) +
theme_minimal() +
theme(legend.position = "none",
panel.grid = element_blank())
Created on 2018-05-24 by the reprex package (v0.2.0).
What I'm wondering is how ggplot
calculates breaks, and if there's a function it uses that I could harness for this? Like if there's some internal calculation for the breaks that I could feed into both geom_segment
and scale_y_continuous
, so they'd match without me setting up a separate data frame of break information.
Alternatively, maybe this is a silly method and there's a better way to get the gridlines I want. I'm thinking there also might be a way to do this with a trans
function, like building a trans_new()
, but I don't know how to go about that.
Thanks in advance!
I would like to propose an alternative approach using annotation_custom()
, which achieves a similar look, at least in this case:
ggplot(df, aes(x = year, y = value, color = industry)) +
geom_line() +
annotation_custom(
grob = grid::rectGrob(gp = grid::gpar(col = NA, fill = "white")),
xmin = max(df$year)
) +
geom_text(aes(label = industry), data = . %>% filter(year == max(year)),
size = 3, hjust = 0, nudge_x = 0.1) +
# note: my version of ggplot2 doesn't have the expand_scale function, but I suppose
# it isn't critical to the question here
scale_x_continuous(expand = c(0.2, 0), breaks = c(min(df$year), max(df$year)),
name = NULL) +
scale_y_continuous(limits = c(0, NA), name = "Value (thousands)") +
theme_minimal() +
theme(legend.position = "none",
panel.grid.major.x = element_blank(),
panel.grid.minor = element_blank())
annotation_custom
's default location parameters are Inf
/ -Inf
, stretching the displayed grob to all four edges of the plot panel, so specifying xmin = max(df$year)
is sufficient to have it cover grid lines for everything to the right of max(df$year)
.
This trick works best with ggplot themes that do not have a visible panel.border
component, such as theme_minimal
, theme_grey
, or theme_dark
. If you are using theme_bw
/ theme_linedraw
/ theme_light
, the illusion becomes less effective. (As for theme_classic
/ theme_void
, they have no grid lines, so it doesn't matter either way.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With