R开发Skill r-development

现代R开发实践,强调tidyverse模式(dplyr 1.1及以后版本,原生管道,join_by,.by分组),rlang元编程,性能优化和包开发。

数据分析 0 次安装 0 次浏览 更新于 3/2/2026

R开发

这个技能提供了全面的现代R开发指导,强调当前最佳实践,包括tidyverse、性能优化和专业包开发。

核心原则

  1. 使用现代tidyverse模式 - 优先使用dplyr 1.1+特性、原生管道和当前API
  2. 优化前先分析 - 使用profvis和bench来识别真正的瓶颈
  3. 先写可读代码 - 仅在必要时优化,并且在分析后进行
  4. 遵循tidyverse风格指南 - 一致的命名、空格和结构

现代Tidyverse精华

原生管道(|>而不是%>%)

总是使用原生管道|>而不是magrittr%>%(R 4.1+):

# 现代
data |> 
  filter(year >= 2020) |>
  summarise(mean_value = mean(value))

# 避免遗留管道
data %>% filter(year >= 2020)

Join语法(dplyr 1.1+)

使用join_by()进行所有连接:

# 现代连接语法与等式
transactions |> 
  inner_join(companies, by = join_by(company == id))

# 不等连接
transactions |>
  inner_join(companies, join_by(company == id, year >= since))

# 滚动连接(最近匹配)
transactions |>
  inner_join(companies, join_by(company == id, closest(year >= since)))

控制匹配行为:

# 期望1:1匹配
inner_join(x, y, by = join_by(id), multiple = "error")

# 确保所有行匹配
inner_join(x, y, by = join_by(id), unmatched = "error")

每操作分组.by

使用.by而不是group_by() |> ... |> ungroup():

# 现代方法(总是返回未分组)
data |>
  summarise(mean_value = mean(value), .by = category)

# 多个分组变量
data |>
  summarise(total = sum(revenue), .by = c(company, year))

列操作

使用现代列选择和转换函数:

# pick()用于数据掩蔽上下文中的列选择
data |>
  summarise(
    n_x_cols = ncol(pick(starts_with("x"))),
    n_y_cols = ncol(pick(starts_with("y")))
  )

# across()用于将函数应用于多个列
data |>
  summarise(across(where(is.numeric), mean, .names = "mean_{.col}"), .by = group)

# reframe()用于每组多行结果
data |>
  reframe(quantiles = quantile(x, c(0.25, 0.5, 0.75)), .by = group)

rlang元编程

有关全面的rlang模式,请参阅references/rlang-patterns.md

快速参考

  • {{}} - 将函数参数转发给数据掩蔽函数
  • !! - 注入单个表达式或值
  • !!! - 从列表中注入多个参数
  • .data[[]] - 按名称(字符向量)访问列
  • pick() - 在数据掩蔽函数中选择列

带拥抱的示例函数:

my_summary <- function(data, group_var, summary_var) {
  data |>
    summarise(mean_val = mean({{ summary_var }}), .by = {{ group_var }})
}

性能优化

有关详细的性能指导,请参阅references/performance.md

关键策略

  1. 先分析: 使用profvis::profvis()bench::mark()
  2. 向量化操作: 当存在向量化替代方案时避免循环
  3. 使用dtplyr: 对于大型数据操作(lazy evaluation with data.table backend)
  4. 并行处理: 使用furrr::future_map()进行可并行化的工作
  5. 内存效率: 预分配,使用适当的数据类型

快速示例:

# 分析代码
profvis::profvis({
  result <- data |> 
    complex_operation() |>
    another_operation()
})

# 基准测试替代方案
bench::mark(
  approach_1 = method1(data),
  approach_2 = method2(data),
  check = FALSE
)

包开发

有关完整的包开发指导,请参阅references/package-development.md

快速指南

API设计:

  • 使用.by参数进行每操作分组
  • 使用{{}}用于列参数
  • 一致返回tibbles
  • 彻底验证用户面向函数的输入

依赖关系:

  • 为显著的功能增益添加依赖
  • 核心tidyverse包通常值得包括:dplyr, purrr, stringr, tidyr
  • 为广泛使用的包最小化依赖

测试:

  • 为单个函数编写单元测试
  • 为工作流程编写集成测试
  • 测试边缘情况和错误条件

文档:

  • 文档化所有导出的函数
  • 提供使用示例
  • 解释非明显的参数交互

常见迁移模式

基础R → Tidyverse

# 数据操作
subset(data, condition)         → filter(data, condition)
data[order(data$x), ]          → arrange(data, x)
aggregate(x ~ y, data, mean)   → summarise(data, mean(x), .by = y)

# 函数式编程
sapply(x, f)                   → map(x, f)  # 类型稳定
lapply(x, f)                   → map(x, f)

# 字符串
grepl("pattern", text)         → str_detect(text, "pattern")
gsub("old", "new", text)       → str_replace_all(text, "old", "new")

旧 → 新Tidyverse

# 管道
%>%                            → |>

# 分组
group_by() |> ... |> ungroup() → summarise(..., .by = x)

# 连接
by = c("a" = "b")             → by = join_by(a == b)

# 重塑
gather()/spread()              → pivot_longer()/pivot_wider()

额外资源