整洁求值:{{ }} 与 .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 反馈 — 无需本地设置。
此课程中的所有课时
- 分组汇总与 group_by()
- 窗口函数:lag、lead、cumsum
- dplyr 中的多表连接
- 整洁求值:{{ }} 与 .data