ggplot2
Misc
Packages
- {ggreveal} - Reveal a ggplot Incrementally (e.g. groups, facets)
- {thematic} - Enables automatic styling of R plots in Shiny, R Markdown, and RStudio
- {hrbrthemes} - hrbrmstr’s collection of themes
- {{brand_yml}} - Create reports, apps, dashboards, plots and more that match your company’s brand guidelines with a single
_brand.yml
file- For Quarto dashboards, websites, reports, etc.
- For Python shiny, seaborn, matplotlib, etc.
- For R shiny, ggplot2, plot, RMarkdown
- {ggblanket} - Similar ggplot ui but with simpler structure. Stuff that would require multiple geoms are replaced with one geom and basic arguments. Compatible with popular ggplot extensions
Resources
- Docs, Aesthetic Specifications, A Complete Guide to Scales
- ggplot2: Elegant Graphics for Data Analysis (Wickam, Navarro, Pederson)
- Modern Data Visualization with R
- R Graphics Cookbook
- ggplot2 extension cookbook - step-by-step, how-to extension examples (mostly Stat/geom_*)
Don’t use stat calculating geoms and set axis limits with scale_y_continuous
- See examples of the behavior in this thread
Defaults for any {ggplot2} geom using the default_aes field (i.e.
GeomBlah$default_aes
)Basic Function
<- viz_monthly function(df, y_var, threshhold = NULL) { ggplot(df, aes(x = .data[["day"]], y = .data[[y_var]])) + geom_line() + geom_hline(yintercept = threshhold, color = "red", linetype = 2) + scale_x_continuous(breaks = seq(1, 29, by = 7)) + theme_minimal() }
Storing geom settings in a dataframe (source)
<- data.frame(x = seq(from = 1, to = 20, by = 1), df y = rnorm(20, mean = 50, sd = 4), limits = "c(0,30)") ggplot(data = df, aes(x = x, y = y)) + geom_line() + scale_y_continuous(limits= eval(parse(text=df$limits)))
FIltering Data in Geoms (source)
|> mtcars rownames_to_column() |> ggplot(aes(mpg, hp)) + geom_point() + geom_label( aes(label = rowname), data = ~slice_max(.x, hp,n = 2), nudge_x = 3 )
Dynamic Axis Limits (source)
ggplot(...) + + ... scale_x_continuous( breaks = seq(2021, 2023, 1), limits = c(min(df$year) - .1, max(df$year) + .1), position = "top", labels = c("Baseline", "2022", "2023") )
Themes
Docs
Theme Elements Cheatsheet (source)
Use the same font family to all
geom_text
elementsupdate_geom_defaults( geom = "text", aes(family = "Open Sans") )
geom_text
is not impacted by theme settings, so setting this option keeps you from having to manually add the font family to each instance.
Setting color defaults (source)
# Discrete with a list options(ggplot2.discrete.colour = list_of_colors) # Continuous with a function options( ggplot2.continuous.colour = \(...){ ::scale_color_scico( scicopalette = "batlow", ... ) } ) # Ordinal with a function options( ggplot2.ordinal.colour = \(...){ scale_color_viridis_d( option = "G", direction = -1, ... ) } )
- If you have a particular set of palettes you like to use for projects, these options can be put into a R file and loaded via
source
e.g.source(here::here("_defaults.R"))
- If you have a particular set of palettes you like to use for projects, these options can be put into a R file and loaded via
Basic Syntax
<- function() { theme_psc theme_void(base_family = "Open Sans") + theme() }
Use Utility FIles
utils/base-theme.R (source)
## R/themes/base_theme.R #' Get default theme colors #' @param palette Optional custom color palette #' @return List of theme colors <- function(palette = NULL) { get_theme_colors list( background = "#f5f5f2", title = "gray20", subtitle = "gray30", text = "gray30", caption = "gray40", palette = palette # This can be overridden weekly ) } #' Create base theme with consistent elements #' @param colors List of colors from get_theme_colors() #' @return ggplot theme object <- function(colors = get_theme_colors()) { create_base_theme # Only include the truly fixed elements that don't change weekly theme_minimal(base_size = 14, base_family = fonts$text) + theme( plot.title.position = "plot", plot.caption.position = "plot", legend.position = "plot", # Background elements plot.background = element_rect(fill = colors$background, color = colors$background), panel.background = element_rect(fill = colors$background, color = colors$background), # Common margins plot.margin = margin(t = 10, r = 20, b = 10, l = 20), # Text elements that stay consistent axis.text = element_text( family = fonts$text, size = rel(0.79), color = colors$text ),axis.title = element_text( family = fonts$text, size = rel(0.93), face = "bold", color = colors$text ) # Note: specific theme elements should be added in the scripts ) } #' Extend base theme with customizations #' @param base_theme Existing theme to extend #' @param new_theme_elements List of theme elements specific to this project #' @return Modified ggplot theme <- function(base_theme, new_theme_elements) { extend_base_theme + new_theme_elements base_theme }
script.R (source)
source(here::here("utils/base_theme.R")) # Get base colors with custom palette <- colors get_theme_colors(palette = c("#f7fbff", "#9ecae1", "#2171b5", "#084594")) <- create_base_theme(colors) base_theme <- extend_base_theme( project_theme base_theme,theme( # project-specific modifications axis.line.x = element_line(color = "#252525", linewidth = .2), panel.spacing.x = unit(2, 'lines'), panel.spacing.y = unit(1, 'lines'), panel.grid.major.x = element_blank(), panel.grid.major.y = element_line(color = alpha(colors[5], 0.2), linewidth = 0.2), panel.grid.minor = element_blank(), ) ) theme_set(project_theme)
Transparent Background
+ p theme( panel.background = element_rect(fill='transparent'), #transparent panel bg plot.background = element_rect(fill='transparent', color=NA), #transparent plot bg panel.grid.major = element_blank(), #remove major gridlines panel.grid.minor = element_blank(), #remove minor gridlines legend.background = element_rect(fill='transparent'), #transparent legend bg legend.box.background = element_rect(fill='transparent') #transparent legend panel )ggsave('myplot.png', p, bg='transparent')
theme_notebook
<- function(...) { theme_notebook theme_minimal() %+replace% theme( panel.background = element_rect(fill='#FFFDF9FF'), panel.grid.minor = element_blank(), plot.background = element_rect(fill='#FFFDF9FF', color=NA), legend.background = element_rect(fill='#FFFDF9FF'), legend.box.background = element_rect(fill='#FFFDF9FF'), ... ) }
Cedric Sherer (article)
theme_set(theme_minimal(base_size = 15, base_family = "Anybody")) theme_update( axis.title.x = element_text(margin = margin(12, 0, 0, 0), color = "grey30"), axis.title.y = element_text(margin = margin(0, 12, 0, 0), color = "grey30"), panel.grid.minor = element_blank(), panel.border = element_rect(color = "grey45", fill = NA, linewidth = 1.5), panel.spacing = unit(.9, "lines"), strip.text = element_text(size = rel(1)), plot.title = element_text(size = rel(1.4), face = "bold", hjust = .5), plot.title.position = "plot" )
Eric Bickel
::theme_set( ggplot2theme_bw(base_size = 12) + theme( plot.title = element_text(face = 'bold', hjust = 0), text = element_text(colour = '#4e5c65'), panel.background = element_rect('#ffffff'), strip.background = element_rect('#ffffff', colour = 'white'), plot.background = element_rect('#ffffff'), panel.border = element_rect(colour = '#ffffff'), panel.grid.major.x = element_blank(), panel.grid.major.y = element_blank(), panel.grid.minor.y = element_blank(), legend.background = element_rect('#ffffff'), legend.title = element_blank(), legend.position = 'right', legend.direction = 'vertical', legend.key = element_blank(), strip.text = element_text(face = 'bold', size = 10), axis.text = element_text(face = 'bold', size = 9), axis.title = element_blank(), axis.ticks = element_blank() )
Bespoke
- Comparison Plot (source)
-
Code
library(tidyverse) library(scales) <- comparison_plot function( df, highlight_town, value_type, highlight_color ) { <- plot |> df ggplot( aes( x = value, y = 1 )+ ) # Light gray lines for all towns geom_point( shape = 124, color = "gray80" + ) # Line for town to highlight geom_point( data = df |> filter(location == highlight_town), shape = 124, color = highlight_color, size = 10 ) if (value_type == "percent") { <- plot + final_plot scale_x_continuous( labels = percent_format(accuracy = 1) ) } if (value_type == "number") { <- plot + final_plot scale_x_continuous( labels = comma_format(accuracy = 1) ) } } comparison_plot( df = single_family_homes, highlight_town = "Hartford", value_type = "percent", highlight_color = psc_blue + ) theme_whatever() <- big_number_plot function(value, text, value_color ) {ggplot() + # Add value geom_text( aes( x = 1, y = 1, label = value ),color = value_color, fontface = "bold", size = 20, hjust = 0 + ) # Add text geom_text( aes( x = 1, y = 2, label = str_to_upper(text) ),color = "gray70", size = 7, hjust = 0 ) } big_number_plot( value = "19%", text = "Single-Family Homes as\nPercent of All Homes", value_color = psc_blue + ) theme_whatever() # should start with theme_void
-