0Pricing
R Academy · 课时

整洁求值:{{ }} 与 .data

安全地编写接受列名作为参数的 dplyr 函数

整洁求值:{{ }} 与 .data 是 CoddyKit 上的免费 R Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。

问题:使用 dplyr 编程

dplyr 使用非标准求值(NSE)——传入列名时不加引号。这在交互式操作中很方便,但编写函数时会比较棘手。如何将列名作为变量传递给 dplyr 函数?

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!')

{{ }} 拥抱运算符

在函数内部,使用 {{ col_var }}(称为“双花括号”或“拥抱”)传递列名;该列名会在数据框上下文中求值。这是为大多数 dplyr 函数编程时推荐的方法。

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)

使用 {{ }} 生成列名

您还可以在 := 的左侧使用 {{ }},以动态命名输出列。:= 运算符(冒号等号运算符)允许在 dplyr 动词内部的赋值语句左侧使用变量名。

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)

.data 代词

.data[['col_name']]允许您使用字符串变量选择列。它明确告诉 dplyr 在当前数据框中查找该列。当列名以字符字符串形式保存时,这非常有用。

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() — 应用于多个列

在 mutate() 或 summarize() 内使用 across(cols, fns),可以一次将函数应用于多个列。使用 tidyselect 辅助函数选择列:starts_with()、where(is.numeric)、everything() 等。

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()

将命名函数列表传递给 across(),即可计算每列的多个统计量。输出列的命名形式为 col_fn。使用 .names 自定义命名模式。

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}'
  ))

在 mutate() 中使用 across()

在 mutate() 内使用 across() 可以同时转换多个列,避免为每一列重复编写相同的转换操作。

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() — 选择用于操作的列

pick()(dplyr 1.1+)将部分列选为一个小型数据框,适合传递给需要数据框的函数。它可以在 mutate() 和 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))
  )

编写可复用的 dplyr 函数

结合使用 {{ }}、.data[[]] 和 across(),可以编写功能强大且可复用的分析函数。关键模式是:将列名作为不加引号的参数(使用 {{ }})或字符串(使用 .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)

整洁选择辅助函数

tidyselect 辅助函数可以在 across()、select() 和 pick() 内使用:starts_with()、ends_with()、contains()、matches()、num_range()、where(predicate) 和 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()
  )

组合整洁求值模式

实际使用的 dplyr 函数通常会混合使用 {{ }} 处理分组变量、across() 处理多个指标,以及 .data[[]] 处理字符串列名。了解每种模式的适用时机,可以让您的代码灵活且正确。

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')

快速检查

在自定义函数内部,向 dplyr 传递不加引号的列名参数的正确方式是什么?

回顾:整洁求值

使用 dplyr 编程的要点:

  • {{ col }} — 在函数中使用拥抱方式接收不加引号的列名参数
  • .data[['col_name']] — 通过字符串变量名访问列
  • := 冒号等号运算符 — 与 {{ }} 或胶水字符串结合使用,以动态生成输出列名
  • across(cols, fns) — 一次将函数应用于多个列
  • pick(cols) — 将列选为子数据框,用于逐行操作
  • tidyselect 辅助函数: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)

常见问题解答

「整洁求值:{{ }} 与 .data」课时是免费的吗?

是的 — 「整洁求值:{{ }} 与 .data」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。

「整洁求值:{{ }} 与 .data」这节课中我会学到什么?

安全地编写接受列名作为参数的 dplyr 函数 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 R Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「整洁求值:{{ }} 与 .data」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 R Academy 课中编写并运行代码吗?

能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 分组汇总与 group_by()
  2. 窗口函数:lag、lead、cumsum
  3. dplyr 中的多表连接
  4. 整洁求值:{{ }} 与 .data
← 返回 R Academy