5  R 数据处理进阶

6 R 数据处理进阶

上一章我们学习了 R 的基础操作。本章将介绍 tidyverse——R 中最流行的数据处理生态系统,它能让你的数据处理代码更简洁、更易读。

6.1 tidyverse 简介

tidyverse 是一组协同工作的 R 包集合,由 Hadley Wickham 等人开发:

library(tidyverse)

加载 tidyverse 会同时加载以下核心包:

包名 功能
dplyr 数据操作(筛选、排序、汇总)
tidyr 数据重塑(长宽格式转换)
ggplot2 数据可视化
readr 数据读取
stringr 字符串处理
forcats 因子处理
tibble 增强版数据框
purrr 函数式编程

6.2 管道操作符

管道操作符 |> 是 tidyverse 编程风格的核心。它的作用很简单:把左边的结果,作为右边函数的第一个参数传入

# 传统写法(从内向外读,难以理解)
round(mean(c(1.5, 2.3, 3.7, 4.1)), 1)

# 管道写法(从左到右读,清晰直观)
c(1.5, 2.3, 3.7, 4.1) |>
  mean() |>
  round(1)

上面的管道写法等价于:

  1. c(1.5, 2.3, 3.7, 4.1) 的结果传给 mean() → 得到 2.9
  2. 2.9 传给 round(1) → 即 round(2.9, 1) → 得到 2.9

你可以把 |> 读作”然后”:取这些数字,然后求平均,然后四舍五入。

Tip管道的快捷键

在 RStudio 中,按 Ctrl + Shift + M 可以快速输入管道操作符。

Note|>%>% 的区别

你可能在网上看到另一种管道 %>%(来自 magrittr 包)。两者功能基本相同,区别在于:

  • |> 是 R 4.1+ 内置的原生管道,不需要加载任何包
  • %>% 是 magrittr 包提供的管道,需要加载 tidyverse 或 magrittr

本课程统一使用 |>。如果你在旧教程中看到 %>%,直接替换为 |> 即可。

6.2.1 示例数据

本章使用一个模拟的植物样方调查数据集:

# 创建示例数据
set.seed(2027)
survey <- tibble(
  plot_id = rep(paste0("P", 1:10), each = 5),
  species = sample(c("马尾松", "杉木", "桉树", "樟树", "楠木"), 50, replace = TRUE),
  height = round(rnorm(50, mean = 12, sd = 4), 1),
  dbh = round(rnorm(50, mean = 20, sd = 8), 1),
  habitat = rep(sample(c("山脊", "山坡", "山谷"), 10, replace = TRUE), each = 5),
  soil_ph = round(rnorm(50, mean = 5.5, sd = 0.8), 2)
)

# 人为加入一些缺失值
survey$height[c(3, 17, 28)] <- NA
survey$dbh[c(8, 42)] <- NA

head(survey, 10)

6.3 dplyr:数据操作五大动词

6.3.1 filter() — 筛选行

# 筛选马尾松
survey |>
  filter(species == "马尾松")

# 多条件筛选:株高 > 15 且 土壤 pH < 6
survey |>
  filter(height > 15, soil_ph < 6)

# 或条件:马尾松或杉木
survey |>
  filter(species %in% c("马尾松", "杉木"))

6.3.2 select() — 选择列

# 选择特定列
survey |>
  select(plot_id, species, height)

# 排除某列
survey |>
  select(-soil_ph)

# 选择数值列
survey |>
  select(where(is.numeric))

6.3.3 mutate() — 新增/修改列

# 新增列:计算树木断面积(basal area)
survey |>
  mutate(
    basal_area = pi * (dbh / 200)^2,    # 单位:m²
    height_class = case_when(
      height < 8  ~ "矮",
      height < 15 ~ "中",
      TRUE        ~ "高"
    )
  ) |>
  head()

6.3.4 arrange() — 排序

# 按株高降序排列
survey |>
  arrange(desc(height)) |>
  head()

# 先按样方排序,再按株高排序
survey |>
  arrange(plot_id, desc(height)) |>
  head()

6.3.5 summarise() + group_by() — 分组汇总

# 按物种分组统计
survey |>
  group_by(species) |>
  summarise(
    n = n(),
    mean_height = mean(height, na.rm = TRUE),
    sd_height = sd(height, na.rm = TRUE),
    mean_dbh = mean(dbh, na.rm = TRUE)
  ) |>
  arrange(desc(mean_height))
# 按样方和生境分组
survey |>
  group_by(habitat) |>
  summarise(
    n_trees = n(),
    n_species = n_distinct(species),
    mean_ph = mean(soil_ph, na.rm = TRUE)
  )

6.4 组合使用:数据处理流水线

dplyr 的强大之处在于可以用管道将多个操作串联起来:

# 完整的数据处理流水线
result <- survey |>
  filter(!is.na(height), !is.na(dbh)) |>       # 1. 去除缺失值
  mutate(basal_area = pi * (dbh / 200)^2) |>    # 2. 计算断面积
  group_by(habitat, species) |>                  # 3. 按生境和物种分组
  summarise(
    n = n(),
    mean_height = round(mean(height), 1),
    total_ba = round(sum(basal_area), 4),
    .groups = "drop"
  ) |>
  arrange(habitat, desc(total_ba))               # 4. 排序

result

6.5 tidyr:数据重塑

生态学数据经常需要在”长格式”和”宽格式”之间转换。

6.5.1 宽格式 → 长格式:pivot_longer()

# 宽格式数据:每个物种一列
wide_data <- tibble(
  plot = paste0("P", 1:5),
  马尾松 = c(12, 8, 0, 15, 6),
  杉木 = c(5, 0, 10, 3, 8),
  桉树 = c(0, 7, 4, 0, 2)
)
wide_data

# 转为长格式
long_data <- wide_data |>
  pivot_longer(
    cols = -plot,           # 除了 plot 列,其他都转
    names_to = "species",   # 列名变成 species 列
    values_to = "count"     # 值变成 count 列
  )
long_data

6.5.2 长格式 → 宽格式:pivot_wider()

# 转回宽格式
long_data |>
  pivot_wider(
    names_from = species,
    values_from = count
  )
Note什么时候用长格式?什么时候用宽格式?
  • 长格式:适合 ggplot2 绘图和大多数统计分析
  • 宽格式:适合展示和人工查看,以及某些多元统计分析(如 PCA)

一般原则:存储和分析用长格式,展示用宽格式

6.6 数据合并

研究中经常需要合并来自不同来源的数据:

# 样方环境数据
plot_env <- tibble(
  plot_id = paste0("P", 1:10),
  elevation = round(runif(10, 200, 800)),
  slope = round(runif(10, 5, 35)),
  aspect = sample(c("N", "S", "E", "W"), 10, replace = TRUE)
)

# 将环境数据合并到调查数据
survey_full <- survey |>
  left_join(plot_env, by = "plot_id")

head(survey_full)

常用的合并方式:

函数 说明
left_join(x, y) 保留 x 的所有行,匹配 y 的列
right_join(x, y) 保留 y 的所有行
inner_join(x, y) 只保留两边都有的行
full_join(x, y) 保留所有行

6.7 实践:物种调查数据整理

综合运用本章所学,完成以下数据处理任务:

# 任务:生成一份各样方的物种多样性报告

diversity_report <- survey |>
  filter(!is.na(height)) |>                    # 去除缺失值
  left_join(plot_env, by = "plot_id") |>       # 合并环境数据
  group_by(plot_id, habitat, elevation) |>     # 按样方分组
  summarise(
    n_individuals = n(),                        # 个体数
    n_species = n_distinct(species),            # 物种数
    mean_height = round(mean(height), 1),       # 平均株高
    max_height = max(height),                   # 最大株高
    .groups = "drop"
  ) |>
  arrange(desc(n_species))

diversity_report

6.8 课后练习

  1. 使用 iris 数据集,用 dplyr 计算每个物种的花瓣长度和宽度的平均值
  2. iris 数据集转换为长格式(4 个测量值变成一列)
  3. 筛选出花萼长度大于 6 的记录,按物种分组统计数量
  4. 创建一个数据处理流水线:筛选 → 新增列(花瓣面积 = 长 × 宽)→ 分组汇总 → 排序
  5. 将代码保存为 .qmd 文件,渲染为 HTML 后提交到 GitHub