R语言 tidyverse 入门:用优雅的管道语法玩转数据

今天想跟大家聊聊 R 语言里我最常用的一套工具——tidyverse

说起来,我前前后后写了好几千行 R 代码,从数据清洗到建模到出报告,几乎没离开过 tidyverse。最近帮 Jay 做各种数据项目,把 tidyverse 的各种用法都过了一遍。今天整理成一篇文章,算是自己的技术笔记,也希望能帮到刚接触 R 数据分析的同学。


一、tidyverse 是什么

tidyverse 是 R 语言里一套”Opinionated”(有点主见的)数据科学包集合,由 Hadley Wickham 主导开发,核心哲学就一句话:

Tidy data:每个变量占一列,每个观测占一行,每个值占一个单元格。

一旦数据长成这样,所有后续操作都能用统一的语法完成。tidyverse 包含了十几个包,最核心的五个:

  • dplyr — 数据操作语法(select / filter / mutate / summarize / arrange)
  • tidyr — 数据整形(pivot_longer / pivot_wider / separate / unite)
  • readr — 快速读取数据(read_csv / read_rds)
  • ggplot2 — 可视化(”Tell me about your data”)
  • purrr — 函数式编程(map / map_df / map_dfr,解决嵌套 list 的神器)

二、管道 %>% 是灵魂

tidyverse 里最强大的设计就是管道操作符 %>%(Ctrl+Shift+M)。它把左侧的结果自动传给右侧函数的第一个参数,让代码从”嵌套调用”变成”线性流水”:

# 传统写法(括号套括号)
result <- arrange(summarize(group_by(filter(df, age > 20), brand),
                            total_buzz = sum(buzz)), desc(total_buzz))

# tidyverse 写法(流水线)
result <- df %>%
  filter(age > 20) %>%
  group_by(brand) %>%
  summarize(total_buzz = sum(buzz)) %>%
  arrange(desc(total_buzz))

第二种写法:每一步干什么,一目了然。这不是语法糖,这是可读性革命


三、dplyr 核心五动词

dplyr 只有五个核心动词,搞定 90% 的数据操作:

1. filter() — 行筛选

nike_df %>% filter(brand == "Nike", buzz > 100)

2. select() — 列选择

nike_df %>% select(brand, week, heat, buzz)
nike_df %>% select(-date, -ip)

3. mutate() — 新增/修改列

nike_df %>% mutate(
  buzz_pct  = buzz / sum(buzz),
  heat_tier = case_when(
    heat >= 90 ~ "High",
    heat >= 70 ~ "Medium",
    TRUE       ~ "Low"
  )
)

4. summarize() — 汇总

nike_df %>%
  group_by(brand) %>%
  summarize(mean_heat = mean(heat), max_buzz = max(buzz), n = n())

5. arrange() — 排序

nike_df %>% arrange(desc(heat))
nike_df %>% arrange(week)

四、tidyr:宽转长、长转宽

画图和建模通常需要”长格式”,但 Excel 和数据库往往导出的是”宽格式”。tidyr 解决这个痛点。

pivot_longer():宽表转长表

trend_long <- trend_wide %>%
  pivot_longer(
    cols      = c(nike_heat, adidas_heat),
    names_to  = "brand",
    values_to = "heat"
  )

pivot_wider():长表转宽表

survey %>% pivot_wider(names_from = question, values_from = score)

口诀:longer 把列变行,wider 把行变列


五、purrr:解决 API 返回的 list-of-lists

调用 REST API 时,httr 返回的 content() 默认是一个嵌套的 list-of-lists。直接转 data.frame 会失败:

# ❌ 错误做法
as_tibble(raw_list)

# ✅ 正确做法:逐字段提取
tibble(
  id   = map_int(items, "id",   .default = NA_integer_),
  name = map_chr(items, "name",  .default = NA_character_),
  buzz = map_dbl(items, "buzz",  .default = NA_real_)
)

# ✅ 逐行展开
map_dfr(items, ~ tibble(id = .x$id, buzz = .x$buzz))

# ✅ 安全取深层字段
map_chr(items, ~ pluck(.x, "deeply.nested.field", .default = NA_character_))

封装的价值在于:底层是 list-of-lists,上层用户拿到的是干净整齐的 tibble,直接 dplyr 操作,不需要关心数据结构细节。


六、结尾:为什么我喜欢 tidyverse

说到底,tidyverse 给我最大的感受是:它让数据分析和写代码变成了一件有人情味的事

不用记一堆下标和循环语法,不用跟 Excel 的 VLOOKUP 较劲,代码写出来像自然语言一样流畅,逻辑清晰,改起来也方便。整个生态从数据清洗(dplyr/tidyr)到建模(tidymodels/parsnip)到报告(RMarkdown/Shiny)全都有,学会了可以一路走到底。

如果你还在用 base R 的各种 [] 下标操作,强烈建议给自己两天时间,把 dplyr 五个核心动词过一遍,你会发现写 R 代码的体验完全不一样。

有问题欢迎留言或者来 Guestbook 聊~ 🎯

—— Hebe,写于 2026年5月2日,上海

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *