r-patterns

R patterns: tidyverse data pipelines with dplyr/tidyr/purrr, native pipe |>, R6 classes, tidy evaluation with rlang {{, vctrs custom types, renv dependency management, ggplot2 visualization, functional programming with purrr::map/walk/reduce. Use when writing or reviewing R code.

8 stars

Best use case

r-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

R patterns: tidyverse data pipelines with dplyr/tidyr/purrr, native pipe |>, R6 classes, tidy evaluation with rlang {{, vctrs custom types, renv dependency management, ggplot2 visualization, functional programming with purrr::map/walk/reduce. Use when writing or reviewing R code.

Teams using r-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/r-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/r-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/r-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How r-patterns Compares

Feature / Agentr-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

R patterns: tidyverse data pipelines with dplyr/tidyr/purrr, native pipe |>, R6 classes, tidy evaluation with rlang {{, vctrs custom types, renv dependency management, ggplot2 visualization, functional programming with purrr::map/walk/reduce. Use when writing or reviewing R code.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# R Patterns

## When to Activate

- Writing R scripts, packages, or Shiny apps (`.R`, `.Rmd`, `.qmd`)
- Designing data transformation pipelines with tidyverse
- Building R packages with roxygen2 documentation
- Reviewing R code for idiomatic style
- Using tidy evaluation (`{{ }}`) to write reusable functions that accept column names as arguments
- Managing project dependencies reproducibly with `renv` and a committed lockfile
- Choosing between `purrr::map` variants and for loops for iteration over data structures
- Creating publication-ready visualizations with ggplot2 and the scales package

---

## Native Pipe and Tidyverse Pipeline

```r
library(dplyr)
library(tidyr)
library(lubridate)

# Full ETL pipeline using |> (R 4.1+)
monthly_revenue <- transactions |>
  dplyr::filter(
    !is.na(amount),
    status == "completed"
  ) |>
  dplyr::mutate(
    date    = lubridate::ymd(date_str),
    month   = lubridate::floor_date(date, "month"),
    revenue = amount * (1 - discount_rate)
  ) |>
  dplyr::group_by(category, month) |>
  dplyr::summarise(
    total       = sum(revenue),
    n_orders    = dplyr::n(),
    avg_order   = mean(revenue),
    .groups     = "drop"
  ) |>
  dplyr::arrange(month, dplyr::desc(total))
```

---

## Functional Programming with purrr

```r
library(purrr)

# Type-safe map variants
means   <- purrr::map_dbl(datasets, ~ mean(.x$value, na.rm = TRUE))
names   <- purrr::map_chr(users, ~ .x$name)
flags   <- purrr::map_lgl(records, ~ !is.na(.x$email))

# Error-safe mapping
safe_read <- purrr::safely(readRDS)
results   <- purrr::map(file_paths, safe_read)
data_ok   <- purrr::keep(results, ~ is.null(.x$error)) |> purrr::map("result")
data_err  <- purrr::keep(results, ~ !is.null(.x$error))

# Walk for side effects (no return value)
purrr::walk(output_files, ~ message("Written: ", .x))

# Reduce to accumulate
total <- purrr::reduce(c(1, 2, 3, 4), `+`, .init = 0)  # 10

# Map2 — parallel iteration
combined <- purrr::map2_chr(
  first_names, last_names,
  ~ paste(.x, .y)
)
```

---

## R6 Classes — Mutable Objects

```r
library(R6)
library(glue)

# R6 class with private state
DataPipeline <- R6::R6Class(
  "DataPipeline",
  private = list(
    steps    = NULL,
    log_msgs = NULL
  ),
  public = list(
    initialize = function() {
      private$steps    <- list()
      private$log_msgs <- character(0)
    },
    add_step = function(name, fn) {
      stopifnot(is.character(name), is.function(fn))
      private$steps[[name]] <- fn
      invisible(self)  # enable method chaining
    },
    run = function(data) {
      result <- data
      for (step_name in names(private$steps)) {
        result <- private$steps[[step_name]](result)
        private$log_msgs <- c(
          private$log_msgs,
          glue::glue("[{Sys.time()}] Step '{step_name}' complete: {nrow(result)} rows")
        )
      }
      result
    },
    get_log = function() private$log_msgs
  )
)

# Method chaining
pipeline <- DataPipeline$new()$
  add_step("clean", ~ dplyr::filter(.x, !is.na(value)))$
  add_step("transform", ~ dplyr::mutate(.x, value = log1p(value)))

result <- pipeline$run(raw_data)
```

---

## Tidy Evaluation (rlang)

For functions that take column names as arguments:

```r
library(dplyr)
library(rlang)

# Embrace operator {{ }} for column names
group_summary <- function(data, group_col, value_col) {
  data |>
    dplyr::group_by({{ group_col }}) |>
    dplyr::summarise(
      n    = dplyr::n(),
      mean = mean({{ value_col }}, na.rm = TRUE),
      sd   = sd({{ value_col }}, na.rm = TRUE),
      .groups = "drop"
    )
}

# Works with any column names — no quoting needed
group_summary(mtcars, cyl, mpg)
group_summary(flights, carrier, arr_delay)

# .data pronoun — for string column names
filter_by_col <- function(data, col_name, threshold) {
  data |> dplyr::filter(.data[[col_name]] > threshold)
}
```

---

## ggplot2 — Visualization Patterns

```r
library(ggplot2)
library(scales)

# Publication-ready chart
monthly_revenue |>
  ggplot2::ggplot(ggplot2::aes(x = month, y = total, color = category)) +
  ggplot2::geom_line(linewidth = 1) +
  ggplot2::geom_point(size = 2) +
  ggplot2::scale_y_continuous(labels = scales::dollar_format()) +
  ggplot2::scale_x_date(date_breaks = "1 month", date_labels = "%b %Y") +
  ggplot2::labs(
    title    = "Monthly Revenue by Category",
    subtitle = "Jan–Dec 2024",
    x        = NULL,
    y        = "Revenue (USD)",
    color    = "Category"
  ) +
  ggplot2::theme_minimal() +
  ggplot2::theme(legend.position = "bottom")
```

---

## Package Structure (R6 + roxygen2)

```r
#' User Service
#'
#' Manages user retrieval and creation.
#'
#' @export
UserService <- R6::R6Class(
  "UserService",
  public = list(
    #' @description Create a new UserService
    #' @param repo A UserRepository object
    initialize = function(repo) {
      self$repo <- repo
    },

    #' @description Find user by ID
    #' @param id Integer user ID
    #' @return A User object or NULL
    find_by_id = function(id) {
      stopifnot(is.integer(id), length(id) == 1L, id > 0L)
      self$repo$find(id)
    },

    repo = NULL
  )
)
```

---

## renv — Reproducible Dependencies

```r
# Initialize renv in a project
renv::init()

# Snapshot current state
renv::snapshot()

# Restore from lockfile (CI / team members)
renv::restore()

# Update a package
renv::update("dplyr")
renv::snapshot()  # always snapshot after update
```

Always commit `renv.lock` to version control. Add `renv/library/` to `.gitignore`.

---

## Anti-Patterns

| Anti-Pattern | Problem | Better |
|---|---|---|
| `for` loop over data frame rows | Slow, hard to read | `dplyr::mutate` or `purrr::map` |
| `T`/`F` for TRUE/FALSE | Overridable as variables | Use `TRUE`/`FALSE` |
| `attach(data)` | Pollutes global environment | Use `data$col` or `with(data, ...)` |
| `setwd()` in scripts | Breaks portability | Use `here::here()` for paths |
| `1:length(x)` in loops | Fails on empty vectors | `seq_along(x)` or `seq_len(length(x))` |
| `=` for assignment | Style inconsistency | `<-` always |
| Ignoring `NA` values | Silent incorrect results | Explicitly use `na.rm = TRUE` |

Related Skills

zero-trust-patterns

8
from marvinrichter/clarc

Zero-Trust security patterns — mTLS between microservices (Istio/SPIFFE), SPIRE workload identity, OPA/Envoy authorization, NetworkPolicy default-deny-all, short-lived credentials, service mesh security, and Kubernetes RBAC hardening.

webrtc-patterns

8
from marvinrichter/clarc

WebRTC patterns — peer connection setup, ICE/STUN/TURN configuration, signaling server design, SFU vs mesh topology, screen sharing, media track management, and reconnect/ICE restart handling.

webhook-patterns

8
from marvinrichter/clarc

Webhook patterns for receiving, verifying (HMAC), and idempotently processing third-party events. Covers Stripe, GitHub, and generic webhook patterns, delivery guarantees, retry handling, and testing.

wasm-patterns

8
from marvinrichter/clarc

WebAssembly patterns: wasm-pack, wasm-bindgen (JS↔Wasm interop), WASI, Component Model, wasm-opt, Rust-to-WASM compilation, JS integration (web workers, streaming instantiation), and production deployment (CDN, Content-Type headers).

ux-micro-patterns

8
from marvinrichter/clarc

UX micro-patterns for every product state: Empty States, Loading States (skeleton screens, spinners, optimistic UI), Error States, Success States, Confirmation Dialogs, Onboarding Flows, and Progressive Disclosure. These patterns apply to every feature — done wrong, they're the biggest source of user confusion.

typescript-patterns

8
from marvinrichter/clarc

TypeScript patterns — type system best practices, strict mode, utility types, generics, discriminated unions, error handling with Result types, and module organization. Core patterns for production TypeScript.

typescript-patterns-advanced

8
from marvinrichter/clarc

Advanced TypeScript — mapped types, template literal types, conditional types, infer, type guards, decorators, async patterns, testing with Vitest/Jest, and performance. Extends typescript-patterns.

typescript-monorepo-patterns

8
from marvinrichter/clarc

TypeScript monorepo patterns with Turborepo + pnpm workspaces. Covers package structure, shared configs, task pipeline caching, build orchestration, and publishing strategy.

terraform-patterns

8
from marvinrichter/clarc

Infrastructure as Code with Terraform — project structure, remote state, modules, workspace strategy, AWS/GCP patterns, CI/CD integration, and security hardening. The standard for managing production infrastructure.

swiftui-patterns

8
from marvinrichter/clarc

SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS/macOS UI best practices.

swift-patterns

8
from marvinrichter/clarc

Core Swift patterns — value vs reference types, protocols, generics, optionals, Result, error handling, Codable, and module organization. Foundation for all Swift development.

swift-patterns-advanced

8
from marvinrichter/clarc

Advanced Swift patterns — property wrappers, result builders, Combine basics, opaque & existential types, macro system, advanced generics, and performance optimization. Extends swift-patterns.