Applied Metaphors: Learning TRIZ, Complexity, Data/Stats/ML using Metaphors
  1. Lab-7: The Lobster Quadrille
  • Teaching
    • Data Viz and Analytics
      • Tools
        • Introduction to R and RStudio
        • Introduction to Radiant
        • Introduction to Orange
      • Descriptive Analytics
        • Data
        • Graphs
        • Summaries
        • Counts
        • Quantities
        • Groups
        • Densities
        • Groups and Densities
        • Change
        • Proportions
        • Parts of a Whole
        • Evolution and Flow
        • Ratings and Rankings
        • Surveys
        • Time
        • Space
        • Networks
        • Experiments
        • Miscellaneous Graphing Tools, and References
      • Statistical Inference
        • 🧭 Basics of Statistical Inference
        • 🎲 Samples, Populations, Statistics and Inference
        • Basics of Randomization Tests
        • πŸƒ Inference for a Single Mean
        • πŸƒ Inference for Two Independent Means
        • πŸƒ Inference for Comparing Two Paired Means
        • Comparing Multiple Means with ANOVA
        • Inference for Correlation
        • πŸƒ Testing a Single Proportion
        • πŸƒ Inference Test for Two Proportions
      • Inferential Modelling
        • Modelling with Linear Regression
        • Modelling with Logistic Regression
        • πŸ•” Modelling and Predicting Time Series
      • Predictive Modelling
        • πŸ‰ Intro to Orange
        • ML - Regression
        • ML - Classification
        • ML - Clustering
      • Prescriptive Modelling
        • πŸ“ Intro to Linear Programming
        • πŸ’­ The Simplex Method - Intuitively
        • πŸ“… The Simplex Method - In Excel
      • Workflow
        • Facing the Abyss
        • I Publish, therefore I Am
      • Using AI in Analytics
        • Case Studies
          • Demo:Product Packaging and Elderly People
          • Ikea Furniture
          • Movie Profits
          • Gender at the Work Place
          • Heptathlon
          • School Scores
          • Children's Games
          • Valentine’s Day Spending
          • Women Live Longer?
          • Hearing Loss in Children
          • California Transit Payments
          • Seaweed Nutrients
          • Coffee Flavours
          • Legionnaire’s Disease in the USA
          • Antarctic Sea ice
          • William Farr's Observations on Cholera in London
      • TRIZ for Problem Solvers
        • I am Water
        • I am What I yam
        • Birds of Different Feathers
        • I Connect therefore I am
        • I Think, Therefore I am
        • The Art of Parallel Thinking
        • A Year of Metaphoric Thinking
        • TRIZ - Problems and Contradictions
        • TRIZ - The Unreasonable Effectiveness of Available Resources
        • TRIZ - The Ideal Final Result
        • TRIZ - A Contradictory Language
        • TRIZ - The Contradiction Matrix Workflow
        • TRIZ - The Laws of Evolution
        • TRIZ - Substance Field Analysis, and ARIZ
      • Math Models for Creative Coders
        • Maths Basics
          • Vectors
          • Matrix Algebra Whirlwind Tour
          • content/courses/MathModelsDesign/Modules/05-Maths/70-MultiDimensionGeometry/index.qmd
        • Tech
          • Tools and Installation
          • Adding Libraries to p5.js
          • Using Constructor Objects in p5.js
        • Geometry
          • Circles
          • Complex Numbers
          • Fractals
          • Affine Transformation Fractals
          • L-Systems
          • Kolams and Lusona
        • Media
          • Fourier Series
          • Additive Sound Synthesis
          • Making Noise Predictably
          • The Karplus-Strong Guitar Algorithm
        • AI
          • Working with Neural Nets
          • The Perceptron
          • The Multilayer Perceptron
          • MLPs and Backpropagation
          • Gradient Descent
        • Projects
          • Projects
      • Tech for Creative Education
        • 🧭 Using Idyll
        • 🧭 Using Apparatus
        • 🧭 Using g9.js
      • Literary Jukebox: In Short, the World
        • Italy - Dino Buzzati
        • France - Guy de Maupassant
        • Japan - Hisaye Yamamoto
        • Peru - Ventura Garcia Calderon
        • Russia - Maxim Gorky
        • Egypt - Alifa Rifaat
        • Brazil - Clarice Lispector
        • England - V S Pritchett
        • Russia - Ivan Bunin
        • Czechia - Milan Kundera
        • Sweden - Lars Gustaffsson
        • Canada - John Cheever
        • Ireland - William Trevor
        • USA - Raymond Carver
        • Italy - Primo Levi
        • India - Ruth Prawer Jhabvala
        • USA - Carson McCullers
        • Zimbabwe - Petina Gappah
        • India - Bharati Mukherjee
        • USA - Lucia Berlin
        • USA - Grace Paley
        • England - Angela Carter
        • USA - Kurt Vonnegut
        • Spain-Merce Rodoreda
        • Israel - Ruth Calderon
        • Israel - Etgar Keret
    • Posts
    • Blogs and Talks

    On this page

    • Introduction
    • Goals
    • Pedagogical Note
      • Setting up R Packages
      • Setting up Plot Theme
      • Using Google Fonts
    • Data
    • Basic Plot
    • Customized Plot
    • Using {ggtext}
      • Using element_markdown()
      • element_markdown() in combination with HTML
      • Adding images to ggplot
      • Annotations with geom_richtext() and geom_textbox()
      • Formatted Text boxes on plots
      • Using geom_texbox() for formatted text boxes with word wrapping
      • Using {ggforce}
      • ggplot tricks
      • Add Images
      • Using {patchwork}
    • References

    Lab-7: The Lobster Quadrille

    Fonts and other Wizardy in ggplot

    Published

    July 21, 2021

    Modified

    January 21, 2025

    Introduction

    We will add icing and froth to our vanilla ggplots: fonts, annotations, highlights and even pictures!!

    Goals

    • Appreciate that a publication-worth graphic takes a lot of work!!
    • Adding annotations, pictures and references to graphs is necessary for good understanding
    • Judicious use of colour and scales can enhance comprehension.

    Pedagogical Note

    The method followed will be based on PRIMM:

    • PREDICT Inspect the code and guess at what the code might do, write predictions
    • RUN the code provided and check what happens
    • INFER what the parameters of the code do and write comments to explain. What bells and whistles can you see?
    • MODIFY the parameters code provided to understand the options available. Write comments to show what you have aimed for and achieved.
    • MAKE : take an idea/concept of your own, and graph it.

    Setting up R Packages

    Let’s load up a few packages that we need to start:

    ## packages
    library(tidyverse) ## data science package collection (incl. the ggplot2 package)
    library(systemfonts) ## use custom fonts (need to be installed on your OS)
    library(paletteer) ## scico  and many other colour palettes palettes(http://www.fabiocrameri.ch/colourmaps.php) in R
    library(ggtext) ## add improved text rendering to ggplot2
    library(ggforce) ## add missing functionality to ggplot2
    library(concaveman) ## Needed by ggforce for plot annotation hulls
    library(ggdist) ## add uncertainty visualizations to ggplot2
    library(ggformula) ## Formula interface to ggplot
    library(magick) ## load images into R
    library(patchwork) ## combine outputs from ggplot2
    library(palmerpenguins)
    
    library(showtext) ## add google fonts to plots
    
    knitr::opts_chunk$set(
      error = TRUE,
      comment = NA,
      warning = FALSE,
      errors = FALSE,
      message = FALSE,
      tidy = FALSE,
      cache = FALSE,
      echo = TRUE,
      warning = FALSE,
      # from the vignette for the showtext package
      fig.showtext = TRUE,
      fig.retina = 1,
      fig.path = "figs/"
      # fig.height = 3.09,
      # fig.width = 5
    )

    Setting up Plot Theme

    Using Google Fonts

    We will want to add a few new fonts to our graphs. The best way (currently) is to use the showtext package (which we loaded above) to bring into our work fonts from Google. To view and select the fonts you might want to work with, spend some time looking over:

    1. Google Webfonts Helper App

    2. Google Fonts

    We will work with a familiar dataset, so that we can concentrate on the chart aesthetics, without having to spend time getting used to the data: the penguins dataset again, from the palmerpenguins package.

    Tipggformula and ggplot worlds do intersect!

    It seems we can mix `ggformula` code with `ggtext` code, using the `+` sign!! What joy !!! Need to find out if this works for other `ggplot` extensions as well !!!

    Data

    Always start your work with a table of the data:

    penguins <- penguins %>% drop_na()
    # remove data containing missing data
    penguins
    ABCDEFGHIJ0123456789
    species
    <fct>
    island
    <fct>
    bill_length_mm
    <dbl>
    bill_depth_mm
    <dbl>
    flipper_length_mm
    <int>
    body_mass_g
    <int>
    sex
    <fct>
    year
    <int>
    AdelieTorgersen39.118.71813750male2007
    AdelieTorgersen39.517.41863800female2007
    AdelieTorgersen40.318.01953250female2007
    AdelieTorgersen36.719.31933450female2007
    AdelieTorgersen39.320.61903650male2007
    AdelieTorgersen38.917.81813625female2007
    AdelieTorgersen39.219.61954675male2007
    AdelieTorgersen41.117.61823200female2007
    AdelieTorgersen38.621.21913800male2007
    AdelieTorgersen34.621.11984400male2007
    Next
    123456
    ...
    34
    Previous
    1-10 of 333 rows

    Basic Plot

    A basic scatter plot, which we will progressively dress up.

    • Using ggformula
    • Using ggplot
    ## simple plot: data + mappings + geometry
    ## no colour or fill yet
    gf <- gf_point(bill_depth_mm ~ bill_length_mm,
      data = penguins,
      alpha = 0.6, size = 3.5
    )
    gf

    ## simple plot: data + mappings + geometry
    ## no colour or fill yet
    gg <- ggplot(penguins, aes(
      x = bill_length_mm,
      y = bill_depth_mm
    )) +
      geom_point(alpha = .6, size = 3.5)
    gg

    Customized Plot

    Let us set some ggplot theme aspects now!! Here is a handy picture showing (most of) the theme-able aspects of a ggplot plot.

    Ggplot theme elements

    Rosana Ferrero (@RosanaFerrero) on Twitter Sept 11, 2022

    For more info, type ?theme in your console.

    ## change global theme settings (for all following plots)
    my_theme <- theme_set(theme_classic(
      base_size = 12,
      base_family = "roboto"
    )) +
    
      ## modify plot elements globally (for all following plots)
      theme_update(
        text = element_text(family = "roboto"),
        axis.ticks = element_line(color = "grey92"),
        axis.ticks.length = unit(.5, "lines"),
        panel.grid.minor = element_blank(),
        legend.title = element_text(size = 12),
        legend.text = element_text(color = "grey30"),
        plot.title = element_text(size = 18, face = "bold"),
        plot.subtitle = element_text(size = 12, color = "grey30"),
        plot.caption = element_text(size = 9, margin = margin(t = 15))
      )

    Since we know what the basic plot looks like, let’s add titles, labels and colours. We will also set limits and scales.

    • Using ggformula
    • Using ggplot
    theme_set(my_theme)
    
    gf1 <- penguins %>%
      gf_point(bill_depth_mm ~ bill_length_mm,
    
        # colour by continuous variable
        color = ~body_mass_g,
        alpha = .6, size = 3.5
      ) %>%
      ## custom axes scaling
      gf_refine(
        scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)),
        scale_y_continuous(
          breaks = seq(12.5, 22.5, by = 2.5),
          limits = c(12.5, 22.5)
        ),
    
        ## custom colors from the scico package
        ## using the paletteer super package
        paletteer::scale_color_paletteer_c(`"scico::bamako"`,
          direction = -1
        ),
    
        ## custom labels
        labs(
          title = "Bill Dimensions of Brush-Tailed Penguins (Pygoscelis)",
          subtitle = "A scatter plot of bill depth versus bill length.",
          caption = "Data: Gorman, Williams & Fraser (2014) PLoS ONE",
          x = "Bill Length (mm)",
          y = "Bill Depth (mm)",
          ## See this!
          color = "Body mass (g)"
        )
      )
    
    gf1

    Note this neat way of naming a scale and the legend in the labs command above!

    theme_set(my_theme)
    
    
    gg1 <- penguins %>%
      ggplot(aes(y = bill_depth_mm, x = bill_length_mm),
        alpha = .6
      ) +
      geom_point(aes(colour = body_mass_g), size = 3.5) +
    
    
      ## custom axes scaling
      scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
      scale_y_continuous(
        breaks = seq(12.5, 22.5, by = 2.5),
        limits = c(12.5, 22.5)
      ) +
    
      ## custom colors from the scico package
      paletteer::scale_color_paletteer_c(`"scico::bamako"`,
        direction = -1
      ) +
    
      ## custom labels
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (Pygoscelis)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) PLoS ONE",
        x = "Bill Length (mm)",
        y = "Bill Depth (mm)",
        color = "Body mass (g)"
      )
    gg1

    Using {ggtext}

    From Claus Wilke’s website β†’ www.wilkelab.org/ggtext

    The ggtext package provides simple Markdown and HTML rendering for ggplot2. Under the hood, the package uses the gridtext package for the actual rendering, and consequently it is limited to the feature set provided by gridtext.
    Support is provided for Markdown both in theme elements (plot titles, subtitles, captions, axis labels, legends, etc.) and in geoms (similar to geom_text()). In both cases, there are two alternatives, one for creating simple text labels and one for creating text boxes with word wrapping.

    CautionWorking with ggtext

    NOTE: on some machines, the ggtext package may not work as expected. In this case, please do as follows, using your Console:

    • remove gridtext: remove.packages(gridtext).
    • Install development version of gridtext: install.packages(remotes) remotes::install_github("wilkelab/gridtext")

    Using element_markdown()

    We can use our familiar markdown syntax right inside the titles and captions of the plot. element_markdown() is a theme-ing command made available by the ggtext package.

    element_markdown() β†’ formatted text elements, e.g. titles, caption, axis text, striptext.

    • Using ggformula
    • Using ggplot
    theme_set(my_theme)
    
    gf2 <- penguins %>%
      gf_point(bill_depth_mm ~ bill_length_mm,
        color = ~body_mass_g,
        alpha = 0.6, size = 3.5
      ) %>%
      gf_refine(
        scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)),
        scale_y_continuous(
          breaks = seq(12.5, 22.5, by = 2.5),
          limits = c(12.5, 22.5)
        ),
    
        ## custom colors from the scico package
        paletteer::scale_color_paletteer_c("scico::bamako",
          direction = -1
        ),
    
        ## custom labels using element_markdown()
        labs(
          title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
          subtitle = "A scatter plot of bill depth versus bill length.",
          caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
          x = "**Bill Length** (mm)",
          y = "**Bill Depth** (mm)",
          color = "Body mass (g)"
        )
      ) %>%
      # New code from here
      # Enables markdown titles, captions and labels
      gf_theme(theme(
        plot.title = ggtext::element_markdown(),
        plot.caption = ggtext::element_markdown(),
        axis.title.x = ggtext::element_markdown(),
        axis.title.y = ggtext::element_markdown()
      ))
    
    gf2

    theme_set(my_theme)
    
    gg2 <- ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +
      geom_point(aes(color = body_mass_g), alpha = .6, size = 3.5) +
      scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
      scale_y_continuous(
        breaks = seq(12.5, 22.5, by = 2.5),
        limits = c(12.5, 22.5)
      ) +
      paletteer::scale_color_paletteer_c(`"scico::bamako"`,
        direction = -1
      ) +
    
      ## New code starts here: Two Step Procedure with ggtext
      ## 1. Markdown formatting of labels and title, using asterisks
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)"
      ) +
    
      ## 2. Add theme related commands from ggtext
      ## render respective text elements
      theme(
        plot.title = ggtext::element_markdown(),
        plot.caption = ggtext::element_markdown(),
        axis.title.x = ggtext::element_markdown(),
        axis.title.y = ggtext::element_markdown()
      )
    gg2

    element_markdown() in combination with HTML

    This allows us to change fonts in titles, labels, and captions.

    • Using ggformula
    • Using ggplot
    theme_set(my_theme)
    
    ## use HTML syntax to change text color
    
    gf2 %>%
      # html in labels
      gf_labs(title = 'Bill Dimensions of Brush-Tailed Penguins
              <i style = "color:#28A87D;">Pygoscelis </i>')
    ## use HTML syntax to change font and text size
    gf2 %>%
      gf_labs(title = 'Bill Dimensions of Brush-Tailed Penguins <b style="font-size:32pt;font-family:tangerine;">Pygoscelis</b>')

    theme_set(my_theme)
    
    ## use HTML syntax to change text color
    gg2 +
      labs(title = 'Bill Dimensions of Brush-Tailed Penguins <i style="color:#28A87D;">Pygoscelis</i>') +
      theme(plot.margin = margin(t = 15))
    ## use HTML syntax to change font and text size
    gg2 +
      labs(title = 'Bill Dimensions of Brush-Tailed Penguins <b style="font-size:32pt;font-family:tangerine;">Pygoscelis</b>')

    Adding images to ggplot

    Save an image from the web in the same folder as your RMarkdown. Use html tags to include it, say as part of your plot title, as shown below.

    • Using ggformula
    • Using ggplot
    theme_set(my_theme)
    
    ## use HTML syntax to add images to text elements
    gf2 %>%
      gf_labs(title = 'Bill Dimensions of Brush-Tailed Penguins &nbsp;&nbsp;&nbsp; < img src="images/culmen_depth.png"β€š width="480"/>')

    theme_set(my_theme)
    
    ## use HTML syntax to add images to text elements
    gg2 +
      labs(title = 'Bill Dimensions of Brush-Tailed Penguins &nbsp;&nbsp;&nbsp; <img src="images/culmen_depth.png"β€š width="480"/>')

    Annotations with geom_richtext() and geom_textbox()

    Further ggplot annotations can be achieved using geom_richtext() and geom_textbox(). geom_richtext() also allows formatted text labels with 360Β° rotation. One needs to pass a tibble to geom_richtext() giving the location, colour, rotation etc of the label annotation.

    • Design the labels
    • Using ggformula
    • Using ggplot
    # Create a label tibble
    # Three rich text labels,
    # so three sets of locations x and y, and angle of rotation
    
    labels <- tibble(
      x = c(34, 56, 54),
      y = c(20, 18.5, 14.5),
      angle = c(12, 20, 335),
      species = c("Adelie", "Chinstrap", "Gentoo"),
      lab = c(
        "<b style='font-family:anton;font-size:24pt;'>AdΓ©lie</b><br><i style='color:darkgrey;'>P. adΓ©liae</i>",
        "<b style='font-family:anton;font-size:24pt;'>Chinstrap</b><br><i style='color:darkgrey;'>P. antarctica</i>",
        "<b style='font-family:anton;font-size:24pt;'>Gentoo</b><br><i style='color:darkgrey;'>P. papua</i>"
      )
    )
    labels
    ABCDEFGHIJ0123456789
    x
    <dbl>
    y
    <dbl>
    angle
    <dbl>
    species
    <chr>
    3420.012Adelie
    5618.520Chinstrap
    5414.5335Gentoo
    3 rows | 1-4 of 5 columns
    theme_set(my_theme)
    
    gf_rich <- penguins %>%
      gf_point(bill_depth_mm ~ bill_length_mm,
        color = ~species,
        alpha = 0.6, size = 3.5, data = penguins
      ) +
    
    
      ## add text annotations for each species
      ggtext::geom_richtext(
        data = labels,
        # Now pass the data variables as aesthetics
        aes(x, y, label = lab, color = species, angle = angle),
        size = 4, fill = NA, label.color = NA,
        lineheight = .3
      ) +
      # show.legend = FALSE else we get some unusual legends!
      # fill = NA makes the labels' fill transparent
    
    
      scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
      scale_y_continuous(
        breaks = seq(12.5, 22.5, by = 2.5),
        limits = c(12.5, 22.5)
      ) +
      scale_colour_paletteer_d(
        palette = `"rcartocolor::Bold"`,
        guide = "none"
      ) +
    
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)"
      ) +
    
      # Use theme and element_markdown() to format axes and titles as usual
      theme(
        plot.title = ggtext::element_markdown(),
        plot.caption = ggtext::element_markdown(),
        axis.title.x = ggtext::element_markdown(),
        axis.title.y = ggtext::element_markdown(),
        plot.margin = margin(25, 6, 15, 6)
      )
    
    
    gf_rich

    Important

    Note the plus sign usage here!!We are combining the ggformula and ggplot syntax, and it works!

    theme_set(my_theme)
    
    
    gg_rich <- ggplot(penguins, aes(
      x = bill_length_mm,
      y = bill_depth_mm
    )) +
      geom_point(aes(color = species), alpha = .6, size = 3.5) +
    
      ## add text annotations for each species
      ggtext::geom_richtext(
        data = labels,
        # Now pass the data variables as aesthetics
        aes(x, y, label = lab, color = species, angle = angle),
        size = 4, fill = NA, label.color = NA,
        lineheight = .3
      ) +
      scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
      scale_y_continuous(
        breaks = seq(12.5, 22.5, by = 2.5),
        limits = c(12.5, 22.5)
      ) +
      scale_colour_paletteer_d(`"rcartocolor::Bold"`, guide = "none") +
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)"
      ) +
    
      # Use theme and element_markdown() to format axes and titles as usual
      theme(
        plot.title = ggtext::element_markdown(),
        plot.caption = ggtext::element_markdown(),
        axis.title.x = ggtext::element_markdown(),
        axis.title.y = ggtext::element_markdown(),
        plot.margin = margin(25, 6, 15, 6)
      )
    
    gg_rich

    Formatted Text boxes on plots

    element_textbox() and element_textbox_simple() β†’ formatted text boxes with word wrapping.

    • Using ggformula
    • Using ggplot
    theme_set(my_theme)
    
    gf_box <- penguins %>%
      gf_point(bill_depth_mm ~ bill_length_mm,
        color = ~species,
        alpha = 0.6, size = 3.5, data = penguins
      ) +
    
    
      ## add text annotations for each species
      ggtext::geom_richtext(
        data = labels,
        # Now pass the data variables as aesthetics
        aes(x, y, label = lab, color = species, angle = angle),
        size = 4, fill = NA, label.color = NA,
        lineheight = .3
      ) +
      # show.legend = FALSE else we get some unusual legends!
      # fill = NA makes the labels' fill transparent
    
    
      scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
      scale_y_continuous(
        breaks = seq(12.5, 22.5, by = 2.5),
        limits = c(12.5, 22.5)
      ) +
      scale_colour_paletteer_d(
        palette = `"rcartocolor::Bold"`,
        guide = "none"
      ) +
    
    
      # Now for the Plot Titles and Labels, as before
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)"
      ) +
    
      # Add the ggtext theme related commands
      theme(
        ## turn title into filled textbox
        plot.title = ggtext::element_textbox_simple(
          color = "white",
          fill = "#28A78D",
          size = 28,
          padding = margin(8, 4, 8, 4),
          margin = margin(b = 5),
          lineheight = .9
        ),
        plot.subtitle = ggtext::element_textbox_simple(
          size = 10,
          padding = margin(5.5, 5.5, 5.5, 5.5),
          margin = margin(0, 0, 5.5, 0),
          fill = "orange1"
        ),
    
        ## add round outline to caption
        plot.caption = ggtext::element_textbox_simple(
          width = NULL,
          linetype = 1,
          fill = "grey",
          padding = margin(4, 8, 4, 8),
          margin = margin(t = 15),
          r = grid::unit(8, "pt")
        ),
        axis.title.x = ggtext::element_markdown(),
        axis.title.y = ggtext::element_markdown(),
        plot.margin = margin(25, 6, 15, 6)
      )
    
    gf_box

    theme_set(my_theme)
    
    gg_box <- ggplot(
      penguins,
      aes(x = bill_length_mm, y = bill_depth_mm)
    ) +
      geom_point(aes(color = species),
        alpha = .6, size = 3.5
      ) +
    
      ## add text annotations for each species
      ggtext::geom_richtext(
        data = labels,
        # Now pass the data variables as aesthetics
        aes(x, y, label = lab, color = species, angle = angle),
        size = 4, fill = NA, label.color = NA,
        lineheight = .3
      ) +
      # show.legend = FALSE else we get some unusual legends!
      # fill = NA makes the labels' fill transparent
    
    
      scale_x_continuous(breaks = 3:6 * 10, limits = c(30, 60)) +
      scale_y_continuous(
        breaks = seq(12.5, 22.5, by = 2.5),
        limits = c(12.5, 22.5)
      ) +
      scale_colour_paletteer_d(
        palette = `"rcartocolor::Bold"`,
        guide = "none"
      ) +
    
    
      # Now for the Plot Titles and Labels, as before
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)"
      ) +
    
      # Add the ggtext theme related commands
      theme(
        ## turn title into filled textbox
        plot.title = ggtext::element_textbox_simple(
          color = "white",
          fill = "#28A78D",
          size = 28,
          padding = margin(8, 4, 8, 4),
          margin = margin(b = 5),
          lineheight = .9
        ),
        plot.subtitle = ggtext::element_textbox_simple(
          size = 10,
          padding = margin(5.5, 5.5, 5.5, 5.5),
          margin = margin(0, 0, 5.5, 0),
          fill = "orange1"
        ),
    
        ## add round outline to caption
        plot.caption = ggtext::element_textbox_simple(
          width = NULL,
          linetype = 1,
          fill = "grey",
          padding = margin(4, 8, 4, 8),
          margin = margin(t = 15),
          r = grid::unit(8, "pt")
        ),
        axis.title.x = ggtext::element_markdown(),
        axis.title.y = ggtext::element_markdown(),
        plot.margin = margin(25, 6, 15, 6)
      )
    
    gg_box

    Using geom_texbox() for formatted text boxes with word wrapping

    • Using ggformula
    • Using ggplot
    theme_set(my_theme)
    
    text_box <- tibble(x = 34, y = 13.7, label = "<span style='font-size:12pt;font-family:anton;'>Lorem Ipsum Dolor Sit Amet</span><br><br>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")
    
    
    gf_box +
      ## add textbox with long paragraphs
      ggtext::geom_textbox(
        data = text_box,
        aes(x, y,
          label = label
        ),
        size = 2.2, family = "sans",
        fill = "cornsilk",
        colour = "black",
        # This is ESSENTIAL !!!
        # It appears that the original colour aesthetic mapping in `gf_box` and a possible colour aesthetic with `geom_textbox` have a clash, *only* with ggformula. No such issues below with the ggplot.
        # So declaring a colour here is essential
    
        box.color = "cornsilk3",
        # box.padding = c(2,2,2,2),
        width = unit(11, "lines")
      ) +
      coord_cartesian(clip = "off") # ensure no clipping of labels near the edge

    theme_set(my_theme)
    
    gg_box +
      ## add textbox with long paragraphs
      ggtext::geom_textbox(
        data = text_box,
        aes(x, y, label = label),
        size = 2.2, family = "sans",
        fill = "cornsilk", box.color = "cornsilk3",
        width = unit(11, "lines")
      ) +
      coord_cartesian(clip = "off") # ensure no clipping of labels near the edge

    Using {ggforce}

    From Thomas Lin Pedersen’s website β†’ www.ggforce.data-imaginist.com

    ggforce is a package aimed at providing missing functionality to ggplot2 through the extension system introduced with ggplot2 v2.0.0. Broadly speaking ggplot2 has been aimed primarily at explorative data visualization in order to investigate the data at hand, and less at providing utilities for composing custom plots a la D3.js. ggforce is mainly an attempt to address these β€œshortcomings” (design choices might be a better description). The goal is to provide a repository of geoms, stats, etc. that are as well documented and implemented as the official ones found in ggplot2.

    We will start with the basic plot, with the ggtext related work done up to now:

    ## use ggtext rendering for the following plots
    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    • Using ggformula and ggforce
    • Using ggplot and ggforce
    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    
    ## plot that we will annotate with ggforce afterwards
    gf3 <- penguins %>%
      gf_point(bill_depth_mm ~ bill_length_mm,
        color = ~body_mass_g,
        alpha = .6,
        size = 3.5
      ) +
    
      coord_cartesian(xlim = c(25, 65), ylim = c(10, 25)) +
    
      # Add Colour scales
      scale_color_paletteer_c(`"grDevices::Lajolla"`,
        direction = -1
      ) +
    
      # Add labels
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)",
        fill = "Species"
      )
    
    
    
    ## ellipsoids for all groups
    gf3 +
      ggforce::geom_mark_ellipse(
        aes(
          fill = species,
          label = species
        ),
        color = "black",
        # This is good to include
        # Else ellipses get coloured too
    
        alpha = .15,
        show.legend = FALSE
      )

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    
    ## plot that we will annotate with ggforce afterwards
    gg3 <- ggplot(
      penguins,
      aes(x = bill_length_mm, y = bill_depth_mm)
    ) +
      geom_point(aes(color = body_mass_g),
        alpha = .6,
        size = 3.5
      ) +
      coord_cartesian(xlim = c(25, 65), ylim = c(10, 25)) +
    
      # Add Colour scales
      scale_color_paletteer_c(`"grDevices::Lajolla"`,
        direction = -1
      ) +
    
    
      # Add labels
      labs(
        title = "Bill Dimensions of Brush-Tailed Penguins (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "Body mass (g)",
        fill = "Species"
      )
    
    
    ## ellipsoids for all groups
    gg3 +
      ggforce::geom_mark_ellipse(
        aes(
          fill = species,
          label = species
        ),
        alpha = .15,
        show.legend = FALSE
      )

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    
    ## ellipsoids for specific subset
    gg3 +
      ggforce::geom_mark_ellipse(
        aes(
          fill = species, label = species,
          filter = species == "Gentoo"
        ),
        alpha = 0, show.legend = FALSE
      ) +
      geom_point(aes(color = body_mass_g), alpha = .6, size = 3.5)

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    
    ## circles
    gg3 +
      ggforce::geom_mark_circle(
        aes(
          fill = species, label = species,
          filter = species == "Gentoo"
        ),
        alpha = 0, show.legend = FALSE
      ) +
      geom_point(aes(color = body_mass_g), alpha = .6, size = 3.5)

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    
    ## rectangles
    gg3 +
      ggforce::geom_mark_rect(
        aes(
          fill = species, label = species,
          filter = species == "Gentoo"
        ),
        alpha = 0, show.legend = FALSE
      ) +
      geom_point(aes(color = body_mass_g), alpha = .6, size = 3.5)

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown()
    )
    
    ## hull
    gg3 +
      ggforce::geom_mark_hull(
        aes(
          fill = species, label = species,
          filter = species == "Gentoo"
        ),
        alpha = 0, show.legend = FALSE
      ) +
      geom_point(aes(color = body_mass_g), alpha = .6, size = 3.5)

    ggplot tricks

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    gg0 <-
      ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +
      ggforce::geom_mark_ellipse(
        aes(
          fill = species,
          label = species
        ),
        alpha = .15,
        show.legend = FALSE
      ) +
      geom_point(aes(color = body_mass_g), alpha = .6, size = 3.5) +
      scale_x_continuous(
        breaks = seq(25, 65, by = 5),
        limits = c(25, 65)
      ) +
      scale_y_continuous(
        breaks = seq(12, 24, by = 2),
        limits = c(12, 24)
      ) +
      scico::scale_color_scico(palette = "bamako", direction = -1) +
      labs(
        title = "**Bill Dimensions of Brush-Tailed Penguins** (*Pygoscelis*)",
        subtitle = "A scatter plot of bill depth versus bill length.",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE*",
        x = "**Bill Length** (mm)",
        y = "**Bill Depth** (mm)",
        color = "**Body mass** (g)"
      )
    gg0

    Left-Aligned Title

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    (gg1 <- gg0 + theme(plot.title.position = "plot"))

    Right-Aligned Caption

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    gg1b <- gg1 + theme(plot.caption.position = "plot")
    gg1b

    Legend Design

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    gg1b + theme(legend.position = "top")
    # ggsave("06a_legend_position.pdf", width = 9, height = 8, device = cairo_pdf)
    
    gg1b +
      theme(legend.position = "top") +
      guides(
        color = guide_colorbar(
          # title.position = "top",
          # title.hjust = .5,
          legend.key.width = unit(20, "lines"),
          legend.bar.height = unit(.5, "lines")
        )
      )

    Add Images

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    ## read PNG file from web
    png <- magick::image_read("images/culmen_depth.png")
    
    ## turn image into `rasterGrob`
    img <- grid::rasterGrob(png, interpolate = TRUE)
    
    gg5 <-
      gg1 +
      annotation_custom(img,
        ymin = 22, ymax = 28,
        xmin = 65, xmax = 80
      ) +
      labs(caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE* &bull; Illustration: Allison Horst") +
      coord_cartesian(clip = "off") # ensure no clipping of labels near the edge
    gg5

    Using {patchwork}

    The goal of patchwork is to make it ridiculously simple to combine separate ggplots into the same graphic. As such it tries to solve the same problem as gridExtra::grid.arrange() and cowplot::plot_grid but using an API that incites exploration and iteration, and scales to arbitrarily complex layouts.

    β†’ https://patchwork.data-imaginist.com/

    Let us make two plots and combine them into a single patchwork plot.

    theme_set(new = my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    ## calculate bill ratio
    penguins_stats <- penguins %>%
      mutate(bill_ratio = bill_length_mm / bill_depth_mm) %>%
      filter(!is.na(bill_ratio))
    
    ## create a second chart
    gg6 <-
      ggplot(
        penguins_stats,
        aes(
          y = bill_ratio,
          x = species,
          fill = species,
          color = species
        )
      ) +
      geom_violin() +
      labs(
        y = "Bill ratio",
        x = "Species",
        subtitle = "",
        caption = "Data: Gorman, Williams & Fraser (2014) *PLoS ONE* &bull; Illustration: Allison Horst"
      ) +
      theme(
        panel.grid.major.x = element_line(linewidth = .35),
        panel.grid.major.y = element_blank(),
        axis.text.y = element_text(size = 13),
        axis.ticks.length = unit(0, "lines"),
        plot.title.position = "plot",
        plot.subtitle = element_text(margin = margin(t = 5, b = 10)),
        plot.margin = margin(10, 25, 10, 25)
      )

    Now to combine both plots into one using simple operators:

    For the special case of putting plots besides each other or on top of each other patchwork provides 2 shortcut operators. | will place plots next to each other while / will place them on top of each other.

    First we stack up the graphs side by side:

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    ## combine both plots
    gg5 | (gg6 + labs(
      title = "Bill Ratios of Brush-Tailed Penguins",
      subtitle = "Violin Plots of Bill Ration versus species"
    ))

    We can place them in one column:

    theme_set(my_theme)
    theme_update(
      plot.title = ggtext::element_markdown(),
      plot.caption = ggtext::element_markdown(),
      axis.title.x = ggtext::element_markdown(),
      axis.title.y = ggtext::element_markdown(),
      legend.title = ggtext::element_markdown()
    )
    
    gg5 / (gg6 + labs(
      title = "Bill Ratios of Brush-Tailed Penguins",
      subtitle = "Violin Plots of Bill Ration versus species"
    )) +
      plot_layout(heights = c(0.4, 0.4))

    References


    1. Thomas Lin Pedersen, https://www.data-imaginist.com/. The creator of ggforce, and patchwork packages.
    2. Claus Wilke, cowplot – Streamlined plot theme and plot annotations for ggplot2, https://wilkelab.org/cowplot/index.html
    3. Claus Wilke, Spruce up your ggplot2 visualizations with formatted text, https://clauswilke.com/talk/rstudio_conf_2020/. Slides, Code, and Video !
    4. Robert Kabacoff, ggplot theme cheatsheet, https://rkabacoff.github.io/datavis/modifyingthemes.pdf
    5. Zuguang Gu, Circular Visualization in R, https://jokergoo.github.io/circlize_book/book/

    R Package Citations
    Package Version Citation
    ggdist 3.3.2 @ggdist2024a; @ggdist2024b
    ggforce 0.4.2 @ggforce
    ggtext 0.1.2 @ggtext
    grid 4.4.2 @grid
    magick 2.8.5 @magick
    paletteer 1.6.0 @paletteer
    patchwork 1.3.0 @patchwork
    scico 1.5.0 @scico
    showtext 0.9.7 @showtext
    sysfonts 0.8.9 @sysfonts
    systemfonts 1.2.0 @systemfonts
    Back to top

    License: CC BY-SA 2.0

    Website made with ❀️ and Quarto, by Arvind V.

    Hosted by Netlify .