Skip to content
R for the Rest of Us Logo

Twenty-Five Things You Didn't Know You Could Do with R

This blog post contains code samples for my keynote at Cascadia R Conference 2025. In addition, I've added resources for those interested in learning more. I'll also post the recording of my talk when it is available.

Slides

Access Data Automagically

Pull in Data Directly from Google Sheets

Sample Code

library(googlesheets4)

survey_data <-
  read_sheet(YOURSHEETURLHERE)

Resources

Pull in Data Directly from Qualtrics

Sample Code

library(qualtRics)

survey_data <-
  fetch_survey(surveyID = "YOURSURVEYIDHERE")

Resources

Pull in Data Directly from the Census Bureau

Sample Code

library(tidycensus)

get_acs(
  state = "OR",
  geography = "place",
  geometry = TRUE,
  variables = "B01003_001"
)

Resources

Work with APIs to Access Data

Sample Code

This code doesn't work (you won't have an API key to make it work), but should give you a sense of how {httr2} works.

library(httr2)

fathom_api_key <- Sys.getenv("FATHOM_API_KEY")

request("https://api.usefathom.com/v1/aggregations") |>
  req_url_query(
    entity = "pageview",
    aggregates = "visits,uniques,pageviews",
    sort_by = "visits:desc"
  ) |>
  req_headers(
    Authorization = str_glue("Bearer {fathom_api_key}")
  ) |>
  req_perform()
library(rvest)
library(tidyverse)

read_html("https://en.wikipedia.org/wiki/List_of_FIFA_World_Cup_finals") |>
  html_elements("table") |>
  pluck(4) |>
  html_table() |>
  select(-Ref.)

Sample Code

Resources

Efficiently Make Beautiful Data Viz

Make Your Own Theme

Sample Code

Here is the full code for my theme:

theme_dk <- function(base_family = "Inter Tight", base_size = 14) {
  theme_dk <-
    ggplot2::theme_minimal(
      base_size = base_size,
      base_family = base_family
    ) +
    ggplot2::theme(
      panel.grid.minor = ggplot2::element_blank(),
      panel.grid.major = ggplot2::element_line(
        color = "grey90",
        linewidth = 0.5,
        linetype = "dashed"
      ),
      axis.ticks = ggplot2::element_blank(),
      axis.text = ggplot2::element_text(
        color = "grey50",
        size = ggplot2::rel(0.8)
      ),
      axis.title = ggplot2::element_blank(),
      plot.title.position = "plot",
      plot.title = ggplot2::element_text(
        face = "bold",
        size = ggplot2::rel(1.5)
      ),
      plot.subtitle = ggplot2::element_text(
        color = "grey40",
        size = ggplot2::rel(1.1)
      ),
      plot.caption = ggplot2::element_text(
        color = "grey50",
        margin = ggplot2::margin(t = 20)
      ),
      plot.margin = ggplot2::margin(10, 10, 10, 10),
      strip.text = ggplot2::element_text(
        color = "grey40",
        size = ggplot2::rel(0.9)
      ),
      panel.spacing = ggplot2::unit(2, "lines")
    )

  theme_dk
}

Resources

Check out the Customize Your Theme lesson video from my Going Deeper with R course.

Transcript

Click on the transcript to go to that point in the video. Please note that transcripts are auto generated and may contain minor inaccuracies.

I also talk about it in Chapter 3 of my book R for the Rest of Us: A Statistics-Free Introduction.

Use Your Theme Everywhere

Sample Code

---
title: "Penguins Report"
---

```{r}
theme_dk <- function(base_family = "Inter Tight", base_size = 14) {}
```

```{r}
theme_set(theme_dk())
```

```{r}
penguins_bar_chart
```

Resources

Learn more about modifying themes in theme() function documentation

Make Your Text Consistent with Your Theme

Sample Code

---
title: "Penguins Report"
---

```{r}
theme_dk <- function()
```

```{r}
theme_set(theme_dk())

update_geom_defaults(geom = "text", aes(family = "IBM Plex Mono"))
```

```{r}
penguins_bar_chart +
  geom_text(
    aes(label = avg_bill_length_formatted),
    vjust = 1.5,
    color = "white",
    size = 4
  )
```

Resources

Make Maps

Make Maps with ggplot

Sample Code

library(tidycensus)

median_income_by_county <-
  get_acs(
    geography = "county",
    variables = c(median_income = "B19013_001"),
    geometry = TRUE
  )

Resources

Do Geospatial Analysis

Sample Code

library(sf)
library(tidyverse)
library(mapgl)

portland_libraries <- read_sf("https://raw.githubusercontent.com/rfortherestofus/twenty-five-things/refs/heads/main/data/portland_libraries.geojson")
					  
portland_libraries_one_mile_buffer <-
  portland_libraries |>
  st_buffer(1609.34)

pps_elementary_schools <-
  read_sf("https://raw.githubusercontent.com/rfortherestofus/twenty-five-things/refs/heads/main/data/pps_elementary_schools.geojson
  
pps_elementary_schools_near_libraries <-
  pps_elementary_schools |>
  st_join(portland_libraries_one_mile_buffer) |>
  mutate(has_nearby_library = case_when(
    is.na(library) ~ "Not within one mile of library",
    .default = "Within one mile of library"
  )) |>
  select(school, has_nearby_library)
 
 maplibre(bounds = portland_libraries_one_mile_buffer) |>
  add_fill_layer(
    source = portland_libraries_one_mile_buffer,
    fill_color = "#7570b3",
    fill_opacity = 0.5,
    tooltip = "library",
    id = "portland_libraries"
  ) |>
  add_circle_layer(
    source = pps_elementary_schools_near_libraries,
    circle_color = match_expr(
      "has_nearby_library",
      values = c(
        "Within one mile of library",
        "Not within one mile of library"
      ),
      stops = c(
        "#1b9e77",
        "#d95f02"
      )
    ),
    tooltip = "school",
    id = "schools"
  ) |>
  add_categorical_legend(
    values = c(
      "Within one mile of library",
      "Not within one mile of library"
    ),
    legend_title = NULL,
    colors = c("#1b9e77", "#d95f02"),
    circular_patches = TRUE
  )

Resources

Make Interactive Maps

Sample Code

maplibre(bounds = pps_elementary_schools_near_libraries) |>
  add_fill_layer(
    source = portland_libraries_one_mile_buffer,
    fill_color = "#7570b3",
    fill_opacity = 0.5,
    tooltip = "library",
    id = "portland_libraries"
  ) |>
  add_circle_layer(
    source = pps_elementary_schools_near_libraries,
    circle_color = match_expr(
      "has_nearby_library",
      values = c(
        "Within one mile of library",
        "Not within one mile of library"
      ),
      stops = c(
        "#1b9e77",
        "#d95f02"
      )
    ),
    tooltip = "school",
    id = "schools"
  ) |>
  add_categorical_legend(
    values = c(
      "Within one mile of library",
      "Not within one mile of library"
    ),
    legend_title = NULL,
    colors = c(
      "#1b9e77",
      "#d95f02"
    ),
    circular_patches = TRUE
  )

Resources

Report in New Ways with Quarto

Make Many Different Outputs with Quarto

Resources

Keep Your Quarto Outputs on Brand

Sample Code

meta:
  name: R for the Rest of Us
  link: https://rfortherestofus.com

color:
  foreground: "#404e6b"
  primary: "#6cabdd"

typography:
  fonts:
    - family: Inter
      source: google
    - family: IBM Plex Mono
      source: google
  base: Inter
  headings: Inter
  monospace: IBM Plex Mono

Resources

  • Check out brand.yml documentation.

  • Watch my recent video interview with Garrick Aden-Buie, covering consistent branding with brand.yml:

Publish Your Quarto Documents Online

Resources

In this Going Deeper with R course video, I teach how to publish your documents using Netlify. Check it out:

Transcript

Click on the transcript to go to that point in the video. Please note that transcripts are auto generated and may contain minor inaccuracies.

Make PDFs with Typst

Sample Code

The best place to see sample code is in this repo, which accompanies my 2024 posit::conf talk (see below).

Resources

My posit::conf(2024) talk covered how to use typst.

Automate all the Things

Email Your Reports Directly from R

Sample Code

library(tidyverse)
library(quarto)
library(gmailr)

rendered_report <- str_glue("covid-business-relief-contact-log-{today()}.html")

quarto_render(
  input = "report.qmd",
  output_file = rendered_report
)

gm_auth_configure()
gm_auth(email = TRUE, cache = ".secret")

email_report <-
  gm_mime() |>
  gm_to("Joe Schmoe <[email protected]>") |>
  gm_from("David Keyes <[email protected]>") |>
  gm_subject("COVID Business Relief Contact Log") |>
  gm_text_body("See attached") |>
  gm_attach_file(rendered_report)

gm_send_message(email_report)

Resources

Run Your Code Without Lifting a Finger

Sample Code

name: Render Report and Send It

on:
  schedule:
    - cron:  '00 14 * * 1-5'

jobs:
  build:
    runs-on: ubuntu-latest
    container: rocker/geospatial
    
    env:
      GMAILR_APP: ${{ secrets.GMAILR_APP }}
      GMAILR_EMAIL: ${{ secrets.GMAILR_EMAIL }}

   steps:
      - name: Checkout Repository
        uses: actions/checkout@v2

      - name: Install dependencies
        run: |
          install.packages("remotes")
          remotes::install_cran("quarto")
          remotes::install_cran("gmailr")
          ETC
        shell: Rscript {0}
          
      - name: Render + Send
        run: |-
          Rscript render_gmail.R

Resources

Use R to Work With Files Created in R

Sample Code

library(fs)
library(zip)
library(tidyverse)
library(googledrive)

county_pages <- dir_ls("outputs/pages/county/")

measure_pages <- dir_ls("outputs/pages/measure/")

all_pages_zip <-
  zip(
    zipfile = str_glue("outputs/zip/obtn-files-{today()}.zip"),
    files = c(county_pages, measure_pages)
  )
 
drive_upload(all_pages_zip)

Resources

See documentation for the {fs}, {zip} and {googledrive} packages.

We also have a blog post on working with {fs}.

Use AI to Write Better Code

Create Custom Instructions

Sample Code

My custom instructions are: "Please answer the following R question. When I program, I always like to use the tidyverse. Please don't ever give me base R solutions. Please also always use the native pipe (|>) not the tidyverse pipe (|>)."

Use AI Directly in your Code Editor

Resources

In the Using AI with R course, I teach how to use AI in both RStudio and Positron.

Using AI in RStudio:

Transcript

Click on the transcript to go to that point in the video. Please note that transcripts are auto generated and may contain minor inaccuracies.

Using AI in Positron:

Transcript

Click on the transcript to go to that point in the video. Please note that transcripts are auto generated and may contain minor inaccuracies.

Show AI Your Data

Resources

Simon Couch shows how the {gander} package can make tedious coding tasks faster and more efficient in a recent R for the Rest of Us podcast interview.

Use AI for Data Analysis

Translate Text

Sample Code

library(tidyverse)
library(mall)

survey_spanish <-
  read_csv("https://raw.githubusercontent.com/rfortherestofus/twenty-five-things/refs/heads/main/data/survey_spanish.csv")	  

survey_translated <-
  survey_spanish |>
  llm_translate(spanish, language = "English", pred_name = "english")

Resources

Check out the {mall} package docs

Summarize Text

Sample Code

survey_translated_summary <-
  survey_translated |>
  llm_summarize(english, max_words = 5, pred_name = "summary")

Resources

Learn about the llm_summarize() function

Create your Own Prompt to Analyze Text

Sample Code

library(ellmer)

identify_themes <- function(text) {
  chat <- chat_openai(
    system_prompt = "You are a sociologist,
    looking for the top three themes in the responses to a survey.
    Each response is separated by \n"
  )

  chat$chat(text)
}

survey_translated_combined <-
  survey_translated |>
  pull(english) |>
  paste(collapse = "\n")

survey_translated_combined |>
  identify_themes()

Resources

I show how to use {ellmer} in my Using AI with R course:

Check out the {ellmer} package docs

Join the Community

I wrote a blog post a long time ago about how the R community became so welcoming.

Learn More about R for the Rest of Us

If you're interested in learning more R, check out our courses. And if you'd like to hire us to help you communicate more effectively and efficiently, check out our consulting services.

Sign up for the newsletter

Get blog posts like this delivered straight to your inbox.

Let us know what you think by adding a comment below.

You need to be signed-in to comment on this post. Login.

David Keyes
By David Keyes
June 20, 2025

Sign up for the newsletter

R tips and tricks straight to your inbox.