0Pricing
R Academy · Lesson

Tidy Evaluation: {{ }} and .data

Write dplyr functions that accept column names as arguments safely.

Tidy Evaluation: {{ }} and .data is a free R Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Problem: Programming with dplyr

dplyr uses non-standard evaluation (NSE) — column names are passed unquoted. This is convenient interactively but tricky when writing functions. How do you pass a column name as a variable to a dplyr function?

library(dplyr)

df <- data.frame(x=1:5, y=c(2,4,6,8,10))

# Works fine interactively:
df %>% summarize(mean_x = mean(x))

# But what if 'x' is a variable?
col_name <- 'x'
# df %>% summarize(result = mean(col_name))  # WRONG! treats 'col_name' as column
cat('Need tidy evaluation to solve this!')

The {{ }} Embrace Operator

Inside a function, use {{ col_var }} (called 'curly-curly' or 'embrace') to pass a column name that will be evaluated in the data frame context. This is the recommended approach for most dplyr function programming.

library(dplyr)

df <- data.frame(
  group = c('A','A','B','B'),
  sales = c(100, 120, 200, 180),
  costs = c(60, 70, 110, 90)
)

# Function using {{ }}
group_mean <- function(data, group_col, value_col) {
  data %>%
    group_by({{ group_col }}) %>%
    summarize(mean_val = mean({{ value_col }}), .groups = 'drop')
}

group_mean(df, group, sales)
group_mean(df, group, costs)

{{ }} for Column Names in Output

You can also use {{ }} on the left side of := to dynamically name output columns. The := operator (walrus operator) allows variable names on the left of assignments inside dplyr verbs.

library(dplyr)

df <- data.frame(a=1:4, b=c(2,4,6,8))

# Dynamic output column name
compute_mean <- function(data, col) {
  col_name <- paste0('mean_', deparse(substitute(col)))
  data %>% summarize('{col_name}' := mean({{ col }}))
}

compute_mean(df, a)
compute_mean(df, b)

The .data Pronoun

.data[['col_name']] lets you select a column using a string variable. It explicitly tells dplyr to look up the column in the current data frame. This is useful when you have column names as character strings.

library(dplyr)

df <- data.frame(
  name = c('Alice','Bob','Carol'),
  score = c(85, 92, 78),
  grade = c('B','A','C')
)

# Using .data with a string variable
get_summary <- function(data, col_name) {
  data %>%
    summarize(
      n = n(),
      mean_val = mean(.data[[col_name]])
    )
}

get_summary(df, 'score')

across() — Apply to Multiple Columns

across(cols, fns) inside mutate() or summarize() applies a function to multiple columns at once. Select columns with tidyselect helpers: starts_with(), where(is.numeric), everything(), etc.

library(dplyr)

df <- data.frame(
  id = 1:3,
  sales_q1 = c(100, 200, 150),
  sales_q2 = c(120, 180, 160),
  cost_q1 = c(60, 110, 80),
  cost_q2 = c(70, 100, 85)
)

# Summarize all numeric columns except 'id'
df %>%
  summarize(across(where(is.numeric) & !id, mean))

across() with Named Functions

Pass a named list of functions to across() to compute multiple statistics per column. The output columns are named as col_fn. Use .names to customize the naming pattern.

library(dplyr)

df <- data.frame(
  x = c(10, 20, 30, 40),
  y = c(5, 15, 25, 35)
)

# Compute mean and sd for both columns
df %>%
  summarize(across(
    c(x, y),
    list(mean = mean, sd = sd),
    .names = '{.col}_{.fn}'
  ))

across() in mutate()

Using across() inside mutate() transforms multiple columns simultaneously. This avoids repeating the same transformation for each column individually.

library(dplyr)

df <- data.frame(
  id = 1:3,
  revenue = c(1000, 2000, 1500),
  cost = c(600, 1100, 800),
  tax = c(100, 200, 150)
)

# Round all numeric columns except id to nearest 10
df %>%
  mutate(across(where(is.numeric) & !id,
                ~round(., -2)))

pick() — Select Columns for Operations

pick() (dplyr 1.1+) selects a subset of columns as a mini data frame, useful for passing to functions that expect a data frame. It works inside mutate() and summarize().

library(dplyr)

df <- data.frame(
  id = 1:3,
  a = c(1, 2, 3),
  b = c(4, 5, 6),
  c_val = c(7, 8, 9)
)

# Use pick() to compute row-wise mean across selected columns
df %>%
  mutate(
    row_mean = rowMeans(pick(a, b, c_val)),
    row_max = do.call(pmax, pick(a, b, c_val))
  )

Writing Reusable dplyr Functions

Combining {{ }}, .data[[]], and across() lets you write powerful reusable analysis functions. The key pattern: accept column names as unquoted arguments (use {{ }}) or strings (use .data[[]]).

library(dplyr)

df <- data.frame(
  region = c('East','East','West','West'),
  product = c('A','B','A','B'),
  revenue = c(100, 200, 150, 250),
  units = c(10, 15, 12, 20)
)

# Flexible grouped summary function
group_stats <- function(data, ..., metric) {
  data %>%
    group_by(...) %>%
    summarize(
      total = sum({{ metric }}),
      average = mean({{ metric }}),
      .groups = 'drop'
    )
}

group_stats(df, region, metric = revenue)
group_stats(df, region, product, metric = units)

Tidy Selection Helpers

tidyselect helpers work inside across(), select(), and pick(): starts_with(), ends_with(), contains(), matches(), num_range(), where(predicate), and everything().

library(dplyr)

df <- data.frame(
  id = 1:3,
  score_2022 = c(80,85,90),
  score_2023 = c(82,88,91),
  score_2024 = c(85,90,93),
  category = c('A','B','A')
)

# Select all score columns and compute their means
df %>%
  summarize(
    across(starts_with('score'), mean),
    n = n()
  )

Combining Tidy Eval Patterns

Real-world dplyr functions often mix {{ }} for group variables, across() for multiple metrics, and .data[[]] for string column names. Understanding when to use each pattern makes your code flexible and correct.

library(dplyr)

df <- data.frame(
  dept = c('HR','HR','Eng','Eng'),
  salary = c(60000, 65000, 100000, 110000),
  bonus = c(5000, 6000, 15000, 18000)
)

# String-based column selection with .data
summarize_column <- function(data, group_col, value_col_name) {
  data %>%
    group_by(.data[[group_col]]) %>%
    summarize(
      mean = mean(.data[[value_col_name]]),
      total = sum(.data[[value_col_name]]),
      .groups = 'drop'
    )
}

summarize_column(df, 'dept', 'salary')
summarize_column(df, 'dept', 'bonus')

Quick Check

Inside a custom function, what is the correct way to pass a column name as an unquoted argument to dplyr?

Recap: Tidy Evaluation

Key takeaways for programming with dplyr:

  • {{ col }} — embrace unquoted column name arguments in functions
  • .data[['col_name']] — access columns by string variable name
  • := walrus operator — use with {{ }} or glue strings for dynamic output names
  • across(cols, fns) — apply function(s) to multiple columns at once
  • pick(cols) — select columns as a sub-data-frame for row-wise ops
  • tidyselect helpers: starts_with(), where(), contains(), everything()
library(dplyr)

df <- data.frame(
  group = c('A','A','B','B'),
  v1 = c(10, 20, 30, 40),
  v2 = c(5, 15, 25, 35)
)

# Reusable function using {{ }} + across
group_summary <- function(data, grp) {
  data %>%
    group_by({{ grp }}) %>%
    summarize(across(where(is.numeric), list(mean=mean, sum=sum)), .groups='drop')
}

group_summary(df, group)

Frequently asked questions

Is the “Tidy Evaluation: {{ }} and .data” lesson free?

Yes — the full text of “Tidy Evaluation: {{ }} and .data” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.

What will I learn in “Tidy Evaluation: {{ }} and .data”?

Write dplyr functions that accept column names as arguments safely. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start R Academy?

No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Tidy Evaluation: {{ }} and .data” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this R Academy lesson?

Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Grouped Summaries and group_by()
  2. Window Functions: lag, lead, cumsum
  3. Multi-table Joins in dplyr
  4. Tidy Evaluation: {{ }} and .data
← Back to R Academy